Merge lp:~phill-ridout/openlp/pathlib4 into lp:openlp

Proposed by Phill
Status: Merged
Approved by: Raoul Snyman
Approved revision: 2769
Merged at revision: 2764
Proposed branch: lp:~phill-ridout/openlp/pathlib4
Merge into: lp:openlp
Diff against target: 2908 lines (+723/-510)
66 files modified
openlp/core/__init__.py (+19/-6)
openlp/core/api/endpoint/controller.py (+1/-2)
openlp/core/common/__init__.py (+6/-8)
openlp/core/common/applocation.py (+8/-11)
openlp/core/common/json.py (+86/-0)
openlp/core/common/path.py (+51/-12)
openlp/core/common/settings.py (+160/-50)
openlp/core/lib/__init__.py (+4/-10)
openlp/core/lib/mediamanageritem.py (+9/-8)
openlp/core/lib/path.py (+0/-61)
openlp/core/lib/plugin.py (+1/-1)
openlp/core/ui/exceptionform.py (+16/-17)
openlp/core/ui/firsttimeform.py (+1/-1)
openlp/core/ui/generaltab.py (+3/-4)
openlp/core/ui/lib/filedialog.py (+5/-7)
openlp/core/ui/lib/pathedit.py (+11/-36)
openlp/core/ui/lib/wizard.py (+21/-19)
openlp/core/ui/maindisplay.py (+2/-1)
openlp/core/ui/mainwindow.py (+15/-85)
openlp/core/ui/servicemanager.py (+15/-15)
openlp/core/ui/themeform.py (+1/-2)
openlp/core/ui/thememanager.py (+13/-15)
openlp/core/ui/themewizard.py (+1/-2)
openlp/plugins/bibles/bibleplugin.py (+1/-1)
openlp/plugins/bibles/lib/importers/csvbible.py (+1/-1)
openlp/plugins/bibles/lib/manager.py (+4/-8)
openlp/plugins/bibles/lib/mediaitem.py (+2/-3)
openlp/plugins/images/imageplugin.py (+0/-8)
openlp/plugins/images/lib/mediaitem.py (+2/-2)
openlp/plugins/media/lib/mediaitem.py (+13/-14)
openlp/plugins/media/mediaplugin.py (+1/-2)
openlp/plugins/presentations/lib/impresscontroller.py (+2/-2)
openlp/plugins/presentations/lib/mediaitem.py (+10/-9)
openlp/plugins/presentations/lib/pdfcontroller.py (+2/-2)
openlp/plugins/presentations/lib/presentationcontroller.py (+1/-1)
openlp/plugins/presentations/lib/presentationtab.py (+5/-7)
openlp/plugins/presentations/presentationplugin.py (+1/-1)
openlp/plugins/songs/forms/editsongform.py (+1/-2)
openlp/plugins/songs/forms/songimportform.py (+8/-9)
openlp/plugins/songs/lib/importers/songbeamer.py (+2/-2)
openlp/plugins/songs/lib/importers/songimport.py (+1/-1)
openlp/plugins/songs/lib/mediaitem.py (+1/-1)
openlp/plugins/songs/lib/openlyricsexport.py (+1/-1)
openlp/plugins/songs/songsplugin.py (+2/-2)
openlp/plugins/songusage/forms/songusagedetailform.py (+7/-6)
openlp/plugins/songusage/songusageplugin.py (+1/-1)
tests/functional/openlp_core_common/test_applocation.py (+7/-6)
tests/functional/openlp_core_common/test_common.py (+1/-2)
tests/functional/openlp_core_common/test_init.py (+1/-1)
tests/functional/openlp_core_common/test_json.py (+122/-0)
tests/functional/openlp_core_common/test_path.py (+52/-2)
tests/functional/openlp_core_common/test_settings.py (+0/-20)
tests/functional/openlp_core_lib/test_db.py (+1/-2)
tests/functional/openlp_core_lib/test_lib.py (+1/-1)
tests/functional/openlp_core_lib/test_path.py (+1/-2)
tests/functional/openlp_core_ui/test_exceptionform.py (+3/-2)
tests/functional/openlp_core_ui/test_firsttimeform.py (+1/-1)
tests/functional/openlp_core_ui/test_themeform.py (+1/-1)
tests/functional/openlp_core_ui_lib/test_filedialog.py (+1/-1)
tests/functional/openlp_core_ui_lib/test_pathedit.py (+1/-1)
tests/functional/openlp_plugins/bibles/test_manager.py (+1/-1)
tests/functional/openlp_plugins/images/test_lib.py (+3/-2)
tests/functional/openlp_plugins/media/test_mediaitem.py (+3/-2)
tests/functional/openlp_plugins/presentations/test_presentationcontroller.py (+1/-1)
tests/interfaces/openlp_core_common/test_utils.py (+1/-1)
tests/interfaces/openlp_core_lib/test_pluginmanager.py (+3/-2)
To merge this branch: bzr merge lp:~phill-ridout/openlp/pathlib4
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Tomas Groth Approve
Raoul Snyman Pending
Phill Pending
OpenLP Core Pending
Review via email: mp+330124@code.launchpad.net

This proposal supersedes a proposal from 2017-08-27.

Description of the change

Change the settings upgrade code to handle versioned upgrades
Upgrade settings to store file paths and json encoded Path objects
Enable the json encoders/decoders to work with custom objects with defined json methods
Added in automatic backup before settings upgrade on superflys request

lp:~phill-ridout/openlp/pathlib4 (revision 2769)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/2184/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/2087/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1974/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1344/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1180/
[SUCCESS] https://ci.openlp.io/job/Branch-04c-Code_Analysis2/310/
[FAILURE] https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/152/
Stopping after failure

To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Looks good.

review: Approve
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Minor tweak

review: Needs Fixing
Revision history for this message
Tim Bentley (trb143) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Phill (phill-ridout) : Posted in a previous version of this proposal
review: Needs Fixing
Revision history for this message
Phill (phill-ridout) wrote : Posted in a previous version of this proposal

Sorry this is ready to go

review: Approve
Revision history for this message
Tim Bentley (trb143) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Tomas Groth (tomasgroth) wrote :

Looks ok.

review: Approve
Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/__init__.py'
2--- openlp/core/__init__.py 2017-08-23 20:13:58 +0000
3+++ openlp/core/__init__.py 2017-09-03 10:39:42 +0000
4@@ -33,13 +33,14 @@
5 import shutil
6 import sys
7 import time
8-from pathlib import Path
9+from datetime import datetime
10 from traceback import format_exception
11
12 from PyQt5 import QtCore, QtGui, QtWidgets
13
14 from openlp.core.common import Registry, OpenLPMixin, AppLocation, LanguageManager, Settings, UiStrings, \
15 check_directory_exists, is_macosx, is_win, translate
16+from openlp.core.common.path import Path
17 from openlp.core.common.versionchecker import VersionThread, get_application_version
18 from openlp.core.lib import ScreenList
19 from openlp.core.resources import qInitResources
20@@ -347,8 +348,7 @@
21 """
22 Setup our logging using log_path
23
24- :param pathlib.Path log_path: The file to save the log to
25- :return: None
26+ :param openlp.core.common.path.Path log_path: The file to save the log to.
27 :rtype: None
28 """
29 check_directory_exists(log_path, True)
30@@ -406,7 +406,7 @@
31 # Set our data path
32 log.info('Data path: {name}'.format(name=data_path))
33 # Point to our data path
34- portable_settings.setValue('advanced/data path', str(data_path))
35+ portable_settings.setValue('advanced/data path', data_path)
36 portable_settings.setValue('advanced/is portable', True)
37 portable_settings.sync()
38 else:
39@@ -423,8 +423,21 @@
40 if application.is_data_path_missing():
41 application.shared_memory.detach()
42 sys.exit()
43- # Remove/convert obsolete settings.
44- Settings().remove_obsolete_settings()
45+ # Upgrade settings.
46+ settings = Settings()
47+ if settings.can_upgrade():
48+ now = datetime.now()
49+ # Only back up if OpenLP has previously run.
50+ if settings.value('core/has run wizard'):
51+ back_up_path = AppLocation.get_data_path() / (now.strftime('%Y-%m-%d %H-%M') + '.conf')
52+ log.info('Settings about to be upgraded. Existing settings are being backed up to {back_up_path}'
53+ .format(back_up_path=back_up_path))
54+ QtWidgets.QMessageBox.information(
55+ None, translate('OpenLP', 'Settings Upgrade'),
56+ translate('OpenLP', 'Your settings are about to upgraded. A backup will be created at {back_up_path}')
57+ .format(back_up_path=back_up_path))
58+ settings.export(back_up_path)
59+ settings.upgrade_settings()
60 # First time checks in settings
61 if not Settings().value('core/has run wizard'):
62 if not FirstTimeLanguageForm().exec():
63
64=== modified file 'openlp/core/api/endpoint/controller.py'
65--- openlp/core/api/endpoint/controller.py 2017-08-24 19:53:55 +0000
66+++ openlp/core/api/endpoint/controller.py 2017-09-03 10:39:42 +0000
67@@ -79,8 +79,7 @@
68 item['title'] = str(frame['display_title'])
69 if current_item.is_capable(ItemCapabilities.HasNotes):
70 item['slide_notes'] = str(frame['notes'])
71- if current_item.is_capable(ItemCapabilities.HasThumbnails) and \
72- Settings().value('api/thumbnails'):
73+ if current_item.is_capable(ItemCapabilities.HasThumbnails) and Settings().value('api/thumbnails'):
74 # If the file is under our app directory tree send the portion after the match
75 data_path = str(AppLocation.get_data_path())
76 if frame['image'][0:len(data_path)] == data_path:
77
78=== modified file 'openlp/core/common/__init__.py'
79--- openlp/core/common/__init__.py 2017-08-12 17:45:56 +0000
80+++ openlp/core/common/__init__.py 2017-09-03 10:39:42 +0000
81@@ -66,9 +66,8 @@
82 """
83 Check a directory exists and if not create it
84
85- :param pathlib.Path directory: The directory to make sure exists
86+ :param openlp.core.common.path.Path directory: The directory to make sure exists
87 :param bool do_not_log: To not log anything. This is need for the start up, when the log isn't ready.
88- :return: None
89 :rtype: None
90 """
91 if not do_not_log:
92@@ -89,7 +88,6 @@
93 :param str glob_pattern: A glob pattern used to find the extension(s) to be imported. Should be relative to the
94 application directory. i.e. plugins/*/*plugin.py
95 :param list[str] excluded_files: A list of file names to exclude that the glob pattern may find.
96- :return: None
97 :rtype: None
98 """
99 app_dir = AppLocation.get_directory(AppLocation.AppDir)
100@@ -110,7 +108,7 @@
101 """
102 Convert a path to a module name (i.e openlp.core.common)
103
104- :param pathlib.Path path: The path to convert to a module name.
105+ :param openlp.core.common.path.Path path: The path to convert to a module name.
106 :return: The module name.
107 :rtype: str
108 """
109@@ -377,7 +375,7 @@
110 """
111 Deletes a file from the system.
112
113- :param pathlib.Path file_path: The file, including path, to delete.
114+ :param openlp.core.common.path.Path file_path: The file, including path, to delete.
115 :return: True if the deletion was successful, or the file never existed. False otherwise.
116 :rtype: bool
117 """
118@@ -412,7 +410,7 @@
119 """
120 Validate that the file is not an image file.
121
122- :param pathlib.Path file_path: The file to be checked.
123+ :param openlp.core.common.path.Path file_path: The file to be checked.
124 :return: If the file is not an image
125 :rtype: bool
126 """
127@@ -440,7 +438,7 @@
128 """
129 Function that checks whether a binary exists.
130
131- :param pathlib.Path program_path: The full path to the binary to check.
132+ :param openlp.core.common.path.Path program_path: The full path to the binary to check.
133 :return: program output to be parsed
134 :rtype: bytes
135 """
136@@ -466,7 +464,7 @@
137 """
138 Utility function to incrementally detect the file encoding.
139
140- :param pathlib.Path file_path: Filename for the file to determine the encoding for.
141+ :param openlp.core.common.path.Path file_path: Filename for the file to determine the encoding for.
142 :return: A dict with the keys 'encoding' and 'confidence'
143 :rtype: dict[str, float]
144 """
145
146=== modified file 'openlp/core/common/applocation.py'
147--- openlp/core/common/applocation.py 2017-08-18 19:34:20 +0000
148+++ openlp/core/common/applocation.py 2017-09-03 10:39:42 +0000
149@@ -25,9 +25,9 @@
150 import logging
151 import os
152 import sys
153-from pathlib import Path
154
155 from openlp.core.common import Settings, is_win, is_macosx
156+from openlp.core.common.path import Path
157
158
159 if not is_win() and not is_macosx():
160@@ -64,10 +64,8 @@
161 Return the appropriate directory according to the directory type.
162
163 :param dir_type: The directory type you want, for instance the data directory. Default *AppLocation.AppDir*
164- :type dir_type: AppLocation Enum
165-
166 :return: The requested path
167- :rtype: pathlib.Path
168+ :rtype: openlp.core.common.path.Path
169 """
170 if dir_type == AppLocation.AppDir or dir_type == AppLocation.VersionDir:
171 return get_frozen_path(FROZEN_APP_PATH, APP_PATH)
172@@ -84,11 +82,11 @@
173 Return the path OpenLP stores all its data under.
174
175 :return: The data path to use.
176- :rtype: pathlib.Path
177+ :rtype: openlp.core.common.path.Path
178 """
179 # Check if we have a different data location.
180 if Settings().contains('advanced/data path'):
181- path = Path(Settings().value('advanced/data path'))
182+ path = Settings().value('advanced/data path')
183 else:
184 path = AppLocation.get_directory(AppLocation.DataDir)
185 check_directory_exists(path)
186@@ -104,7 +102,7 @@
187 :param str extension: Defaults to ''. The extension to search for. For example::
188 '.png'
189 :return: List of files found.
190- :rtype: list[pathlib.Path]
191+ :rtype: list[openlp.core.common.path.Path]
192 """
193 path = AppLocation.get_data_path()
194 if section:
195@@ -120,9 +118,8 @@
196 """
197 Return the path a particular module stores its data under.
198
199- :type section: str
200-
201- :rtype: pathlib.Path
202+ :param str section:
203+ :rtype: openlp.core.common.path.Path
204 """
205 path = AppLocation.get_data_path() / section
206 check_directory_exists(path)
207@@ -135,7 +132,7 @@
208
209 :param dir_type: AppLocation Enum of the requested path type
210 :return: The requested path
211- :rtype: pathlib.Path
212+ :rtype: openlp.core.common.path.Path
213 """
214 # If running from source, return the language directory from the source directory
215 if dir_type == AppLocation.LanguageDir:
216
217=== added file 'openlp/core/common/json.py'
218--- openlp/core/common/json.py 1970-01-01 00:00:00 +0000
219+++ openlp/core/common/json.py 2017-09-03 10:39:42 +0000
220@@ -0,0 +1,86 @@
221+# -*- coding: utf-8 -*-
222+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
223+
224+###############################################################################
225+# OpenLP - Open Source Lyrics Projection #
226+# --------------------------------------------------------------------------- #
227+# Copyright (c) 2008-2017 OpenLP Developers #
228+# --------------------------------------------------------------------------- #
229+# This program is free software; you can redistribute it and/or modify it #
230+# under the terms of the GNU General Public License as published by the Free #
231+# Software Foundation; version 2 of the License. #
232+# #
233+# This program is distributed in the hope that it will be useful, but WITHOUT #
234+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
235+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
236+# more details. #
237+# #
238+# You should have received a copy of the GNU General Public License along #
239+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
240+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
241+###############################################################################
242+from json import JSONDecoder, JSONEncoder
243+
244+from openlp.core.common.path import Path
245+
246+
247+class OpenLPJsonDecoder(JSONDecoder):
248+ """
249+ Implement a custom JSONDecoder to handle Path objects
250+
251+ Example Usage:
252+ object = json.loads(json_string, cls=OpenLPJsonDecoder)
253+ """
254+ def __init__(self, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True,
255+ object_pairs_hook=None, **kwargs):
256+ """
257+ Re-implement __init__ so that we can pass in our object_hook method. Any additional kwargs, are stored in the
258+ instance and are passed to custom objects upon encoding or decoding.
259+ """
260+ self.kwargs = kwargs
261+ if object_hook is None:
262+ object_hook = self.custom_object_hook
263+ super().__init__(object_hook, parse_float, parse_int, parse_constant, strict, object_pairs_hook)
264+
265+ def custom_object_hook(self, obj):
266+ """
267+ Implement a custom Path object decoder.
268+
269+ :param dict obj: A decoded JSON object
270+ :return: The original object literal, or a Path object if the object literal contains a key '__Path__'
271+ :rtype: dict | openlp.core.common.path.Path
272+ """
273+ if '__Path__' in obj:
274+ obj = Path.encode_json(obj, **self.kwargs)
275+ return obj
276+
277+
278+class OpenLPJsonEncoder(JSONEncoder):
279+ """
280+ Implement a custom JSONEncoder to handle Path objects
281+
282+ Example Usage:
283+ json_string = json.dumps(object, cls=OpenLPJsonEncoder)
284+ """
285+ def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False,
286+ indent=None, separators=None, default=None, **kwargs):
287+ """
288+ Re-implement __init__ so that we can pass in additional kwargs, which are stored in the instance and are passed
289+ to custom objects upon encoding or decoding.
290+ """
291+ self.kwargs = kwargs
292+ if default is None:
293+ default = self.custom_default
294+ super().__init__(skipkeys, ensure_ascii, check_circular, allow_nan, sort_keys, indent, separators, default)
295+
296+ def custom_default(self, obj):
297+ """
298+ Convert any Path objects into a dictionary object which can be serialized.
299+
300+ :param object obj: The object to convert
301+ :return: The serializable object
302+ :rtype: dict
303+ """
304+ if isinstance(obj, Path):
305+ return obj.json_object(**self.kwargs)
306+ return super().default(obj)
307
308=== modified file 'openlp/core/common/path.py'
309--- openlp/core/common/path.py 2017-08-04 17:40:57 +0000
310+++ openlp/core/common/path.py 2017-09-03 10:39:42 +0000
311@@ -19,17 +19,21 @@
312 # with this program; if not, write to the Free Software Foundation, Inc., 59 #
313 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
314 ###############################################################################
315-
316-from pathlib import Path
317-
318-
319-def path_to_str(path):
320+from contextlib import suppress
321+
322+from openlp.core.common import is_win
323+
324+if is_win():
325+ from pathlib import WindowsPath as PathVariant
326+else:
327+ from pathlib import PosixPath as PathVariant
328+
329+
330+def path_to_str(path=None):
331 """
332 A utility function to convert a Path object or NoneType to a string equivalent.
333
334- :param path: The value to convert to a string
335- :type: pathlib.Path or None
336-
337+ :param openlp.core.common.path.Path | None path: The value to convert to a string
338 :return: An empty string if :param:`path` is None, else a string representation of the :param:`path`
339 :rtype: str
340 """
341@@ -48,14 +52,49 @@
342 This function is of particular use because initating a Path object with an empty string causes the Path object to
343 point to the current working directory.
344
345- :param string: The string to convert
346- :type string: str
347-
348+ :param str string: The string to convert
349 :return: None if :param:`string` is empty, or a Path object representation of :param:`string`
350- :rtype: pathlib.Path or None
351+ :rtype: openlp.core.common.path.Path | None
352 """
353 if not isinstance(string, str):
354 raise TypeError('parameter \'string\' must be of type str')
355 if string == '':
356 return None
357 return Path(string)
358+
359+
360+class Path(PathVariant):
361+ """
362+ Subclass pathlib.Path, so we can add json conversion methods
363+ """
364+ @staticmethod
365+ def encode_json(obj, base_path=None, **kwargs):
366+ """
367+ Create a Path object from a dictionary representation. The dictionary has been constructed by JSON encoding of
368+ a JSON reprensation of a Path object.
369+
370+ :param dict[str] obj: The dictionary representation
371+ :param openlp.core.common.path.Path base_path: If specified, an absolute path to base the relative path off of.
372+ :param kwargs: Contains any extra parameters. Not used!
373+ :return: The reconstructed Path object
374+ :rtype: openlp.core.common.path.Path
375+ """
376+ path = Path(*obj['__Path__'])
377+ if base_path and not path.is_absolute():
378+ return base_path / path
379+ return path
380+
381+ def json_object(self, base_path=None, **kwargs):
382+ """
383+ Create a dictionary that can be JSON decoded.
384+
385+ :param openlp.core.common.path.Path base_path: If specified, an absolute path to make a relative path from.
386+ :param kwargs: Contains any extra parameters. Not used!
387+ :return: The dictionary representation of this Path object.
388+ :rtype: dict[tuple]
389+ """
390+ path = self
391+ if base_path:
392+ with suppress(ValueError):
393+ path = path.relative_to(base_path)
394+ return {'__Path__': path.parts}
395
396=== modified file 'openlp/core/common/settings.py'
397--- openlp/core/common/settings.py 2017-08-25 04:26:25 +0000
398+++ openlp/core/common/settings.py 2017-09-03 10:39:42 +0000
399@@ -24,15 +24,19 @@
400 """
401 import datetime
402 import logging
403+import json
404 import os
405-
406-from PyQt5 import QtCore, QtGui
407-
408-from openlp.core.common import ThemeLevel, SlideLimits, UiStrings, is_win, is_linux
409-
410+from tempfile import gettempdir
411+
412+from PyQt5 import QtCore, QtGui, QtWidgets
413+
414+from openlp.core.common import SlideLimits, ThemeLevel, UiStrings, is_linux, is_win, translate
415+from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
416+from openlp.core.common.path import Path, str_to_path
417
418 log = logging.getLogger(__name__)
419
420+__version__ = 2
421
422 # Fix for bug #1014422.
423 X11_BYPASS_DEFAULT = True
424@@ -44,21 +48,6 @@
425 X11_BYPASS_DEFAULT = False
426
427
428-def recent_files_conv(value):
429- """
430- If the value is not a list convert it to a list
431- :param value: Value to convert
432- :return: value as a List
433- """
434- if isinstance(value, list):
435- return value
436- elif isinstance(value, str):
437- return [value]
438- elif isinstance(value, bytes):
439- return [value.decode()]
440- return []
441-
442-
443 def media_players_conv(string):
444 """
445 If phonon is in the setting string replace it with system
446@@ -73,14 +62,25 @@
447 return string
448
449
450+def file_names_conv(file_names):
451+ """
452+ Convert a list of file names in to a list of file paths.
453+
454+ :param list[str] file_names: The list of file names to convert.
455+ :return: The list converted to file paths
456+ :rtype: openlp.core.common.path.Path
457+ """
458+ if file_names:
459+ return [str_to_path(file_name) for file_name in file_names]
460+
461+
462 class Settings(QtCore.QSettings):
463 """
464 Class to wrap QSettings.
465
466 * Exposes all the methods of QSettings.
467- * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to
468- ``IniFormat``, and the path to the Ini file is set using ``set_filename``,
469- then the Settings constructor (without any arguments) will create a Settings
470+ * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to ``IniFormat``, and the path to the Ini
471+ file is set using ``set_filename``, then the Settings constructor (without any arguments) will create a Settings
472 object for accessing settings stored in that Ini file.
473
474 ``__default_settings__``
475@@ -91,7 +91,7 @@
476
477 ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)])
478
479- The first entry is the *old key*; it will be removed.
480+ The first entry is the *old key*; if it is different from the *new key* it will be removed.
481
482 The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove
483 the old key. The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made.
484@@ -105,11 +105,12 @@
485 So, if the type of the old value is bool, then there must be two rules.
486 """
487 __default_settings__ = {
488+ 'settings/version': 0,
489 'advanced/add page break': False,
490 'advanced/alternate rows': not is_win(),
491 'advanced/autoscrolling': {'dist': 1, 'pos': 0},
492 'advanced/current media plugin': -1,
493- 'advanced/data path': '',
494+ 'advanced/data path': None,
495 # 7 stands for now, 0 to 6 is Monday to Sunday.
496 'advanced/default service day': 7,
497 'advanced/default service enabled': True,
498@@ -143,7 +144,7 @@
499 'api/authentication enabled': False,
500 'api/ip address': '0.0.0.0',
501 'api/thumbnails': True,
502- 'crashreport/last directory': '',
503+ 'crashreport/last directory': None,
504 'formattingTags/html_tags': '',
505 'core/audio repeat list': False,
506 'core/auto open': False,
507@@ -162,7 +163,7 @@
508 'core/screen blank': False,
509 'core/show splash': True,
510 'core/logo background color': '#ffffff',
511- 'core/logo file': ':/graphics/openlp-splash-screen.png',
512+ 'core/logo file': Path(':/graphics/openlp-splash-screen.png'),
513 'core/logo hide on startup': False,
514 'core/songselect password': '',
515 'core/songselect username': '',
516@@ -177,17 +178,17 @@
517 'media/players': 'system,webkit',
518 'media/override player': QtCore.Qt.Unchecked,
519 'players/background color': '#000000',
520- 'servicemanager/last directory': '',
521- 'servicemanager/last file': '',
522- 'servicemanager/service theme': '',
523+ 'servicemanager/last directory': None,
524+ 'servicemanager/last file': None,
525+ 'servicemanager/service theme': None,
526 'SettingsImport/file_date_created': datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
527 'SettingsImport/Make_Changes': 'At_Own_RISK',
528 'SettingsImport/type': 'OpenLP_settings_export',
529 'SettingsImport/version': '',
530 'themes/global theme': '',
531- 'themes/last directory': '',
532- 'themes/last directory export': '',
533- 'themes/last directory import': '',
534+ 'themes/last directory': None,
535+ 'themes/last directory export': None,
536+ 'themes/last directory import': None,
537 'themes/theme level': ThemeLevel.Song,
538 'themes/wrap footer': False,
539 'user interface/live panel': True,
540@@ -208,22 +209,20 @@
541 'projector/db database': '',
542 'projector/enable': True,
543 'projector/connect on start': False,
544- 'projector/last directory import': '',
545- 'projector/last directory export': '',
546+ 'projector/last directory import': None,
547+ 'projector/last directory export': None,
548 'projector/poll time': 20, # PJLink timeout is 30 seconds
549 'projector/socket timeout': 5, # 5 second socket timeout
550 'projector/source dialog type': 0 # Source select dialog box type
551 }
552 __file_path__ = ''
553- __obsolete_settings__ = [
554+ __setting_upgrade_1__ = [
555 # Changed during 2.2.x development.
556- # ('advanced/stylesheet fix', '', []),
557- # ('general/recent files', 'core/recent files', [(recent_files_conv, None)]),
558 ('songs/search as type', 'advanced/search as type', []),
559 ('media/players', 'media/players_temp', [(media_players_conv, None)]), # Convert phonon to system
560 ('media/players_temp', 'media/players', []), # Move temp setting from above to correct setting
561 ('advanced/default color', 'core/logo background color', []), # Default image renamed + moved to general > 2.4.
562- ('advanced/default image', '/core/logo file', []), # Default image renamed + moved to general after 2.4.
563+ ('advanced/default image', 'core/logo file', []), # Default image renamed + moved to general after 2.4.
564 ('remotes/https enabled', '', []),
565 ('remotes/https port', '', []),
566 ('remotes/twelve hour', 'api/twelve hour', []),
567@@ -234,7 +233,6 @@
568 ('remotes/authentication enabled', 'api/authentication enabled', []),
569 ('remotes/ip address', 'api/ip address', []),
570 ('remotes/thumbnails', 'api/thumbnails', []),
571- ('advanced/default image', 'core/logo file', []), # Default image renamed + moved to general after 2.4.
572 ('shortcuts/escapeItem', 'shortcuts/desktopScreenEnable', []), # Escape item was removed in 2.6.
573 ('shortcuts/offlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6.
574 ('shortcuts/onlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6.
575@@ -243,7 +241,28 @@
576 # Last search type was renamed to last used search type in 2.6 since Bible search value type changed in 2.6.
577 ('songs/last search type', 'songs/last used search type', []),
578 ('bibles/last search type', '', []),
579- ('custom/last search type', 'custom/last used search type', [])
580+ ('custom/last search type', 'custom/last used search type', [])]
581+
582+ __setting_upgrade_2__ = [
583+ # The following changes are being made for the conversion to using Path objects made in 2.6 development
584+ ('advanced/data path', 'advanced/data path', [(str_to_path, None)]),
585+ ('crashreport/last directory', 'crashreport/last directory', [(str_to_path, None)]),
586+ ('servicemanager/last directory', 'servicemanager/last directory', [(str_to_path, None)]),
587+ ('servicemanager/last file', 'servicemanager/last file', [(str_to_path, None)]),
588+ ('themes/last directory', 'themes/last directory', [(str_to_path, None)]),
589+ ('themes/last directory export', 'themes/last directory export', [(str_to_path, None)]),
590+ ('themes/last directory import', 'themes/last directory import', [(str_to_path, None)]),
591+ ('projector/last directory import', 'projector/last directory import', [(str_to_path, None)]),
592+ ('projector/last directory export', 'projector/last directory export', [(str_to_path, None)]),
593+ ('bibles/last directory import', 'bibles/last directory import', [(str_to_path, None)]),
594+ ('presentations/pdf_program', 'presentations/pdf_program', [(str_to_path, None)]),
595+ ('songs/last directory import', 'songs/last directory import', [(str_to_path, None)]),
596+ ('songs/last directory export', 'songs/last directory export', [(str_to_path, None)]),
597+ ('songusage/last directory export', 'songusage/last directory export', [(str_to_path, None)]),
598+ ('core/recent files', 'core/recent files', [(file_names_conv, None)]),
599+ ('media/media files', 'media/media files', [(file_names_conv, None)]),
600+ ('presentations/presentations files', 'presentations/presentations files', [(file_names_conv, None)]),
601+ ('core/logo file', 'core/logo file', [(str_to_path, None)])
602 ]
603
604 @staticmethod
605@@ -256,13 +275,16 @@
606 Settings.__default_settings__.update(default_values)
607
608 @staticmethod
609- def set_filename(ini_file):
610+ def set_filename(ini_path):
611 """
612 Sets the complete path to an Ini file to be used by Settings objects.
613
614 Does not affect existing Settings objects.
615+
616+ :param openlp.core.common.path.Path ini_path: ini file path
617+ :rtype: None
618 """
619- Settings.__file_path__ = ini_file
620+ Settings.__file_path__ = str(ini_path)
621
622 @staticmethod
623 def set_up_default_values():
624@@ -431,14 +453,28 @@
625 key = self.group() + '/' + key
626 return Settings.__default_settings__[key]
627
628- def remove_obsolete_settings(self):
629+ def can_upgrade(self):
630+ """
631+ Can / should the settings be upgraded
632+
633+ :rtype: bool
634+ """
635+ return __version__ != self.value('settings/version')
636+
637+ def upgrade_settings(self):
638 """
639 This method is only called to clean up the config. It removes old settings and it renames settings. See
640 ``__obsolete_settings__`` for more details.
641 """
642- for old_key, new_key, rules in Settings.__obsolete_settings__:
643- # Once removed we don't have to do this again.
644- if self.contains(old_key):
645+ current_version = self.value('settings/version')
646+ for version in range(current_version, __version__):
647+ version += 1
648+ upgrade_list = getattr(self, '__setting_upgrade_{version}__'.format(version=version))
649+ for old_key, new_key, rules in upgrade_list:
650+ # Once removed we don't have to do this again. - Can be removed once fully switched to the versioning
651+ # system.
652+ if not self.contains(old_key):
653+ continue
654 if new_key:
655 # Get the value of the old_key.
656 old_value = super(Settings, self).value(old_key)
657@@ -457,14 +493,17 @@
658 old_value = new
659 break
660 self.setValue(new_key, old_value)
661- self.remove(old_key)
662+ if new_key != old_key:
663+ self.remove(old_key)
664+ self.setValue('settings/version', version)
665
666 def value(self, key):
667 """
668 Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the
669 *Settings.__default_settings__* dict.
670
671- :param key: The key to return the value from.
672+ :param str key: The key to return the value from.
673+ :return: The value stored by the setting.
674 """
675 # if group() is not empty the group has not been specified together with the key.
676 if self.group():
677@@ -474,6 +513,18 @@
678 setting = super(Settings, self).value(key, default_value)
679 return self._convert_value(setting, default_value)
680
681+ def setValue(self, key, value):
682+ """
683+ Reimplement the setValue method to handle Path objects.
684+
685+ :param str key: The key of the setting to save
686+ :param value: The value to save
687+ :rtype: None
688+ """
689+ if isinstance(value, Path) or (isinstance(value, list) and value and isinstance(value[0], Path)):
690+ value = json.dumps(value, cls=OpenLPJsonEncoder)
691+ super().setValue(key, value)
692+
693 def _convert_value(self, setting, default_value):
694 """
695 This converts the given ``setting`` to the type of the given ``default_value``.
696@@ -491,8 +542,11 @@
697 if isinstance(default_value, str):
698 return ''
699 # An empty list saved to the settings results in a None type being returned.
700- else:
701+ elif isinstance(default_value, list):
702 return []
703+ elif isinstance(setting, str):
704+ if '__Path__' in setting:
705+ return json.loads(setting, cls=OpenLPJsonDecoder)
706 # Convert the setting to the correct type.
707 if isinstance(default_value, bool):
708 if isinstance(setting, bool):
709@@ -502,3 +556,59 @@
710 if isinstance(default_value, int):
711 return int(setting)
712 return setting
713+
714+ def export(self, dest_path):
715+ """
716+ Export the settings to file.
717+
718+ :param openlp.core.common.path.Path dest_path: The file path to create the export file.
719+ :return: Success
720+ :rtype: bool
721+ """
722+ temp_path = Path(gettempdir(), 'openlp', 'exportConf.tmp')
723+ # Delete old files if found.
724+ if temp_path.exists():
725+ temp_path.unlink()
726+ if dest_path.exists():
727+ dest_path.unlink()
728+ self.remove('SettingsImport')
729+ # Get the settings.
730+ keys = self.allKeys()
731+ export_settings = QtCore.QSettings(str(temp_path), Settings.IniFormat)
732+ # Add a header section.
733+ # This is to insure it's our conf file for import.
734+ now = datetime.datetime.now()
735+ # Write INI format using QSettings.
736+ # Write our header.
737+ export_settings.beginGroup('SettingsImport')
738+ export_settings.setValue('Make_Changes', 'At_Own_RISK')
739+ export_settings.setValue('type', 'OpenLP_settings_export')
740+ export_settings.setValue('file_date_created', now.strftime("%Y-%m-%d %H:%M"))
741+ export_settings.endGroup()
742+ # Write all the sections and keys.
743+ for section_key in keys:
744+ # FIXME: We are conflicting with the standard "General" section.
745+ if 'eneral' in section_key:
746+ section_key = section_key.lower()
747+ key_value = super().value(section_key)
748+ if key_value is not None:
749+ export_settings.setValue(section_key, key_value)
750+ export_settings.sync()
751+ # Temp CONF file has been written. Blanks in keys are now '%20'.
752+ # Read the temp file and output the user's CONF file with blanks to
753+ # make it more readable.
754+ try:
755+ with dest_path.open('w') as export_conf_file, temp_path.open('r') as temp_conf:
756+ for file_record in temp_conf:
757+ # Get rid of any invalid entries.
758+ if file_record.find('@Invalid()') == -1:
759+ file_record = file_record.replace('%20', ' ')
760+ export_conf_file.write(file_record)
761+ except OSError as ose:
762+ QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
763+ translate('OpenLP.MainWindow',
764+ 'An error occurred while exporting the settings: {err}'
765+ ).format(err=ose.strerror),
766+ QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
767+ finally:
768+ temp_path.unlink()
769
770=== modified file 'openlp/core/lib/__init__.py'
771--- openlp/core/lib/__init__.py 2017-08-25 04:26:25 +0000
772+++ openlp/core/lib/__init__.py 2017-09-03 10:39:42 +0000
773@@ -89,7 +89,7 @@
774 returns False. If there is an error loading the file or the content can't be decoded then the function will return
775 None.
776
777- :param pathlib.Path text_file_path: The path to the file.
778+ :param openlp.core.common.path.Path text_file_path: The path to the file.
779 :return: The contents of the file, False if the file does not exist, or None if there is an Error reading or
780 decoding the file.
781 :rtype: str | False | None
782@@ -610,17 +610,11 @@
783 """
784 Apply a transformation function to the specified args or kwargs
785
786- :param args: Positional arguments
787- :type args: (,)
788-
789- :param kwargs: Key Word arguments
790- :type kwargs: dict
791-
792+ :param tuple args: Positional arguments
793+ :param dict kwargs: Key Word arguments
794 :param params: A tuple of tuples with the position and the key word to replace.
795- :type params: ((int, str, path_to_str),)
796-
797 :return: The modified positional and keyword arguments
798- :rtype: (tuple, dict)
799+ :rtype: tuple[tuple, dict]
800
801
802 Usage:
803
804=== modified file 'openlp/core/lib/mediamanageritem.py'
805--- openlp/core/lib/mediamanageritem.py 2017-08-11 20:47:52 +0000
806+++ openlp/core/lib/mediamanageritem.py 2017-09-03 10:39:42 +0000
807@@ -29,7 +29,7 @@
808 from PyQt5 import QtCore, QtWidgets
809
810 from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate
811-from openlp.core.common.path import path_to_str, str_to_path
812+from openlp.core.common.path import Path, path_to_str, str_to_path
813 from openlp.core.lib import ServiceItem, StringContent, ServiceItemContext
814 from openlp.core.lib.searchedit import SearchEdit
815 from openlp.core.lib.ui import create_widget_action, critical_error_message_box
816@@ -313,7 +313,7 @@
817 """
818 file_paths, selected_filter = FileDialog.getOpenFileNames(
819 self, self.on_new_prompt,
820- str_to_path(Settings().value(self.settings_section + '/last directory')),
821+ Settings().value(self.settings_section + '/last directory'),
822 self.on_new_file_masks)
823 log.info('New files(s) {file_paths}'.format(file_paths=file_paths))
824 if file_paths:
825@@ -377,9 +377,8 @@
826 self.list_view.clear()
827 self.load_list(full_list, target_group)
828 last_dir = os.path.split(files[0])[0]
829- Settings().setValue(self.settings_section + '/last directory', last_dir)
830- Settings().setValue('{section}/{section} files'.format(section=self.settings_section),
831- self.get_file_list())
832+ Settings().setValue(self.settings_section + '/last directory', Path(last_dir))
833+ Settings().setValue('{section}/{section} files'.format(section=self.settings_section), self.get_file_list())
834 if duplicates_found:
835 critical_error_message_box(UiStrings().Duplicate,
836 translate('OpenLP.MediaManagerItem',
837@@ -400,13 +399,15 @@
838 def get_file_list(self):
839 """
840 Return the current list of files
841+
842+ :rtype: list[openlp.core.common.path.Path]
843 """
844- file_list = []
845+ file_paths = []
846 for index in range(self.list_view.count()):
847 list_item = self.list_view.item(index)
848 filename = list_item.data(QtCore.Qt.UserRole)
849- file_list.append(filename)
850- return file_list
851+ file_paths.append(str_to_path(filename))
852+ return file_paths
853
854 def load_list(self, load_list, target_group):
855 """
856
857=== removed file 'openlp/core/lib/path.py'
858--- openlp/core/lib/path.py 2017-08-02 06:09:38 +0000
859+++ openlp/core/lib/path.py 1970-01-01 00:00:00 +0000
860@@ -1,61 +0,0 @@
861-# -*- coding: utf-8 -*-
862-# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
863-
864-###############################################################################
865-# OpenLP - Open Source Lyrics Projection #
866-# --------------------------------------------------------------------------- #
867-# Copyright (c) 2008-2017 OpenLP Developers #
868-# --------------------------------------------------------------------------- #
869-# This program is free software; you can redistribute it and/or modify it #
870-# under the terms of the GNU General Public License as published by the Free #
871-# Software Foundation; version 2 of the License. #
872-# #
873-# This program is distributed in the hope that it will be useful, but WITHOUT #
874-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
875-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
876-# more details. #
877-# #
878-# You should have received a copy of the GNU General Public License along #
879-# with this program; if not, write to the Free Software Foundation, Inc., 59 #
880-# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
881-###############################################################################
882-
883-from pathlib import Path
884-
885-
886-def path_to_str(path):
887- """
888- A utility function to convert a Path object or NoneType to a string equivalent.
889-
890- :param path: The value to convert to a string
891- :type: pathlib.Path or None
892-
893- :return: An empty string if :param:`path` is None, else a string representation of the :param:`path`
894- :rtype: str
895- """
896- if not isinstance(path, Path) and path is not None:
897- raise TypeError('parameter \'path\' must be of type Path or NoneType')
898- if path is None:
899- return ''
900- else:
901- return str(path)
902-
903-
904-def str_to_path(string):
905- """
906- A utility function to convert a str object to a Path or NoneType.
907-
908- This function is of particular use because initating a Path object with an empty string causes the Path object to
909- point to the current working directory.
910-
911- :param string: The string to convert
912- :type string: str
913-
914- :return: None if :param:`string` is empty, or a Path object representation of :param:`string`
915- :rtype: pathlib.Path or None
916- """
917- if not isinstance(string, str):
918- raise TypeError('parameter \'string\' must be of type str')
919- if string == '':
920- return None
921- return Path(string)
922
923=== modified file 'openlp/core/lib/plugin.py'
924--- openlp/core/lib/plugin.py 2016-12-31 11:01:36 +0000
925+++ openlp/core/lib/plugin.py 2017-09-03 10:39:42 +0000
926@@ -150,7 +150,7 @@
927 self.status = PluginStatus.Inactive
928 # Add the default status to the default settings.
929 default_settings[name + '/status'] = PluginStatus.Inactive
930- default_settings[name + '/last directory'] = ''
931+ default_settings[name + '/last directory'] = None
932 # Append a setting for files in the mediamanager (note not all plugins
933 # which have a mediamanager need this).
934 if media_item_class is not None:
935
936=== modified file 'openlp/core/ui/exceptionform.py'
937--- openlp/core/ui/exceptionform.py 2016-12-31 11:01:36 +0000
938+++ openlp/core/ui/exceptionform.py 2017-09-03 10:39:42 +0000
939@@ -70,9 +70,9 @@
940 except ImportError:
941 VLC_VERSION = '-'
942
943-from openlp.core.common import Settings, UiStrings, translate
944+from openlp.core.common import RegistryProperties, Settings, UiStrings, is_linux, translate
945 from openlp.core.common.versionchecker import get_application_version
946-from openlp.core.common import RegistryProperties, is_linux
947+from openlp.core.ui.lib.filedialog import FileDialog
948
949 from .exceptiondialog import Ui_ExceptionDialog
950
951@@ -139,17 +139,17 @@
952 """
953 Saving exception log and system information to a file.
954 """
955- filename = QtWidgets.QFileDialog.getSaveFileName(
956+ file_path, filter_used = FileDialog.getSaveFileName(
957 self,
958 translate('OpenLP.ExceptionForm', 'Save Crash Report'),
959 Settings().value(self.settings_section + '/last directory'),
960- translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))[0]
961- if filename:
962- filename = str(filename).replace('/', os.path.sep)
963- Settings().setValue(self.settings_section + '/last directory', os.path.dirname(filename))
964+ translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))
965+ if file_path:
966+ Settings().setValue(self.settings_section + '/last directory', file_path.parent)
967 opts = self._create_report()
968 report_text = self.report_text.format(version=opts['version'], description=opts['description'],
969 traceback=opts['traceback'], libs=opts['libs'], system=opts['system'])
970+ filename = str(file_path)
971 try:
972 report_file = open(filename, 'w')
973 try:
974@@ -212,17 +212,16 @@
975
976 def on_attach_file_button_clicked(self):
977 """
978- Attache files to the bug report e-mail.
979+ Attach files to the bug report e-mail.
980 """
981- files, filter_used = QtWidgets.QFileDialog.getOpenFileName(self,
982- translate('ImagePlugin.ExceptionDialog',
983- 'Select Attachment'),
984- Settings().value(self.settings_section +
985- '/last directory'),
986- '{text} (*)'.format(text=UiStrings().AllFiles))
987- log.info('New files(s) {files}'.format(files=str(files)))
988- if files:
989- self.file_attachment = str(files)
990+ file_path, filter_used = \
991+ FileDialog.getOpenFileName(self,
992+ translate('ImagePlugin.ExceptionDialog', 'Select Attachment'),
993+ Settings().value(self.settings_section + '/last directory'),
994+ '{text} (*)'.format(text=UiStrings().AllFiles))
995+ log.info('New file {file}'.format(file=file_path))
996+ if file_path:
997+ self.file_attachment = str(file_path)
998
999 def __button_state(self, state):
1000 """
1001
1002=== modified file 'openlp/core/ui/firsttimeform.py'
1003--- openlp/core/ui/firsttimeform.py 2017-08-23 20:13:58 +0000
1004+++ openlp/core/ui/firsttimeform.py 2017-09-03 10:39:42 +0000
1005@@ -30,13 +30,13 @@
1006 import urllib.parse
1007 import urllib.error
1008 from configparser import ConfigParser, MissingSectionHeaderError, NoOptionError, NoSectionError
1009-from pathlib import Path
1010 from tempfile import gettempdir
1011
1012 from PyQt5 import QtCore, QtWidgets
1013
1014 from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, \
1015 translate, clean_button_text, trace_error_handler
1016+from openlp.core.common.path import Path
1017 from openlp.core.lib import PluginStatus, build_icon
1018 from openlp.core.lib.ui import critical_error_message_box
1019 from openlp.core.common.httputils import get_web_page, get_url_file_size, url_get_file, CONNECTION_TIMEOUT
1020
1021=== modified file 'openlp/core/ui/generaltab.py'
1022--- openlp/core/ui/generaltab.py 2017-08-04 21:53:02 +0000
1023+++ openlp/core/ui/generaltab.py 2017-09-03 10:39:42 +0000
1024@@ -23,12 +23,11 @@
1025 The general tab of the configuration dialog.
1026 """
1027 import logging
1028-from pathlib import Path
1029
1030 from PyQt5 import QtCore, QtGui, QtWidgets
1031
1032 from openlp.core.common import Registry, Settings, UiStrings, translate, get_images_filter
1033-from openlp.core.common.path import path_to_str, str_to_path
1034+from openlp.core.common.path import Path, path_to_str, str_to_path
1035 from openlp.core.lib import SettingsTab, ScreenList
1036 from openlp.core.ui.lib import ColorButton, PathEdit
1037
1038@@ -294,7 +293,7 @@
1039 self.auto_open_check_box.setChecked(settings.value('auto open'))
1040 self.show_splash_check_box.setChecked(settings.value('show splash'))
1041 self.logo_background_color = settings.value('logo background color')
1042- self.logo_file_path_edit.path = str_to_path(settings.value('logo file'))
1043+ self.logo_file_path_edit.path = settings.value('logo file')
1044 self.logo_hide_on_startup_check_box.setChecked(settings.value('logo hide on startup'))
1045 self.logo_color_button.color = self.logo_background_color
1046 self.check_for_updates_check_box.setChecked(settings.value('update check'))
1047@@ -328,7 +327,7 @@
1048 settings.setValue('auto open', self.auto_open_check_box.isChecked())
1049 settings.setValue('show splash', self.show_splash_check_box.isChecked())
1050 settings.setValue('logo background color', self.logo_background_color)
1051- settings.setValue('logo file', path_to_str(self.logo_file_path_edit.path))
1052+ settings.setValue('logo file', self.logo_file_path_edit.path)
1053 settings.setValue('logo hide on startup', self.logo_hide_on_startup_check_box.isChecked())
1054 settings.setValue('update check', self.check_for_updates_check_box.isChecked())
1055 settings.setValue('save prompt', self.save_check_service_check_box.isChecked())
1056
1057=== modified file 'openlp/core/ui/lib/filedialog.py'
1058--- openlp/core/ui/lib/filedialog.py 2017-08-10 06:28:30 +0000
1059+++ openlp/core/ui/lib/filedialog.py 2017-09-03 10:39:42 +0000
1060@@ -20,11 +20,9 @@
1061 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
1062 ###############################################################################
1063 """ Patch the QFileDialog so it accepts and returns Path objects"""
1064-from pathlib import Path
1065-
1066 from PyQt5 import QtWidgets
1067
1068-from openlp.core.common.path import path_to_str, str_to_path
1069+from openlp.core.common.path import Path, path_to_str, str_to_path
1070 from openlp.core.lib import replace_params
1071
1072
1073@@ -36,7 +34,7 @@
1074
1075 :type parent: QtWidgets.QWidget or None
1076 :type caption: str
1077- :type directory: pathlib.Path
1078+ :type directory: openlp.core.common.path.Path
1079 :type options: QtWidgets.QFileDialog.Options
1080 :rtype: tuple[Path, str]
1081 """
1082@@ -55,7 +53,7 @@
1083
1084 :type parent: QtWidgets.QWidget or None
1085 :type caption: str
1086- :type directory: pathlib.Path
1087+ :type directory: openlp.core.common.path.Path
1088 :type filter: str
1089 :type initialFilter: str
1090 :type options: QtWidgets.QFileDialog.Options
1091@@ -76,7 +74,7 @@
1092
1093 :type parent: QtWidgets.QWidget or None
1094 :type caption: str
1095- :type directory: pathlib.Path
1096+ :type directory: openlp.core.common.path.Path
1097 :type filter: str
1098 :type initialFilter: str
1099 :type options: QtWidgets.QFileDialog.Options
1100@@ -98,7 +96,7 @@
1101
1102 :type parent: QtWidgets.QWidget or None
1103 :type caption: str
1104- :type directory: pathlib.Path
1105+ :type directory: openlp.core.common.path.Path
1106 :type filter: str
1107 :type initialFilter: str
1108 :type options: QtWidgets.QFileDialog.Options
1109
1110=== modified file 'openlp/core/ui/lib/pathedit.py'
1111--- openlp/core/ui/lib/pathedit.py 2017-08-07 20:50:01 +0000
1112+++ openlp/core/ui/lib/pathedit.py 2017-09-03 10:39:42 +0000
1113@@ -20,12 +20,11 @@
1114 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
1115 ###############################################################################
1116 from enum import Enum
1117-from pathlib import Path
1118
1119 from PyQt5 import QtCore, QtWidgets
1120
1121 from openlp.core.common import UiStrings, translate
1122-from openlp.core.common.path import path_to_str, str_to_path
1123+from openlp.core.common.path import Path, path_to_str, str_to_path
1124 from openlp.core.lib import build_icon
1125 from openlp.core.ui.lib.filedialog import FileDialog
1126
1127@@ -46,19 +45,11 @@
1128 """
1129 Initialise the PathEdit widget
1130
1131- :param parent: The parent of the widget. This is just passed to the super method.
1132- :type parent: QWidget or None
1133-
1134- :param dialog_caption: Used to customise the caption in the QFileDialog.
1135- :type dialog_caption: str
1136-
1137- :param default_path: The default path. This is set as the path when the revert button is clicked
1138- :type default_path: pathlib.Path
1139-
1140- :param show_revert: Used to determine if the 'revert button' should be visible.
1141- :type show_revert: bool
1142-
1143- :return: None
1144+ :param QtWidget.QWidget | None: The parent of the widget. This is just passed to the super method.
1145+ :param str dialog_caption: Used to customise the caption in the QFileDialog.
1146+ :param openlp.core.common.path.Path default_path: The default path. This is set as the path when the revert
1147+ button is clicked
1148+ :param bool show_revert: Used to determine if the 'revert button' should be visible.
1149 :rtype: None
1150 """
1151 super().__init__(parent)
1152@@ -72,10 +63,7 @@
1153 def _setup(self, show_revert):
1154 """
1155 Set up the widget
1156- :param show_revert: Show or hide the revert button
1157- :type show_revert: bool
1158-
1159- :return: None
1160+ :param bool show_revert: Show or hide the revert button
1161 :rtype: None
1162 """
1163 widget_layout = QtWidgets.QHBoxLayout()
1164@@ -102,7 +90,7 @@
1165 A property getter method to return the selected path.
1166
1167 :return: The selected path
1168- :rtype: pathlib.Path
1169+ :rtype: openlp.core.common.path.Path
1170 """
1171 return self._path
1172
1173@@ -111,10 +99,7 @@
1174 """
1175 A Property setter method to set the selected path
1176
1177- :param path: The path to set the widget to
1178- :type path: pathlib.Path
1179-
1180- :return: None
1181+ :param openlp.core.common.path.Path path: The path to set the widget to
1182 :rtype: None
1183 """
1184 self._path = path
1185@@ -138,10 +123,7 @@
1186 """
1187 A Property setter method to set the path type
1188
1189- :param path_type: The type of path to select
1190- :type path_type: PathType
1191-
1192- :return: None
1193+ :param PathType path_type: The type of path to select
1194 :rtype: None
1195 """
1196 self._path_type = path_type
1197@@ -151,7 +133,6 @@
1198 """
1199 Called to update the tooltips on the buttons. This is changing path types, and when the widget is initalised
1200
1201- :return: None
1202 :rtype: None
1203 """
1204 if self._path_type == PathType.Directories:
1205@@ -167,7 +148,6 @@
1206
1207 Show the QFileDialog and process the input from the user
1208
1209- :return: None
1210 :rtype: None
1211 """
1212 caption = self.dialog_caption
1213@@ -189,7 +169,6 @@
1214
1215 Set the new path to the value of the default_path instance variable.
1216
1217- :return: None
1218 :rtype: None
1219 """
1220 self.on_new_path(self.default_path)
1221@@ -198,7 +177,6 @@
1222 """
1223 A handler to handle when the line edit has finished being edited.
1224
1225- :return: None
1226 :rtype: None
1227 """
1228 path = str_to_path(self.line_edit.text())
1229@@ -210,10 +188,7 @@
1230
1231 Emits the pathChanged Signal
1232
1233- :param path: The new path
1234- :type path: pathlib.Path
1235-
1236- :return: None
1237+ :param openlp.core.common.path.Path path: The new path
1238 :rtype: None
1239 """
1240 if self._path != path:
1241
1242=== modified file 'openlp/core/ui/lib/wizard.py'
1243--- openlp/core/ui/lib/wizard.py 2017-06-09 06:06:49 +0000
1244+++ openlp/core/ui/lib/wizard.py 2017-09-03 10:39:42 +0000
1245@@ -30,6 +30,7 @@
1246 from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate, is_macosx
1247 from openlp.core.lib import build_icon
1248 from openlp.core.lib.ui import add_welcome_page
1249+from openlp.core.ui.lib.filedialog import FileDialog
1250
1251 log = logging.getLogger(__name__)
1252
1253@@ -278,37 +279,38 @@
1254
1255 def get_file_name(self, title, editbox, setting_name, filters=''):
1256 """
1257- Opens a QFileDialog and saves the filename to the given editbox.
1258+ Opens a FileDialog and saves the filename to the given editbox.
1259
1260- :param title: The title of the dialog (unicode).
1261- :param editbox: An editbox (QLineEdit).
1262- :param setting_name: The place where to save the last opened directory.
1263- :param filters: The file extension filters. It should contain the file description
1264+ :param str title: The title of the dialog.
1265+ :param QtWidgets.QLineEdit editbox: An QLineEdit.
1266+ :param str setting_name: The place where to save the last opened directory.
1267+ :param str filters: The file extension filters. It should contain the file description
1268 as well as the file extension. For example::
1269
1270 'OpenLP 2 Databases (*.sqlite)'
1271+ :rtype: None
1272 """
1273 if filters:
1274 filters += ';;'
1275 filters += '%s (*)' % UiStrings().AllFiles
1276- filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(
1277- self, title, os.path.dirname(Settings().value(self.plugin.settings_section + '/' + setting_name)),
1278- filters)
1279- if filename:
1280- editbox.setText(filename)
1281- Settings().setValue(self.plugin.settings_section + '/' + setting_name, filename)
1282+ file_path, filter_used = FileDialog.getOpenFileName(
1283+ self, title, Settings().value(self.plugin.settings_section + '/' + setting_name), filters)
1284+ if file_path:
1285+ editbox.setText(str(file_path))
1286+ Settings().setValue(self.plugin.settings_section + '/' + setting_name, file_path.parent)
1287
1288 def get_folder(self, title, editbox, setting_name):
1289 """
1290- Opens a QFileDialog and saves the selected folder to the given editbox.
1291+ Opens a FileDialog and saves the selected folder to the given editbox.
1292
1293- :param title: The title of the dialog (unicode).
1294- :param editbox: An editbox (QLineEdit).
1295- :param setting_name: The place where to save the last opened directory.
1296+ :param str title: The title of the dialog.
1297+ :param QtWidgets.QLineEdit editbox: An QLineEditbox.
1298+ :param str setting_name: The place where to save the last opened directory.
1299+ :rtype: None
1300 """
1301- folder = QtWidgets.QFileDialog.getExistingDirectory(
1302+ folder_path = FileDialog.getExistingDirectory(
1303 self, title, Settings().value(self.plugin.settings_section + '/' + setting_name),
1304 QtWidgets.QFileDialog.ShowDirsOnly)
1305- if folder:
1306- editbox.setText(folder)
1307- Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder)
1308+ if folder_path:
1309+ editbox.setText(str(folder_path))
1310+ Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder_path)
1311
1312=== modified file 'openlp/core/ui/maindisplay.py'
1313--- openlp/core/ui/maindisplay.py 2017-08-26 14:01:04 +0000
1314+++ openlp/core/ui/maindisplay.py 2017-09-03 10:39:42 +0000
1315@@ -37,6 +37,7 @@
1316
1317 from openlp.core.common import AppLocation, Registry, RegistryProperties, OpenLPMixin, Settings, translate,\
1318 is_macosx, is_win
1319+from openlp.core.common.path import path_to_str
1320 from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte
1321 from openlp.core.lib.theme import BackgroundType
1322 from openlp.core.ui import HideMode, AlertLocation, DisplayControllerType
1323@@ -259,7 +260,7 @@
1324 background_color.setNamedColor(Settings().value('core/logo background color'))
1325 if not background_color.isValid():
1326 background_color = QtCore.Qt.white
1327- image_file = Settings().value('core/logo file')
1328+ image_file = path_to_str(Settings().value('core/logo file'))
1329 splash_image = QtGui.QImage(image_file)
1330 self.initial_fame = QtGui.QImage(
1331 self.screen['size'].width(),
1332
1333=== modified file 'openlp/core/ui/mainwindow.py'
1334--- openlp/core/ui/mainwindow.py 2017-08-23 20:13:58 +0000
1335+++ openlp/core/ui/mainwindow.py 2017-09-03 10:39:42 +0000
1336@@ -30,7 +30,6 @@
1337 from datetime import datetime
1338 from distutils import dir_util
1339 from distutils.errors import DistutilsFileError
1340-from pathlib import Path
1341 from tempfile import gettempdir
1342
1343 from PyQt5 import QtCore, QtGui, QtWidgets
1344@@ -40,6 +39,7 @@
1345 from openlp.core.common import Registry, RegistryProperties, AppLocation, LanguageManager, Settings, UiStrings, \
1346 check_directory_exists, translate, is_win, is_macosx, add_actions
1347 from openlp.core.common.actions import ActionList, CategoryOrder
1348+from openlp.core.common.path import Path, path_to_str, str_to_path
1349 from openlp.core.common.versionchecker import get_application_version
1350 from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon
1351 from openlp.core.lib.ui import create_action
1352@@ -50,6 +50,7 @@
1353 from openlp.core.ui.printserviceform import PrintServiceForm
1354 from openlp.core.ui.projector.manager import ProjectorManager
1355 from openlp.core.ui.lib.dockwidget import OpenLPDockWidget
1356+from openlp.core.ui.lib.filedialog import FileDialog
1357 from openlp.core.ui.lib.mediadockmanager import MediaDockManager
1358
1359
1360@@ -876,11 +877,12 @@
1361 shutil.copyfile(import_file_name, temp_config)
1362 settings = Settings()
1363 import_settings = Settings(temp_config, Settings.IniFormat)
1364- # Convert image files
1365+
1366 log.info('hook upgrade_plugin_settings')
1367 self.plugin_manager.hook_upgrade_plugin_settings(import_settings)
1368- # Remove/rename old settings to prepare the import.
1369- import_settings.remove_obsolete_settings()
1370+ # Upgrade settings to prepare the import.
1371+ if import_settings.can_upgrade():
1372+ import_settings.upgrade_settings()
1373 # Lets do a basic sanity check. If it contains this string we can assume it was created by OpenLP and so we'll
1374 # load what we can from it, and just silently ignore anything we don't recognise.
1375 if import_settings.value('SettingsImport/type') != 'OpenLP_settings_export':
1376@@ -936,89 +938,17 @@
1377 """
1378 Export settings to a .conf file in INI format
1379 """
1380- export_file_name, filter_used = QtWidgets.QFileDialog.getSaveFileName(
1381+ export_file_path, filter_used = FileDialog.getSaveFileName(
1382 self,
1383 translate('OpenLP.MainWindow', 'Export Settings File'),
1384- '',
1385+ None,
1386 translate('OpenLP.MainWindow', 'OpenLP Settings (*.conf)'))
1387- if not export_file_name:
1388+ if not export_file_path:
1389 return
1390- # Make sure it's a .conf file.
1391- if not export_file_name.endswith('conf'):
1392- export_file_name += '.conf'
1393- temp_file = os.path.join(gettempdir(), 'openlp', 'exportConf.tmp')
1394+ # Make sure it's a .conf file.
1395+ export_file_path = export_file_path.with_suffix('.conf')
1396 self.save_settings()
1397- setting_sections = []
1398- # Add main sections.
1399- setting_sections.extend([self.general_settings_section])
1400- setting_sections.extend([self.advanced_settings_section])
1401- setting_sections.extend([self.ui_settings_section])
1402- setting_sections.extend([self.shortcuts_settings_section])
1403- setting_sections.extend([self.service_manager_settings_section])
1404- setting_sections.extend([self.themes_settings_section])
1405- setting_sections.extend([self.display_tags_section])
1406- # Add plugin sections.
1407- for plugin in self.plugin_manager.plugins:
1408- setting_sections.extend([plugin.name])
1409- # Delete old files if found.
1410- if os.path.exists(temp_file):
1411- os.remove(temp_file)
1412- if os.path.exists(export_file_name):
1413- os.remove(export_file_name)
1414- settings = Settings()
1415- settings.remove(self.header_section)
1416- # Get the settings.
1417- keys = settings.allKeys()
1418- export_settings = Settings(temp_file, Settings.IniFormat)
1419- # Add a header section.
1420- # This is to insure it's our conf file for import.
1421- now = datetime.now()
1422- application_version = get_application_version()
1423- # Write INI format using Qsettings.
1424- # Write our header.
1425- export_settings.beginGroup(self.header_section)
1426- export_settings.setValue('Make_Changes', 'At_Own_RISK')
1427- export_settings.setValue('type', 'OpenLP_settings_export')
1428- export_settings.setValue('file_date_created', now.strftime("%Y-%m-%d %H:%M"))
1429- export_settings.setValue('version', application_version['full'])
1430- export_settings.endGroup()
1431- # Write all the sections and keys.
1432- for section_key in keys:
1433- # FIXME: We are conflicting with the standard "General" section.
1434- if 'eneral' in section_key:
1435- section_key = section_key.lower()
1436- try:
1437- key_value = settings.value(section_key)
1438- except KeyError:
1439- QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
1440- translate('OpenLP.MainWindow', 'The key "{key}" does not have a default '
1441- 'value so it will be skipped in this '
1442- 'export.').format(key=section_key),
1443- QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
1444- key_value = None
1445- if key_value is not None:
1446- export_settings.setValue(section_key, key_value)
1447- export_settings.sync()
1448- # Temp CONF file has been written. Blanks in keys are now '%20'.
1449- # Read the temp file and output the user's CONF file with blanks to
1450- # make it more readable.
1451- temp_conf = open(temp_file, 'r')
1452- try:
1453- export_conf = open(export_file_name, 'w')
1454- for file_record in temp_conf:
1455- # Get rid of any invalid entries.
1456- if file_record.find('@Invalid()') == -1:
1457- file_record = file_record.replace('%20', ' ')
1458- export_conf.write(file_record)
1459- temp_conf.close()
1460- export_conf.close()
1461- os.remove(temp_file)
1462- except OSError as ose:
1463- QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
1464- translate('OpenLP.MainWindow',
1465- 'An error occurred while exporting the '
1466- 'settings: {err}').format(err=ose.strerror),
1467- QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
1468+ Settings().export(export_file_path)
1469
1470 def on_mode_default_item_clicked(self):
1471 """
1472@@ -1277,7 +1207,7 @@
1473 settings.remove('custom slide')
1474 settings.remove('service')
1475 settings.beginGroup(self.general_settings_section)
1476- self.recent_files = settings.value('recent files')
1477+ self.recent_files = [path_to_str(file_path) for file_path in settings.value('recent files')]
1478 settings.endGroup()
1479 settings.beginGroup(self.ui_settings_section)
1480 self.move(settings.value('main window position'))
1481@@ -1301,7 +1231,7 @@
1482 log.debug('Saving QSettings')
1483 settings = Settings()
1484 settings.beginGroup(self.general_settings_section)
1485- settings.setValue('recent files', self.recent_files)
1486+ settings.setValue('recent files', [str_to_path(file) for file in self.recent_files])
1487 settings.endGroup()
1488 settings.beginGroup(self.ui_settings_section)
1489 settings.setValue('main window position', self.pos())
1490@@ -1443,7 +1373,7 @@
1491 log.info('No data copy requested')
1492 # Change the location of data directory in config file.
1493 settings = QtCore.QSettings()
1494- settings.setValue('advanced/data path', self.new_data_path)
1495+ settings.setValue('advanced/data path', Path(self.new_data_path))
1496 # Check if the new data path is our default.
1497 if self.new_data_path == str(AppLocation.get_directory(AppLocation.DataDir)):
1498 settings.remove('advanced/data path')
1499
1500=== modified file 'openlp/core/ui/servicemanager.py'
1501--- openlp/core/ui/servicemanager.py 2017-08-23 20:13:58 +0000
1502+++ openlp/core/ui/servicemanager.py 2017-09-03 10:39:42 +0000
1503@@ -28,7 +28,6 @@
1504 import shutil
1505 import zipfile
1506 from datetime import datetime, timedelta
1507-from pathlib import Path
1508 from tempfile import mkstemp
1509
1510 from PyQt5 import QtCore, QtGui, QtWidgets
1511@@ -36,11 +35,13 @@
1512 from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, ThemeLevel, OpenLPMixin, \
1513 RegistryMixin, check_directory_exists, UiStrings, translate, split_filename, delete_file
1514 from openlp.core.common.actions import ActionList, CategoryOrder
1515+from openlp.core.common.languagemanager import format_time
1516+from openlp.core.common.path import Path, path_to_str, str_to_path
1517 from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon
1518 from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
1519 from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
1520 from openlp.core.ui.lib import OpenLPToolbar
1521-from openlp.core.common.languagemanager import format_time
1522+from openlp.core.ui.lib.filedialog import FileDialog
1523
1524
1525 class ServiceManagerList(QtWidgets.QTreeWidget):
1526@@ -373,7 +374,7 @@
1527 """
1528 self._file_name = str(file_name)
1529 self.main_window.set_service_modified(self.is_modified(), self.short_file_name())
1530- Settings().setValue('servicemanager/last file', file_name)
1531+ Settings().setValue('servicemanager/last file', Path(file_name))
1532 self._save_lite = self._file_name.endswith('.oszl')
1533
1534 def file_name(self):
1535@@ -435,18 +436,17 @@
1536 elif result == QtWidgets.QMessageBox.Save:
1537 self.decide_save_method()
1538 if not load_file:
1539- file_name, filter_used = QtWidgets.QFileDialog.getOpenFileName(
1540+ file_path, filter_used = FileDialog.getOpenFileName(
1541 self.main_window,
1542 translate('OpenLP.ServiceManager', 'Open File'),
1543 Settings().value(self.main_window.service_manager_settings_section + '/last directory'),
1544 translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz *.oszl)'))
1545- if not file_name:
1546+ if not file_path:
1547 return False
1548 else:
1549- file_name = load_file
1550- Settings().setValue(self.main_window.service_manager_settings_section + '/last directory',
1551- split_filename(file_name)[0])
1552- self.load_file(file_name)
1553+ file_path = str_to_path(load_file)
1554+ Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
1555+ self.load_file(str(file_path))
1556
1557 def save_modified_service(self):
1558 """
1559@@ -477,7 +477,7 @@
1560 self.set_file_name('')
1561 self.service_id += 1
1562 self.set_modified(False)
1563- Settings().setValue('servicemanager/last file', '')
1564+ Settings().setValue('servicemanager/last file', None)
1565 self.plugin_manager.new_service_created()
1566
1567 def create_basic_service(self):
1568@@ -513,7 +513,7 @@
1569 base_name = os.path.splitext(file_name)[0]
1570 service_file_name = '{name}.osj'.format(name=base_name)
1571 self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
1572- Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
1573+ Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
1574 service = self.create_basic_service()
1575 write_list = []
1576 missing_list = []
1577@@ -634,7 +634,7 @@
1578 base_name = os.path.splitext(file_name)[0]
1579 service_file_name = '{name}.osj'.format(name=base_name)
1580 self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
1581- Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
1582+ Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
1583 service = self.create_basic_service()
1584 self.application.set_busy_cursor()
1585 # Number of items + 1 to zip it
1586@@ -695,7 +695,7 @@
1587 default_file_name = format_time(default_pattern, local_time)
1588 else:
1589 default_file_name = ''
1590- directory = Settings().value(self.main_window.service_manager_settings_section + '/last directory')
1591+ directory = path_to_str(Settings().value(self.main_window.service_manager_settings_section + '/last directory'))
1592 path = os.path.join(directory, default_file_name)
1593 # SaveAs from osz to oszl is not valid as the files will be deleted on exit which is not sensible or usable in
1594 # the long term.
1595@@ -778,7 +778,7 @@
1596 delete_file(Path(p_file))
1597 self.main_window.add_recent_file(file_name)
1598 self.set_modified(False)
1599- Settings().setValue('servicemanager/last file', file_name)
1600+ Settings().setValue('servicemanager/last file', Path(file_name))
1601 else:
1602 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
1603 self.log_error('File contains no service data')
1604@@ -843,7 +843,7 @@
1605 Load the last service item from the service manager when the service was last closed. Can be blank if there was
1606 no service present.
1607 """
1608- file_name = Settings().value('servicemanager/last file')
1609+ file_name = str_to_path(Settings().value('servicemanager/last file'))
1610 if file_name:
1611 self.load_file(file_name)
1612
1613
1614=== modified file 'openlp/core/ui/themeform.py'
1615--- openlp/core/ui/themeform.py 2017-08-12 17:45:56 +0000
1616+++ openlp/core/ui/themeform.py 2017-09-03 10:39:42 +0000
1617@@ -24,12 +24,11 @@
1618 """
1619 import logging
1620 import os
1621-from pathlib import Path
1622
1623 from PyQt5 import QtCore, QtGui, QtWidgets
1624
1625 from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file
1626-from openlp.core.common.path import path_to_str, str_to_path
1627+from openlp.core.common.path import Path, path_to_str, str_to_path
1628 from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
1629 from openlp.core.lib.ui import critical_error_message_box
1630 from openlp.core.ui import ThemeLayoutForm
1631
1632=== modified file 'openlp/core/ui/thememanager.py'
1633--- openlp/core/ui/thememanager.py 2017-08-12 17:45:56 +0000
1634+++ openlp/core/ui/thememanager.py 2017-09-03 10:39:42 +0000
1635@@ -25,14 +25,13 @@
1636 import os
1637 import zipfile
1638 import shutil
1639-from pathlib import Path
1640
1641 from xml.etree.ElementTree import ElementTree, XML
1642 from PyQt5 import QtCore, QtGui, QtWidgets
1643
1644 from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \
1645 UiStrings, check_directory_exists, translate, is_win, get_filesystem_encoding, delete_file
1646-from openlp.core.common.path import path_to_str, str_to_path
1647+from openlp.core.common.path import Path, path_to_str, str_to_path
1648 from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \
1649 check_item_selected, create_thumb, validate_thumb
1650 from openlp.core.lib.theme import Theme, BackgroundType
1651@@ -379,16 +378,16 @@
1652 critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.'))
1653 return
1654 theme = item.data(QtCore.Qt.UserRole)
1655- path, filter_used = \
1656- QtWidgets.QFileDialog.getSaveFileName(self.main_window,
1657- translate('OpenLP.ThemeManager', 'Save Theme - ({name})').
1658- format(name=theme),
1659- Settings().value(self.settings_section + '/last directory export'),
1660- translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
1661+ export_path, filter_used = \
1662+ FileDialog.getSaveFileName(self.main_window,
1663+ translate('OpenLP.ThemeManager', 'Save Theme - ({name})').
1664+ format(name=theme),
1665+ Settings().value(self.settings_section + '/last directory export'),
1666+ translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
1667 self.application.set_busy_cursor()
1668- if path:
1669- Settings().setValue(self.settings_section + '/last directory export', path)
1670- if self._export_theme(path, theme):
1671+ if export_path:
1672+ Settings().setValue(self.settings_section + '/last directory export', export_path.parent)
1673+ if self._export_theme(str(export_path), theme):
1674 QtWidgets.QMessageBox.information(self,
1675 translate('OpenLP.ThemeManager', 'Theme Exported'),
1676 translate('OpenLP.ThemeManager',
1677@@ -429,16 +428,15 @@
1678 file_paths, selected_filter = FileDialog.getOpenFileNames(
1679 self,
1680 translate('OpenLP.ThemeManager', 'Select Theme Import File'),
1681- str_to_path(Settings().value(self.settings_section + '/last directory import')),
1682+ Settings().value(self.settings_section + '/last directory import'),
1683 translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
1684 self.log_info('New Themes {file_paths}'.format(file_paths=file_paths))
1685 if not file_paths:
1686 return
1687 self.application.set_busy_cursor()
1688 for file_path in file_paths:
1689- file_name = path_to_str(file_path)
1690- Settings().setValue(self.settings_section + '/last directory import', str(file_name))
1691- self.unzip_theme(file_name, self.path)
1692+ self.unzip_theme(path_to_str(file_path), self.path)
1693+ Settings().setValue(self.settings_section + '/last directory import', file_path)
1694 self.load_themes()
1695 self.application.set_normal_cursor()
1696
1697
1698=== modified file 'openlp/core/ui/themewizard.py'
1699--- openlp/core/ui/themewizard.py 2017-08-07 20:50:01 +0000
1700+++ openlp/core/ui/themewizard.py 2017-09-03 10:39:42 +0000
1701@@ -22,11 +22,10 @@
1702 """
1703 The Create/Edit theme wizard
1704 """
1705-from pathlib import Path
1706-
1707 from PyQt5 import QtCore, QtGui, QtWidgets
1708
1709 from openlp.core.common import UiStrings, translate, is_macosx
1710+from openlp.core.common.path import Path
1711 from openlp.core.lib import build_icon
1712 from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType
1713 from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets
1714
1715=== modified file 'openlp/plugins/bibles/bibleplugin.py'
1716--- openlp/plugins/bibles/bibleplugin.py 2017-08-03 17:54:40 +0000
1717+++ openlp/plugins/bibles/bibleplugin.py 2017-09-03 10:39:42 +0000
1718@@ -59,7 +59,7 @@
1719 'bibles/range separator': '',
1720 'bibles/list separator': '',
1721 'bibles/end separator': '',
1722- 'bibles/last directory import': '',
1723+ 'bibles/last directory import': None,
1724 'bibles/hide combined quick error': False,
1725 'bibles/is search while typing enabled': True
1726 }
1727
1728=== modified file 'openlp/plugins/bibles/lib/importers/csvbible.py'
1729--- openlp/plugins/bibles/lib/importers/csvbible.py 2017-08-12 17:45:56 +0000
1730+++ openlp/plugins/bibles/lib/importers/csvbible.py 2017-09-03 10:39:42 +0000
1731@@ -51,9 +51,9 @@
1732 """
1733 import csv
1734 from collections import namedtuple
1735-from pathlib import Path
1736
1737 from openlp.core.common import get_file_encoding, translate
1738+from openlp.core.common.path import Path
1739 from openlp.core.lib.exceptions import ValidationError
1740 from openlp.plugins.bibles.lib.bibleimport import BibleImport
1741
1742
1743=== modified file 'openlp/plugins/bibles/lib/manager.py'
1744--- openlp/plugins/bibles/lib/manager.py 2017-08-12 17:45:56 +0000
1745+++ openlp/plugins/bibles/lib/manager.py 2017-09-03 10:39:42 +0000
1746@@ -21,10 +21,9 @@
1747 ###############################################################################
1748
1749 import logging
1750-import os
1751-from pathlib import Path
1752
1753 from openlp.core.common import AppLocation, OpenLPMixin, RegistryProperties, Settings, translate, delete_file, UiStrings
1754+from openlp.core.common.path import Path
1755 from openlp.plugins.bibles.lib import LanguageSelection, parse_reference
1756 from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
1757 from .importers.csvbible import CSVBible
1758@@ -306,13 +305,10 @@
1759 """
1760 Does a verse search for the given bible and text.
1761
1762- :param bible: The bible to search
1763- :type bible: str
1764- :param text: The text to search for
1765- :type text: str
1766-
1767+ :param str bible: The bible to search
1768+ :param str text: The text to search for
1769 :return: The search results if valid, or None if the search is invalid.
1770- :rtype: None, list
1771+ :rtype: None | list
1772 """
1773 log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))
1774 if not text:
1775
1776=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
1777--- openlp/plugins/bibles/lib/mediaitem.py 2017-06-09 15:56:40 +0000
1778+++ openlp/plugins/bibles/lib/mediaitem.py 2017-09-03 10:39:42 +0000
1779@@ -465,8 +465,7 @@
1780 """
1781 Show the selected tab and set focus to it
1782
1783- :param index: The tab selected
1784- :type index: int
1785+ :param int index: The tab selected
1786 :return: None
1787 """
1788 if index == SearchTabs.Search or index == SearchTabs.Select:
1789@@ -483,7 +482,7 @@
1790 Update list_widget with the contents of the selected list
1791
1792 :param index: The index of the tab that has been changed to. (int)
1793- :return: None
1794+ :rtype: None
1795 """
1796 if index == ResultsTab.Saved:
1797 self.add_built_results_to_list_widget(self.saved_results)
1798
1799=== modified file 'openlp/plugins/images/imageplugin.py'
1800--- openlp/plugins/images/imageplugin.py 2017-03-03 19:27:31 +0000
1801+++ openlp/plugins/images/imageplugin.py 2017-09-03 10:39:42 +0000
1802@@ -71,14 +71,6 @@
1803 'provided by the theme.')
1804 return about_text
1805
1806- def upgrade_settings(self, settings):
1807- """
1808- Upgrade the settings of this plugin.
1809-
1810- :param settings: The Settings object containing the old settings.
1811- """
1812- pass
1813-
1814 def set_plugin_text_strings(self):
1815 """
1816 Called to define all translatable texts of the plugin.
1817
1818=== modified file 'openlp/plugins/images/lib/mediaitem.py'
1819--- openlp/plugins/images/lib/mediaitem.py 2017-08-23 20:13:58 +0000
1820+++ openlp/plugins/images/lib/mediaitem.py 2017-09-03 10:39:42 +0000
1821@@ -22,12 +22,12 @@
1822
1823 import logging
1824 import os
1825-from pathlib import Path
1826
1827 from PyQt5 import QtCore, QtGui, QtWidgets
1828
1829 from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists, translate, \
1830 delete_file, get_images_filter
1831+from openlp.core.common.path import Path
1832 from openlp.core.lib import ItemCapabilities, MediaManagerItem, ServiceItemContext, StringContent, build_icon, \
1833 check_item_selected, create_thumb, validate_thumb
1834 from openlp.core.lib.ui import create_widget_action, critical_error_message_box
1835@@ -390,7 +390,7 @@
1836 self.application.set_normal_cursor()
1837 self.load_list(files, target_group)
1838 last_dir = os.path.split(files[0])[0]
1839- Settings().setValue(self.settings_section + '/last directory', last_dir)
1840+ Settings().setValue(self.settings_section + '/last directory', Path(last_dir))
1841
1842 def load_list(self, images, target_group=None, initial_load=False):
1843 """
1844
1845=== modified file 'openlp/plugins/media/lib/mediaitem.py'
1846--- openlp/plugins/media/lib/mediaitem.py 2017-08-23 20:13:58 +0000
1847+++ openlp/plugins/media/lib/mediaitem.py 2017-09-03 10:39:42 +0000
1848@@ -22,12 +22,12 @@
1849
1850 import logging
1851 import os
1852-from pathlib import Path
1853
1854 from PyQt5 import QtCore, QtWidgets
1855
1856 from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, UiStrings,\
1857 translate
1858+from openlp.core.common.path import Path, path_to_str, str_to_path
1859 from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \
1860 build_icon, check_item_selected
1861 from openlp.core.lib.ui import create_widget_action, critical_error_message_box, create_horizontal_adjusting_combo_box
1862@@ -303,7 +303,7 @@
1863 self.list_view.clear()
1864 self.service_path = os.path.join(str(AppLocation.get_section_data_path(self.settings_section)), 'thumbnails')
1865 check_directory_exists(Path(self.service_path))
1866- self.load_list(Settings().value(self.settings_section + '/media files'))
1867+ self.load_list([path_to_str(file) for file in Settings().value(self.settings_section + '/media files')])
1868 self.rebuild_players()
1869
1870 def rebuild_players(self):
1871@@ -401,14 +401,14 @@
1872 :param media_type: Type to get, defaults to audio.
1873 :return: The media list
1874 """
1875- media = Settings().value(self.settings_section + '/media files')
1876- media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
1877+ media_file_paths = Settings().value(self.settings_section + '/media files')
1878+ media_file_paths.sort(key=lambda file_path: get_locale_key(file_path.name))
1879 if media_type == MediaType.Audio:
1880 extension = self.media_controller.audio_extensions_list
1881 else:
1882 extension = self.media_controller.video_extensions_list
1883 extension = [x[1:] for x in extension]
1884- media = [x for x in media if os.path.splitext(x)[1] in extension]
1885+ media = [x for x in media_file_paths if x.suffix in extension]
1886 return media
1887
1888 def search(self, string, show_error):
1889@@ -419,13 +419,12 @@
1890 :param show_error: Should the error be shown (True)
1891 :return: The search result.
1892 """
1893- files = Settings().value(self.settings_section + '/media files')
1894 results = []
1895 string = string.lower()
1896- for file in files:
1897- filename = os.path.split(str(file))[1]
1898- if filename.lower().find(string) > -1:
1899- results.append([file, filename])
1900+ for file_path in Settings().value(self.settings_section + '/media files'):
1901+ file_name = file_path.name
1902+ if file_name.lower().find(string) > -1:
1903+ results.append([str(file_path), file_name])
1904 return results
1905
1906 def on_load_optical(self):
1907@@ -446,13 +445,13 @@
1908
1909 :param optical: The clip to add.
1910 """
1911- full_list = self.get_file_list()
1912+ file_paths = self.get_file_list()
1913 # If the clip already is in the media list it isn't added and an error message is displayed.
1914- if optical in full_list:
1915+ if optical in file_paths:
1916 critical_error_message_box(translate('MediaPlugin.MediaItem', 'Mediaclip already saved'),
1917 translate('MediaPlugin.MediaItem', 'This mediaclip has already been saved'))
1918 return
1919 # Append the optical string to the media list
1920- full_list.append(optical)
1921+ file_paths.append(optical)
1922 self.load_list([optical])
1923- Settings().setValue(self.settings_section + '/media files', self.get_file_list())
1924+ Settings().setValue(self.settings_section + '/media files', file_paths)
1925
1926=== modified file 'openlp/plugins/media/mediaplugin.py'
1927--- openlp/plugins/media/mediaplugin.py 2017-08-23 20:13:58 +0000
1928+++ openlp/plugins/media/mediaplugin.py 2017-09-03 10:39:42 +0000
1929@@ -26,12 +26,11 @@
1930 import logging
1931 import os
1932 import re
1933-from pathlib import Path
1934-
1935 from PyQt5 import QtCore
1936
1937 from openlp.core.api.http import register_endpoint
1938 from openlp.core.common import AppLocation, translate, check_binary_exists
1939+from openlp.core.common.path import Path
1940 from openlp.core.lib import Plugin, StringContent, build_icon
1941 from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
1942 from openlp.plugins.media.lib import MediaMediaItem, MediaTab
1943
1944=== modified file 'openlp/plugins/presentations/lib/impresscontroller.py'
1945--- openlp/plugins/presentations/lib/impresscontroller.py 2017-08-12 17:45:56 +0000
1946+++ openlp/plugins/presentations/lib/impresscontroller.py 2017-09-03 10:39:42 +0000
1947@@ -34,9 +34,9 @@
1948 import logging
1949 import os
1950 import time
1951-from pathlib import Path
1952
1953-from openlp.core.common import is_win, Registry, get_uno_command, get_uno_instance, delete_file
1954+from openlp.core.common import is_win, Registry, delete_file
1955+from openlp.core.common.path import Path
1956
1957 if is_win():
1958 from win32com.client import Dispatch
1959
1960=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
1961--- openlp/plugins/presentations/lib/mediaitem.py 2017-03-03 19:27:31 +0000
1962+++ openlp/plugins/presentations/lib/mediaitem.py 2017-09-03 10:39:42 +0000
1963@@ -26,10 +26,11 @@
1964 from PyQt5 import QtCore, QtGui, QtWidgets
1965
1966 from openlp.core.common import Registry, Settings, UiStrings, translate
1967+from openlp.core.common.languagemanager import get_locale_key
1968+from openlp.core.common.path import path_to_str
1969 from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext,\
1970 build_icon, check_item_selected, create_thumb, validate_thumb
1971 from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
1972-from openlp.core.common.languagemanager import get_locale_key
1973 from openlp.plugins.presentations.lib import MessageListener
1974 from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
1975
1976@@ -126,8 +127,8 @@
1977 Populate the media manager tab
1978 """
1979 self.list_view.setIconSize(QtCore.QSize(88, 50))
1980- files = Settings().value(self.settings_section + '/presentations files')
1981- self.load_list(files, initial_load=True)
1982+ file_paths = Settings().value(self.settings_section + '/presentations files')
1983+ self.load_list([path_to_str(file) for file in file_paths], initial_load=True)
1984 self.populate_display_types()
1985
1986 def populate_display_types(self):
1987@@ -157,7 +158,7 @@
1988 existing files, and when the user adds new files via the media manager.
1989 """
1990 current_list = self.get_file_list()
1991- titles = [os.path.split(file)[1] for file in current_list]
1992+ titles = [file_path.name for file_path in current_list]
1993 self.application.set_busy_cursor()
1994 if not initial_load:
1995 self.main_window.display_progress_bar(len(files))
1996@@ -410,11 +411,11 @@
1997 :param show_error: not used
1998 :return:
1999 """
2000- files = Settings().value(self.settings_section + '/presentations files')
2001+ file_paths = Settings().value(self.settings_section + '/presentations files')
2002 results = []
2003 string = string.lower()
2004- for file in files:
2005- filename = os.path.split(str(file))[1]
2006- if filename.lower().find(string) > -1:
2007- results.append([file, filename])
2008+ for file_path in file_paths:
2009+ file_name = file_path.name
2010+ if file_name.lower().find(string) > -1:
2011+ results.append([path_to_str(file_path), file_name])
2012 return results
2013
2014=== modified file 'openlp/plugins/presentations/lib/pdfcontroller.py'
2015--- openlp/plugins/presentations/lib/pdfcontroller.py 2017-08-12 17:45:56 +0000
2016+++ openlp/plugins/presentations/lib/pdfcontroller.py 2017-09-03 10:39:42 +0000
2017@@ -23,12 +23,12 @@
2018 import os
2019 import logging
2020 import re
2021-from pathlib import Path
2022 from shutil import which
2023 from subprocess import check_output, CalledProcessError
2024
2025 from openlp.core.common import AppLocation, check_binary_exists
2026 from openlp.core.common import Settings, is_win
2027+from openlp.core.common.path import Path, path_to_str
2028 from openlp.core.lib import ScreenList
2029 from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
2030
2031@@ -113,7 +113,7 @@
2032 self.also_supports = []
2033 # Use the user defined program if given
2034 if Settings().value('presentations/enable_pdf_program'):
2035- pdf_program = Settings().value('presentations/pdf_program')
2036+ pdf_program = path_to_str(Settings().value('presentations/pdf_program'))
2037 program_type = self.process_check_binary(pdf_program)
2038 if program_type == 'gs':
2039 self.gsbin = pdf_program
2040
2041=== modified file 'openlp/plugins/presentations/lib/presentationcontroller.py'
2042--- openlp/plugins/presentations/lib/presentationcontroller.py 2017-08-12 17:45:56 +0000
2043+++ openlp/plugins/presentations/lib/presentationcontroller.py 2017-09-03 10:39:42 +0000
2044@@ -23,11 +23,11 @@
2045 import logging
2046 import os
2047 import shutil
2048-from pathlib import Path
2049
2050 from PyQt5 import QtCore
2051
2052 from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, md5_hash
2053+from openlp.core.common.path import Path
2054 from openlp.core.lib import create_thumb, validate_thumb
2055
2056 log = logging.getLogger(__name__)
2057
2058=== modified file 'openlp/plugins/presentations/lib/presentationtab.py'
2059--- openlp/plugins/presentations/lib/presentationtab.py 2017-08-04 21:53:02 +0000
2060+++ openlp/plugins/presentations/lib/presentationtab.py 2017-09-03 10:39:42 +0000
2061@@ -155,9 +155,7 @@
2062 enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program')
2063 self.pdf_program_check_box.setChecked(enable_pdf_program)
2064 self.program_path_edit.setEnabled(enable_pdf_program)
2065- pdf_program = Settings().value(self.settings_section + '/pdf_program')
2066- if pdf_program:
2067- self.program_path_edit.path = str_to_path(pdf_program)
2068+ self.program_path_edit.path = Settings().value(self.settings_section + '/pdf_program')
2069
2070 def save(self):
2071 """
2072@@ -193,13 +191,13 @@
2073 Settings().setValue(setting_key, self.ppt_window_check_box.checkState())
2074 changed = True
2075 # Save pdf-settings
2076- pdf_program = path_to_str(self.program_path_edit.path)
2077+ pdf_program_path = self.program_path_edit.path
2078 enable_pdf_program = self.pdf_program_check_box.checkState()
2079 # If the given program is blank disable using the program
2080- if pdf_program == '':
2081+ if not pdf_program_path:
2082 enable_pdf_program = 0
2083- if pdf_program != Settings().value(self.settings_section + '/pdf_program'):
2084- Settings().setValue(self.settings_section + '/pdf_program', pdf_program)
2085+ if pdf_program_path != Settings().value(self.settings_section + '/pdf_program'):
2086+ Settings().setValue(self.settings_section + '/pdf_program', pdf_program_path)
2087 changed = True
2088 if enable_pdf_program != Settings().value(self.settings_section + '/enable_pdf_program'):
2089 Settings().setValue(self.settings_section + '/enable_pdf_program', enable_pdf_program)
2090
2091=== modified file 'openlp/plugins/presentations/presentationplugin.py'
2092--- openlp/plugins/presentations/presentationplugin.py 2017-08-23 20:13:58 +0000
2093+++ openlp/plugins/presentations/presentationplugin.py 2017-09-03 10:39:42 +0000
2094@@ -39,7 +39,7 @@
2095
2096 __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked,
2097 'presentations/enable_pdf_program': QtCore.Qt.Unchecked,
2098- 'presentations/pdf_program': '',
2099+ 'presentations/pdf_program': None,
2100 'presentations/Impress': QtCore.Qt.Checked,
2101 'presentations/Powerpoint': QtCore.Qt.Checked,
2102 'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
2103
2104=== modified file 'openlp/plugins/songs/forms/editsongform.py'
2105--- openlp/plugins/songs/forms/editsongform.py 2017-08-23 20:13:58 +0000
2106+++ openlp/plugins/songs/forms/editsongform.py 2017-09-03 10:39:42 +0000
2107@@ -28,12 +28,11 @@
2108 import re
2109 import os
2110 import shutil
2111-from pathlib import Path
2112
2113 from PyQt5 import QtCore, QtWidgets
2114
2115 from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStrings, check_directory_exists, translate
2116-from openlp.core.common.path import path_to_str
2117+from openlp.core.common.path import Path, path_to_str
2118 from openlp.core.lib import PluginStatus, MediaType, create_separated_list
2119 from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
2120 from openlp.core.ui.lib.filedialog import FileDialog
2121
2122=== modified file 'openlp/plugins/songs/forms/songimportform.py'
2123--- openlp/plugins/songs/forms/songimportform.py 2017-08-07 20:50:01 +0000
2124+++ openlp/plugins/songs/forms/songimportform.py 2017-09-03 10:39:42 +0000
2125@@ -239,13 +239,11 @@
2126 filters += ';;'
2127 filters += '{text} (*)'.format(text=UiStrings().AllFiles)
2128 file_paths, selected_filter = FileDialog.getOpenFileNames(
2129- self, title,
2130- str_to_path(Settings().value(self.plugin.settings_section + '/last directory import')), filters)
2131+ self, title, Settings().value(self.plugin.settings_section + '/last directory import'), filters)
2132 if file_paths:
2133 file_names = [path_to_str(file_path) for file_path in file_paths]
2134 listbox.addItems(file_names)
2135- Settings().setValue(self.plugin.settings_section + '/last directory import',
2136- os.path.split(str(file_names[0]))[0])
2137+ Settings().setValue(self.plugin.settings_section + '/last directory import', file_paths[0].parent)
2138
2139 def get_list_of_files(self, list_box):
2140 """
2141@@ -363,14 +361,15 @@
2142 def on_error_save_to_button_clicked(self):
2143 """
2144 Save the error report to a file.
2145+
2146+ :rtype: None
2147 """
2148- filename, filter_used = QtWidgets.QFileDialog.getSaveFileName(
2149+ file_path, filter_used = FileDialog.getSaveFileName(
2150 self, Settings().value(self.plugin.settings_section + '/last directory import'))
2151- if not filename:
2152+ if not file_path:
2153 return
2154- report_file = codecs.open(filename, 'w', 'utf-8')
2155- report_file.write(self.error_report_text_edit.toPlainText())
2156- report_file.close()
2157+ with file_path.open('w', encoding='utf-8') as report_file:
2158+ report_file.write(self.error_report_text_edit.toPlainText())
2159
2160 def add_file_select_item(self):
2161 """
2162
2163=== modified file 'openlp/plugins/songs/lib/importers/songbeamer.py'
2164--- openlp/plugins/songs/lib/importers/songbeamer.py 2017-08-12 17:45:56 +0000
2165+++ openlp/plugins/songs/lib/importers/songbeamer.py 2017-09-03 10:39:42 +0000
2166@@ -27,11 +27,11 @@
2167 import re
2168 import base64
2169 import math
2170-from pathlib import Path
2171
2172+from openlp.core.common import Settings, is_win, is_macosx, get_file_encoding
2173+from openlp.core.common.path import Path
2174 from openlp.plugins.songs.lib import VerseType
2175 from openlp.plugins.songs.lib.importers.songimport import SongImport
2176-from openlp.core.common import Settings, is_win, is_macosx, get_file_encoding
2177
2178 log = logging.getLogger(__name__)
2179
2180
2181=== modified file 'openlp/plugins/songs/lib/importers/songimport.py'
2182--- openlp/plugins/songs/lib/importers/songimport.py 2017-08-12 17:45:56 +0000
2183+++ openlp/plugins/songs/lib/importers/songimport.py 2017-09-03 10:39:42 +0000
2184@@ -24,11 +24,11 @@
2185 import re
2186 import shutil
2187 import os
2188-from pathlib import Path
2189
2190 from PyQt5 import QtCore
2191
2192 from openlp.core.common import Registry, AppLocation, check_directory_exists, translate
2193+from openlp.core.common.path import Path
2194 from openlp.core.ui.lib.wizard import WizardStrings
2195 from openlp.plugins.songs.lib import clean_song, VerseType
2196 from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
2197
2198=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
2199--- openlp/plugins/songs/lib/mediaitem.py 2017-08-23 20:13:58 +0000
2200+++ openlp/plugins/songs/lib/mediaitem.py 2017-09-03 10:39:42 +0000
2201@@ -23,12 +23,12 @@
2202 import logging
2203 import os
2204 import shutil
2205-from pathlib import Path
2206
2207 from PyQt5 import QtCore, QtWidgets
2208 from sqlalchemy.sql import and_, or_
2209
2210 from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
2211+from openlp.core.common.path import Path
2212 from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
2213 check_item_selected, create_separated_list
2214 from openlp.core.lib.ui import create_widget_action
2215
2216=== modified file 'openlp/plugins/songs/lib/openlyricsexport.py'
2217--- openlp/plugins/songs/lib/openlyricsexport.py 2017-08-12 17:45:56 +0000
2218+++ openlp/plugins/songs/lib/openlyricsexport.py 2017-09-03 10:39:42 +0000
2219@@ -25,11 +25,11 @@
2220 """
2221 import logging
2222 import os
2223-from pathlib import Path
2224
2225 from lxml import etree
2226
2227 from openlp.core.common import RegistryProperties, check_directory_exists, translate, clean_filename
2228+from openlp.core.common.path import Path
2229 from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
2230
2231 log = logging.getLogger(__name__)
2232
2233=== modified file 'openlp/plugins/songs/songsplugin.py'
2234--- openlp/plugins/songs/songsplugin.py 2017-06-11 19:41:34 +0000
2235+++ openlp/plugins/songs/songsplugin.py 2017-09-03 10:39:42 +0000
2236@@ -66,8 +66,8 @@
2237 'songs/display songbook': False,
2238 'songs/display written by': True,
2239 'songs/display copyright symbol': False,
2240- 'songs/last directory import': '',
2241- 'songs/last directory export': '',
2242+ 'songs/last directory import': None,
2243+ 'songs/last directory export': None,
2244 'songs/songselect username': '',
2245 'songs/songselect password': '',
2246 'songs/songselect searches': '',
2247
2248=== modified file 'openlp/plugins/songusage/forms/songusagedetailform.py'
2249--- openlp/plugins/songusage/forms/songusagedetailform.py 2017-08-12 17:45:56 +0000
2250+++ openlp/plugins/songusage/forms/songusagedetailform.py 2017-09-03 10:39:42 +0000
2251@@ -22,13 +22,12 @@
2252
2253 import logging
2254 import os
2255-from pathlib import Path
2256
2257 from PyQt5 import QtCore, QtWidgets
2258 from sqlalchemy.sql import and_
2259
2260 from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate
2261-from openlp.core.common.path import path_to_str, str_to_path
2262+from openlp.core.common.path import Path, path_to_str, str_to_path
2263 from openlp.core.lib.ui import critical_error_message_box
2264 from openlp.plugins.songusage.lib.db import SongUsageItem
2265 from .songusagedetaildialog import Ui_SongUsageDetailDialog
2266@@ -57,14 +56,16 @@
2267 """
2268 self.from_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/from date'))
2269 self.to_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/to date'))
2270- self.report_path_edit.path = str_to_path(
2271- Settings().value(self.plugin.settings_section + '/last directory export'))
2272+ self.report_path_edit.path = Settings().value(self.plugin.settings_section + '/last directory export')
2273
2274 def on_report_path_edit_path_changed(self, file_path):
2275 """
2276- Triggered when the Directory selection button is clicked
2277+ Called when the path in the `PathEdit` has changed
2278+
2279+ :param openlp.core.common.path.Path file_path: The new path.
2280+ :rtype: None
2281 """
2282- Settings().setValue(self.plugin.settings_section + '/last directory export', path_to_str(file_path))
2283+ Settings().setValue(self.plugin.settings_section + '/last directory export', file_path)
2284
2285 def accept(self):
2286 """
2287
2288=== modified file 'openlp/plugins/songusage/songusageplugin.py'
2289--- openlp/plugins/songusage/songusageplugin.py 2016-12-31 11:01:36 +0000
2290+++ openlp/plugins/songusage/songusageplugin.py 2017-09-03 10:39:42 +0000
2291@@ -50,7 +50,7 @@
2292 'songusage/active': False,
2293 'songusage/to date': QtCore.QDate(YEAR, 8, 31),
2294 'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1),
2295- 'songusage/last directory export': ''
2296+ 'songusage/last directory export': None
2297 }
2298
2299
2300
2301=== modified file 'tests/functional/openlp_core_common/test_applocation.py'
2302--- tests/functional/openlp_core_common/test_applocation.py 2017-08-12 17:45:56 +0000
2303+++ tests/functional/openlp_core_common/test_applocation.py 2017-09-03 10:39:42 +0000
2304@@ -22,13 +22,12 @@
2305 """
2306 Functional tests to test the AppLocation class and related methods.
2307 """
2308-import copy
2309 import os
2310-from pathlib import Path
2311 from unittest import TestCase
2312 from unittest.mock import MagicMock, patch
2313
2314 from openlp.core.common import AppLocation, get_frozen_path
2315+from openlp.core.common.path import Path
2316
2317 FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3']
2318
2319@@ -43,12 +42,14 @@
2320 """
2321 with patch('openlp.core.common.applocation.Settings') as mocked_class, \
2322 patch('openlp.core.common.AppLocation.get_directory') as mocked_get_directory, \
2323- patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists:
2324+ patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \
2325+ patch('openlp.core.common.applocation.os') as mocked_os:
2326 # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory()
2327 mocked_settings = mocked_class.return_value
2328 mocked_settings.contains.return_value = False
2329- mocked_get_directory.return_value = Path('test', 'dir')
2330+ mocked_get_directory.return_value = os.path.join('test', 'dir')
2331 mocked_check_directory_exists.return_value = True
2332+ mocked_os.path.normpath.return_value = os.path.join('test', 'dir')
2333
2334 # WHEN: we call AppLocation.get_data_path()
2335 data_path = AppLocation.get_data_path()
2336@@ -56,8 +57,8 @@
2337 # THEN: check that all the correct methods were called, and the result is correct
2338 mocked_settings.contains.assert_called_with('advanced/data path')
2339 mocked_get_directory.assert_called_with(AppLocation.DataDir)
2340- mocked_check_directory_exists.assert_called_with(Path('test', 'dir'))
2341- self.assertEqual(Path('test', 'dir'), data_path, 'Result should be "test/dir"')
2342+ mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir'))
2343+ self.assertEqual(os.path.join('test', 'dir'), data_path, 'Result should be "test/dir"')
2344
2345 def test_get_data_path_with_custom_location(self):
2346 """
2347
2348=== modified file 'tests/functional/openlp_core_common/test_common.py'
2349--- tests/functional/openlp_core_common/test_common.py 2017-08-12 17:45:56 +0000
2350+++ tests/functional/openlp_core_common/test_common.py 2017-09-03 10:39:42 +0000
2351@@ -22,13 +22,12 @@
2352 """
2353 Functional tests to test the AppLocation class and related methods.
2354 """
2355-from pathlib import Path
2356 from unittest import TestCase
2357 from unittest.mock import MagicMock, call, patch
2358
2359-from openlp.core import common
2360 from openlp.core.common import check_directory_exists, clean_button_text, de_hump, extension_loader, is_macosx, \
2361 is_linux, is_win, path_to_module, trace_error_handler, translate
2362+from openlp.core.common.path import Path
2363
2364
2365 class TestCommonFunctions(TestCase):
2366
2367=== modified file 'tests/functional/openlp_core_common/test_init.py'
2368--- tests/functional/openlp_core_common/test_init.py 2017-08-12 17:45:56 +0000
2369+++ tests/functional/openlp_core_common/test_init.py 2017-09-03 10:39:42 +0000
2370@@ -24,12 +24,12 @@
2371 """
2372 import os
2373 from io import BytesIO
2374-from pathlib import Path
2375 from unittest import TestCase
2376 from unittest.mock import MagicMock, PropertyMock, call, patch
2377
2378 from openlp.core.common import add_actions, clean_filename, delete_file, get_file_encoding, get_filesystem_encoding, \
2379 get_uno_command, get_uno_instance, split_filename
2380+from openlp.core.common.path import Path
2381
2382 from tests.helpers.testmixin import TestMixin
2383
2384
2385=== added file 'tests/functional/openlp_core_common/test_json.py'
2386--- tests/functional/openlp_core_common/test_json.py 1970-01-01 00:00:00 +0000
2387+++ tests/functional/openlp_core_common/test_json.py 2017-09-03 10:39:42 +0000
2388@@ -0,0 +1,122 @@
2389+# -*- coding: utf-8 -*-
2390+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
2391+
2392+###############################################################################
2393+# OpenLP - Open Source Lyrics Projection #
2394+# --------------------------------------------------------------------------- #
2395+# Copyright (c) 2008-2017 OpenLP Developers #
2396+# --------------------------------------------------------------------------- #
2397+# This program is free software; you can redistribute it and/or modify it #
2398+# under the terms of the GNU General Public License as published by the Free #
2399+# Software Foundation; version 2 of the License. #
2400+# #
2401+# This program is distributed in the hope that it will be useful, but WITHOUT #
2402+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
2403+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
2404+# more details. #
2405+# #
2406+# You should have received a copy of the GNU General Public License along #
2407+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
2408+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
2409+###############################################################################
2410+"""
2411+Package to test the openlp.core.common.json package.
2412+"""
2413+import json
2414+from unittest import TestCase
2415+from unittest.mock import patch
2416+
2417+from openlp.core.common.path import Path
2418+from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
2419+
2420+
2421+class TestOpenLPJsonDecoder(TestCase):
2422+ """
2423+ Test the OpenLPJsonDecoder class
2424+ """
2425+ def test_object_hook_path_object(self):
2426+ """
2427+ Test the object_hook method when called with a decoded Path JSON object
2428+ """
2429+ # GIVEN: An instance of OpenLPJsonDecoder
2430+ instance = OpenLPJsonDecoder()
2431+
2432+ # WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
2433+ result = instance.object_hook({'__Path__': ['test', 'path']})
2434+
2435+ # THEN: A Path object should be returned
2436+ self.assertEqual(result, Path('test', 'path'))
2437+
2438+ def test_object_hook_non_path_object(self):
2439+ """
2440+ Test the object_hook method when called with a decoded JSON object
2441+ """
2442+ # GIVEN: An instance of OpenLPJsonDecoder
2443+ instance = OpenLPJsonDecoder()
2444+
2445+ # WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
2446+ with patch('openlp.core.common.json.Path') as mocked_path:
2447+ result = instance.object_hook({'key': 'value'})
2448+
2449+ # THEN: The object should be returned unchanged and a Path object should not have been initiated
2450+ self.assertEqual(result, {'key': 'value'})
2451+ self.assertFalse(mocked_path.called)
2452+
2453+ def test_json_decode(self):
2454+ """
2455+ Test the OpenLPJsonDecoder when decoding a JSON string
2456+ """
2457+ # GIVEN: A JSON encoded string
2458+ json_string = '[{"__Path__": ["test", "path1"]}, {"__Path__": ["test", "path2"]}]'
2459+
2460+ # WHEN: Decoding the string using the OpenLPJsonDecoder class
2461+ obj = json.loads(json_string, cls=OpenLPJsonDecoder)
2462+
2463+ # THEN: The object returned should be a python version of the JSON string
2464+ self.assertEqual(obj, [Path('test', 'path1'), Path('test', 'path2')])
2465+
2466+
2467+class TestOpenLPJsonEncoder(TestCase):
2468+ """
2469+ Test the OpenLPJsonEncoder class
2470+ """
2471+ def test_default_path_object(self):
2472+ """
2473+ Test the default method when called with a Path object
2474+ """
2475+ # GIVEN: An instance of OpenLPJsonEncoder
2476+ instance = OpenLPJsonEncoder()
2477+
2478+ # WHEN: Calling the default method with a Path object
2479+ result = instance.default(Path('test', 'path'))
2480+
2481+ # THEN: A dictionary object that can be JSON encoded should be returned
2482+ self.assertEqual(result, {'__Path__': ('test', 'path')})
2483+
2484+ def test_default_non_path_object(self):
2485+ """
2486+ Test the default method when called with a object other than a Path object
2487+ """
2488+ with patch('openlp.core.common.json.JSONEncoder.default') as mocked_super_default:
2489+
2490+ # GIVEN: An instance of OpenLPJsonEncoder
2491+ instance = OpenLPJsonEncoder()
2492+
2493+ # WHEN: Calling the default method with a object other than a Path object
2494+ instance.default('invalid object')
2495+
2496+ # THEN: default method of the super class should have been called
2497+ mocked_super_default.assert_called_once_with('invalid object')
2498+
2499+ def test_json_encode(self):
2500+ """
2501+ Test the OpenLPJsonDEncoder when encoding an object conatining Path objects
2502+ """
2503+ # GIVEN: A list of Path objects
2504+ obj = [Path('test', 'path1'), Path('test', 'path2')]
2505+
2506+ # WHEN: Encoding the object using the OpenLPJsonEncoder class
2507+ json_string = json.dumps(obj, cls=OpenLPJsonEncoder)
2508+
2509+ # THEN: The JSON string return should be a representation of the object encoded
2510+ self.assertEqual(json_string, '[{"__Path__": ["test", "path1"]}, {"__Path__": ["test", "path2"]}]')
2511
2512=== modified file 'tests/functional/openlp_core_common/test_path.py'
2513--- tests/functional/openlp_core_common/test_path.py 2017-08-04 18:06:43 +0000
2514+++ tests/functional/openlp_core_common/test_path.py 2017-09-03 10:39:42 +0000
2515@@ -23,10 +23,9 @@
2516 Package to test the openlp.core.common.path package.
2517 """
2518 import os
2519-from pathlib import Path
2520 from unittest import TestCase
2521
2522-from openlp.core.common.path import path_to_str, str_to_path
2523+from openlp.core.common.path import Path, path_to_str, str_to_path
2524
2525
2526 class TestPath(TestCase):
2527@@ -86,3 +85,54 @@
2528
2529 # THEN: `path_to_str` should return None
2530 self.assertEqual(result, None)
2531+
2532+ def test_path_encode_json(self):
2533+ """
2534+ Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
2535+ from JSON
2536+ """
2537+ # GIVEN: A Path object from openlp.core.common.path
2538+ # WHEN: Calling encode_json, with a dictionary representation
2539+ path = Path.encode_json({'__Path__': ['path', 'to', 'fi.le']}, extra=1, args=2)
2540+
2541+ # THEN: A Path object should have been returned
2542+ self.assertEqual(path, Path('path', 'to', 'fi.le'))
2543+
2544+ def test_path_encode_json_base_path(self):
2545+ """
2546+ Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
2547+ from JSON when the base_path arg is supplied.
2548+ """
2549+ # GIVEN: A Path object from openlp.core.common.path
2550+ # WHEN: Calling encode_json, with a dictionary representation
2551+ path = Path.encode_json({'__Path__': ['path', 'to', 'fi.le']}, base_path=Path('/base'))
2552+
2553+ # THEN: A Path object should have been returned with an absolute path
2554+ self.assertEqual(path, Path('/', 'base', 'path', 'to', 'fi.le'))
2555+
2556+ def test_path_json_object(self):
2557+ """
2558+ Test that `Path.json_object` creates a JSON decode-able object from a Path object
2559+ """
2560+ # GIVEN: A Path object from openlp.core.common.path
2561+ path = Path('/base', 'path', 'to', 'fi.le')
2562+
2563+ # WHEN: Calling json_object
2564+ obj = path.json_object(extra=1, args=2)
2565+
2566+ # THEN: A JSON decodable object should have been returned.
2567+ self.assertEqual(obj, {'__Path__': ('/', 'base', 'path', 'to', 'fi.le')})
2568+
2569+ def test_path_json_object_base_path(self):
2570+ """
2571+ Test that `Path.json_object` creates a JSON decode-able object from a Path object, that is relative to the
2572+ base_path
2573+ """
2574+ # GIVEN: A Path object from openlp.core.common.path
2575+ path = Path('/base', 'path', 'to', 'fi.le')
2576+
2577+ # WHEN: Calling json_object with a base_path
2578+ obj = path.json_object(base_path=Path('/', 'base'))
2579+
2580+ # THEN: A JSON decodable object should have been returned.
2581+ self.assertEqual(obj, {'__Path__': ('path', 'to', 'fi.le')})
2582
2583=== modified file 'tests/functional/openlp_core_common/test_settings.py'
2584--- tests/functional/openlp_core_common/test_settings.py 2017-04-24 05:17:55 +0000
2585+++ tests/functional/openlp_core_common/test_settings.py 2017-09-03 10:39:42 +0000
2586@@ -26,7 +26,6 @@
2587 from unittest.mock import patch
2588
2589 from openlp.core.common import Settings
2590-from openlp.core.common.settings import recent_files_conv
2591
2592 from tests.helpers.testmixin import TestMixin
2593
2594@@ -48,25 +47,6 @@
2595 """
2596 self.destroy_settings()
2597
2598- def test_recent_files_conv(self):
2599- """
2600- Test that recent_files_conv, converts various possible types of values correctly.
2601- """
2602- # GIVEN: A list of possible value types and the expected results
2603- possible_values = [(['multiple', 'values'], ['multiple', 'values']),
2604- (['single value'], ['single value']),
2605- ('string value', ['string value']),
2606- (b'bytes value', ['bytes value']),
2607- ([], []),
2608- (None, [])]
2609-
2610- # WHEN: Calling recent_files_conv with the possible values
2611- for value, expected_result in possible_values:
2612- actual_result = recent_files_conv(value)
2613-
2614- # THEN: The actual result should be the same as the expected result
2615- self.assertEqual(actual_result, expected_result)
2616-
2617 def test_settings_basic(self):
2618 """
2619 Test the Settings creation and its default usage
2620
2621=== modified file 'tests/functional/openlp_core_lib/test_db.py'
2622--- tests/functional/openlp_core_lib/test_db.py 2017-08-12 17:45:56 +0000
2623+++ tests/functional/openlp_core_lib/test_db.py 2017-09-03 10:39:42 +0000
2624@@ -22,9 +22,7 @@
2625 """
2626 Package to test the openlp.core.lib package.
2627 """
2628-import os
2629 import shutil
2630-from pathlib import Path
2631
2632 from tempfile import mkdtemp
2633 from unittest import TestCase
2634@@ -34,6 +32,7 @@
2635 from sqlalchemy.orm.scoping import ScopedSession
2636 from sqlalchemy import MetaData
2637
2638+from openlp.core.common.path import Path
2639 from openlp.core.lib.db import init_db, get_upgrade_op, delete_database, upgrade_db
2640 from openlp.core.lib.projector import upgrade as pjlink_upgrade
2641
2642
2643=== modified file 'tests/functional/openlp_core_lib/test_lib.py'
2644--- tests/functional/openlp_core_lib/test_lib.py 2017-08-25 04:26:25 +0000
2645+++ tests/functional/openlp_core_lib/test_lib.py 2017-09-03 10:39:42 +0000
2646@@ -24,12 +24,12 @@
2647 """
2648 import os
2649 from datetime import datetime, timedelta
2650-from pathlib import Path
2651 from unittest import TestCase
2652 from unittest.mock import MagicMock, patch
2653
2654 from PyQt5 import QtCore, QtGui
2655
2656+from openlp.core.common.path import Path
2657 from openlp.core.lib import FormattingTags, build_icon, check_item_selected, clean_tags, compare_chord_lyric, \
2658 create_separated_list, create_thumb, expand_chords, expand_chords_for_printing, expand_tags, find_formatting_tags, \
2659 get_text_file_string, image_to_byte, replace_params, resize_image, str_to_bool, validate_thumb
2660
2661=== modified file 'tests/functional/openlp_core_lib/test_path.py'
2662--- tests/functional/openlp_core_lib/test_path.py 2017-08-02 06:09:38 +0000
2663+++ tests/functional/openlp_core_lib/test_path.py 2017-09-03 10:39:42 +0000
2664@@ -23,10 +23,9 @@
2665 Package to test the openlp.core.lib.path package.
2666 """
2667 import os
2668-from pathlib import Path
2669 from unittest import TestCase
2670
2671-from openlp.core.lib.path import path_to_str, str_to_path
2672+from openlp.core.common.path import Path, path_to_str, str_to_path
2673
2674
2675 class TestPath(TestCase):
2676
2677=== modified file 'tests/functional/openlp_core_ui/test_exceptionform.py'
2678--- tests/functional/openlp_core_ui/test_exceptionform.py 2017-04-24 05:17:55 +0000
2679+++ tests/functional/openlp_core_ui/test_exceptionform.py 2017-09-03 10:39:42 +0000
2680@@ -29,6 +29,7 @@
2681 from unittest.mock import mock_open, patch
2682
2683 from openlp.core.common import Registry
2684+from openlp.core.common.path import Path
2685 from openlp.core.ui import exceptionform
2686
2687 from tests.helpers.testmixin import TestMixin
2688@@ -154,7 +155,7 @@
2689 # THEN: Verify strings were formatted properly
2690 mocked_add_query_item.assert_called_with('body', MAIL_ITEM_TEXT)
2691
2692- @patch("openlp.core.ui.exceptionform.QtWidgets.QFileDialog.getSaveFileName")
2693+ @patch("openlp.core.ui.exceptionform.FileDialog.getSaveFileName")
2694 @patch("openlp.core.ui.exceptionform.Qt")
2695 def test_on_save_report_button_clicked(self,
2696 mocked_qt,
2697@@ -181,7 +182,7 @@
2698 mocked_qt.PYQT_VERSION_STR = 'PyQt5 Test'
2699 mocked_is_linux.return_value = False
2700 mocked_application_version.return_value = 'Trunk Test'
2701- mocked_save_filename.return_value = ['testfile.txt', ]
2702+ mocked_save_filename.return_value = (Path('testfile.txt'), 'filter')
2703
2704 test_form = exceptionform.ExceptionForm()
2705 test_form.file_attachment = None
2706
2707=== modified file 'tests/functional/openlp_core_ui/test_firsttimeform.py'
2708--- tests/functional/openlp_core_ui/test_firsttimeform.py 2017-08-12 17:45:56 +0000
2709+++ tests/functional/openlp_core_ui/test_firsttimeform.py 2017-09-03 10:39:42 +0000
2710@@ -25,11 +25,11 @@
2711 import os
2712 import tempfile
2713 import urllib
2714-from pathlib import Path
2715 from unittest import TestCase
2716 from unittest.mock import MagicMock, patch
2717
2718 from openlp.core.common import Registry
2719+from openlp.core.common.path import Path
2720 from openlp.core.ui.firsttimeform import FirstTimeForm
2721
2722 from tests.helpers.testmixin import TestMixin
2723
2724=== modified file 'tests/functional/openlp_core_ui/test_themeform.py'
2725--- tests/functional/openlp_core_ui/test_themeform.py 2017-08-04 17:40:57 +0000
2726+++ tests/functional/openlp_core_ui/test_themeform.py 2017-09-03 10:39:42 +0000
2727@@ -22,10 +22,10 @@
2728 """
2729 Package to test the openlp.core.ui.themeform package.
2730 """
2731-from pathlib import Path
2732 from unittest import TestCase
2733 from unittest.mock import MagicMock, patch
2734
2735+from openlp.core.common.path import Path
2736 from openlp.core.ui import ThemeForm
2737
2738
2739
2740=== modified file 'tests/functional/openlp_core_ui_lib/test_filedialog.py'
2741--- tests/functional/openlp_core_ui_lib/test_filedialog.py 2017-08-07 21:01:16 +0000
2742+++ tests/functional/openlp_core_ui_lib/test_filedialog.py 2017-09-03 10:39:42 +0000
2743@@ -1,10 +1,10 @@
2744 import os
2745 from unittest import TestCase
2746 from unittest.mock import patch
2747-from pathlib import Path
2748
2749 from PyQt5 import QtWidgets
2750
2751+from openlp.core.common.path import Path
2752 from openlp.core.ui.lib.filedialog import FileDialog
2753
2754
2755
2756=== modified file 'tests/functional/openlp_core_ui_lib/test_pathedit.py'
2757--- tests/functional/openlp_core_ui_lib/test_pathedit.py 2017-08-07 20:50:01 +0000
2758+++ tests/functional/openlp_core_ui_lib/test_pathedit.py 2017-09-03 10:39:42 +0000
2759@@ -23,10 +23,10 @@
2760 This module contains tests for the openlp.core.ui.lib.pathedit module
2761 """
2762 import os
2763-from pathlib import Path
2764 from unittest import TestCase
2765 from unittest.mock import MagicMock, PropertyMock, patch
2766
2767+from openlp.core.common.path import Path
2768 from openlp.core.ui.lib import PathEdit, PathType
2769 from openlp.core.ui.lib.filedialog import FileDialog
2770
2771
2772=== modified file 'tests/functional/openlp_plugins/bibles/test_manager.py'
2773--- tests/functional/openlp_plugins/bibles/test_manager.py 2017-08-12 17:45:56 +0000
2774+++ tests/functional/openlp_plugins/bibles/test_manager.py 2017-09-03 10:39:42 +0000
2775@@ -22,10 +22,10 @@
2776 """
2777 This module contains tests for the manager submodule of the Bibles plugin.
2778 """
2779-from pathlib import Path
2780 from unittest import TestCase
2781 from unittest.mock import MagicMock, patch
2782
2783+from openlp.core.common.path import Path
2784 from openlp.plugins.bibles.lib.manager import BibleManager
2785
2786
2787
2788=== modified file 'tests/functional/openlp_plugins/images/test_lib.py'
2789--- tests/functional/openlp_plugins/images/test_lib.py 2017-05-08 19:04:14 +0000
2790+++ tests/functional/openlp_plugins/images/test_lib.py 2017-09-03 10:39:42 +0000
2791@@ -28,6 +28,7 @@
2792 from PyQt5 import QtCore, QtWidgets
2793
2794 from openlp.core.common import Registry
2795+from openlp.core.common.path import Path
2796 from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
2797 from openlp.plugins.images.lib.mediaitem import ImageMediaItem
2798
2799@@ -65,7 +66,7 @@
2800 # THEN: load_list should have been called with the file list and None,
2801 # the directory should have been saved to the settings
2802 mocked_load_list.assert_called_once_with(file_list, None)
2803- mocked_settings().setValue.assert_called_once_with(ANY, '/path1')
2804+ mocked_settings().setValue.assert_called_once_with(ANY, Path('/', 'path1'))
2805
2806 @patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_list')
2807 @patch('openlp.plugins.images.lib.mediaitem.Settings')
2808@@ -82,7 +83,7 @@
2809 # THEN: load_list should have been called with the file list and the group name,
2810 # the directory should have been saved to the settings
2811 mocked_load_list.assert_called_once_with(file_list, 'group')
2812- mocked_settings().setValue.assert_called_once_with(ANY, '/path1')
2813+ mocked_settings().setValue.assert_called_once_with(ANY, Path('/', 'path1'))
2814
2815 @patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list')
2816 def test_save_new_images_list_empty_list(self, mocked_load_full_list):
2817
2818=== modified file 'tests/functional/openlp_plugins/media/test_mediaitem.py'
2819--- tests/functional/openlp_plugins/media/test_mediaitem.py 2017-04-24 05:17:55 +0000
2820+++ tests/functional/openlp_plugins/media/test_mediaitem.py 2017-09-03 10:39:42 +0000
2821@@ -28,6 +28,7 @@
2822 from PyQt5 import QtCore
2823
2824 from openlp.core import Settings
2825+from openlp.core.common.path import Path
2826 from openlp.plugins.media.lib.mediaitem import MediaMediaItem
2827
2828 from tests.helpers.testmixin import TestMixin
2829@@ -66,7 +67,7 @@
2830 Media Remote Search Successful find
2831 """
2832 # GIVEN: The Mediaitem set up a list of media
2833- Settings().setValue(self.media_item.settings_section + '/media files', ['test.mp3', 'test.mp4'])
2834+ Settings().setValue(self.media_item.settings_section + '/media files', [Path('test.mp3'), Path('test.mp4')])
2835 # WHEN: Retrieving the test file
2836 result = self.media_item.search('test.mp4', False)
2837 # THEN: a file should be found
2838@@ -77,7 +78,7 @@
2839 Media Remote Search not find
2840 """
2841 # GIVEN: The Mediaitem set up a list of media
2842- Settings().setValue(self.media_item.settings_section + '/media files', ['test.mp3', 'test.mp4'])
2843+ Settings().setValue(self.media_item.settings_section + '/media files', [Path('test.mp3'), Path('test.mp4')])
2844 # WHEN: Retrieving the test file
2845 result = self.media_item.search('test.mpx', False)
2846 # THEN: a file should be found
2847
2848=== modified file 'tests/functional/openlp_plugins/presentations/test_presentationcontroller.py'
2849--- tests/functional/openlp_plugins/presentations/test_presentationcontroller.py 2017-08-12 17:45:56 +0000
2850+++ tests/functional/openlp_plugins/presentations/test_presentationcontroller.py 2017-09-03 10:39:42 +0000
2851@@ -24,10 +24,10 @@
2852 classes and related methods.
2853 """
2854 import os
2855-from pathlib import Path
2856 from unittest import TestCase
2857 from unittest.mock import MagicMock, mock_open, patch
2858
2859+from openlp.core.common.path import Path
2860 from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
2861
2862 FOLDER_TO_PATCH = 'openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder'
2863
2864=== modified file 'tests/interfaces/openlp_core_common/test_utils.py'
2865--- tests/interfaces/openlp_core_common/test_utils.py 2017-08-12 17:45:56 +0000
2866+++ tests/interfaces/openlp_core_common/test_utils.py 2017-09-03 10:39:42 +0000
2867@@ -22,10 +22,10 @@
2868 """
2869 Functional tests to test the AppLocation class and related methods.
2870 """
2871-from pathlib import Path
2872 from unittest import TestCase
2873
2874 from openlp.core.common import is_not_image_file
2875+from openlp.core.common.path import Path
2876 from tests.utils.constants import TEST_RESOURCES_PATH
2877 from tests.helpers.testmixin import TestMixin
2878
2879
2880=== modified file 'tests/interfaces/openlp_core_lib/test_pluginmanager.py'
2881--- tests/interfaces/openlp_core_lib/test_pluginmanager.py 2017-04-24 05:17:55 +0000
2882+++ tests/interfaces/openlp_core_lib/test_pluginmanager.py 2017-09-03 10:39:42 +0000
2883@@ -32,6 +32,7 @@
2884 from PyQt5 import QtWidgets
2885
2886 from openlp.core.common import Registry, Settings
2887+from openlp.core.common.path import Path
2888 from openlp.core.lib.pluginmanager import PluginManager
2889
2890 from tests.helpers.testmixin import TestMixin
2891@@ -48,7 +49,7 @@
2892 """
2893 self.setup_application()
2894 self.build_settings()
2895- self.temp_dir = mkdtemp('openlp')
2896+ self.temp_dir = Path(mkdtemp('openlp'))
2897 Settings().setValue('advanced/data path', self.temp_dir)
2898 Registry.create()
2899 Registry().register('service_list', MagicMock())
2900@@ -62,7 +63,7 @@
2901 # On windows we need to manually garbage collect to close sqlalchemy files
2902 # to avoid errors when temporary files are deleted.
2903 gc.collect()
2904- shutil.rmtree(self.temp_dir)
2905+ shutil.rmtree(str(self.temp_dir))
2906
2907 @patch('openlp.plugins.songusage.lib.db.init_schema')
2908 @patch('openlp.plugins.songs.lib.db.init_schema')