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...

bzr-revno: 2764
This commit is contained in:
Phill 2017-09-04 21:28:50 -07:00 committed by Raoul Snyman
commit 28591ed04a
66 changed files with 718 additions and 505 deletions

View File

@ -33,13 +33,14 @@ import os
import shutil import shutil
import sys import sys
import time import time
from pathlib import Path from datetime import datetime
from traceback import format_exception from traceback import format_exception
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, OpenLPMixin, AppLocation, LanguageManager, Settings, UiStrings, \ from openlp.core.common import Registry, OpenLPMixin, AppLocation, LanguageManager, Settings, UiStrings, \
check_directory_exists, is_macosx, is_win, translate check_directory_exists, is_macosx, is_win, translate
from openlp.core.common.path import Path
from openlp.core.common.versionchecker import VersionThread, get_application_version from openlp.core.common.versionchecker import VersionThread, get_application_version
from openlp.core.lib import ScreenList from openlp.core.lib import ScreenList
from openlp.core.resources import qInitResources from openlp.core.resources import qInitResources
@ -347,8 +348,7 @@ def set_up_logging(log_path):
""" """
Setup our logging using log_path Setup our logging using log_path
:param pathlib.Path log_path: The file to save the log to :param openlp.core.common.path.Path log_path: The file to save the log to.
:return: None
:rtype: None :rtype: None
""" """
check_directory_exists(log_path, True) check_directory_exists(log_path, True)
@ -406,7 +406,7 @@ def main(args=None):
# Set our data path # Set our data path
log.info('Data path: {name}'.format(name=data_path)) log.info('Data path: {name}'.format(name=data_path))
# Point to our data path # Point to our data path
portable_settings.setValue('advanced/data path', str(data_path)) portable_settings.setValue('advanced/data path', data_path)
portable_settings.setValue('advanced/is portable', True) portable_settings.setValue('advanced/is portable', True)
portable_settings.sync() portable_settings.sync()
else: else:
@ -423,8 +423,21 @@ def main(args=None):
if application.is_data_path_missing(): if application.is_data_path_missing():
application.shared_memory.detach() application.shared_memory.detach()
sys.exit() sys.exit()
# Remove/convert obsolete settings. # Upgrade settings.
Settings().remove_obsolete_settings() settings = Settings()
if settings.can_upgrade():
now = datetime.now()
# Only back up if OpenLP has previously run.
if settings.value('core/has run wizard'):
back_up_path = AppLocation.get_data_path() / (now.strftime('%Y-%m-%d %H-%M') + '.conf')
log.info('Settings about to be upgraded. Existing settings are being backed up to {back_up_path}'
.format(back_up_path=back_up_path))
QtWidgets.QMessageBox.information(
None, translate('OpenLP', 'Settings Upgrade'),
translate('OpenLP', 'Your settings are about to upgraded. A backup will be created at {back_up_path}')
.format(back_up_path=back_up_path))
settings.export(back_up_path)
settings.upgrade_settings()
# First time checks in settings # First time checks in settings
if not Settings().value('core/has run wizard'): if not Settings().value('core/has run wizard'):
if not FirstTimeLanguageForm().exec(): if not FirstTimeLanguageForm().exec():

View File

@ -79,8 +79,7 @@ def controller_text(request):
item['title'] = str(frame['display_title']) item['title'] = str(frame['display_title'])
if current_item.is_capable(ItemCapabilities.HasNotes): if current_item.is_capable(ItemCapabilities.HasNotes):
item['slide_notes'] = str(frame['notes']) item['slide_notes'] = str(frame['notes'])
if current_item.is_capable(ItemCapabilities.HasThumbnails) and \ if current_item.is_capable(ItemCapabilities.HasThumbnails) and Settings().value('api/thumbnails'):
Settings().value('api/thumbnails'):
# If the file is under our app directory tree send the portion after the match # If the file is under our app directory tree send the portion after the match
data_path = str(AppLocation.get_data_path()) data_path = str(AppLocation.get_data_path())
if frame['image'][0:len(data_path)] == data_path: if frame['image'][0:len(data_path)] == data_path:

View File

@ -66,9 +66,8 @@ def check_directory_exists(directory, do_not_log=False):
""" """
Check a directory exists and if not create it Check a directory exists and if not create it
:param pathlib.Path directory: The directory to make sure exists :param openlp.core.common.path.Path directory: The directory to make sure exists
:param bool do_not_log: To not log anything. This is need for the start up, when the log isn't ready. :param bool do_not_log: To not log anything. This is need for the start up, when the log isn't ready.
:return: None
:rtype: None :rtype: None
""" """
if not do_not_log: if not do_not_log:
@ -89,7 +88,6 @@ def extension_loader(glob_pattern, excluded_files=[]):
:param str glob_pattern: A glob pattern used to find the extension(s) to be imported. Should be relative to the :param str glob_pattern: A glob pattern used to find the extension(s) to be imported. Should be relative to the
application directory. i.e. plugins/*/*plugin.py application directory. i.e. plugins/*/*plugin.py
:param list[str] excluded_files: A list of file names to exclude that the glob pattern may find. :param list[str] excluded_files: A list of file names to exclude that the glob pattern may find.
:return: None
:rtype: None :rtype: None
""" """
app_dir = AppLocation.get_directory(AppLocation.AppDir) app_dir = AppLocation.get_directory(AppLocation.AppDir)
@ -110,7 +108,7 @@ def path_to_module(path):
""" """
Convert a path to a module name (i.e openlp.core.common) Convert a path to a module name (i.e openlp.core.common)
:param pathlib.Path path: The path to convert to a module name. :param openlp.core.common.path.Path path: The path to convert to a module name.
:return: The module name. :return: The module name.
:rtype: str :rtype: str
""" """
@ -377,7 +375,7 @@ def delete_file(file_path):
""" """
Deletes a file from the system. Deletes a file from the system.
:param pathlib.Path file_path: The file, including path, to delete. :param openlp.core.common.path.Path file_path: The file, including path, to delete.
:return: True if the deletion was successful, or the file never existed. False otherwise. :return: True if the deletion was successful, or the file never existed. False otherwise.
:rtype: bool :rtype: bool
""" """
@ -412,7 +410,7 @@ def is_not_image_file(file_path):
""" """
Validate that the file is not an image file. Validate that the file is not an image file.
:param pathlib.Path file_path: The file to be checked. :param openlp.core.common.path.Path file_path: The file to be checked.
:return: If the file is not an image :return: If the file is not an image
:rtype: bool :rtype: bool
""" """
@ -440,7 +438,7 @@ def check_binary_exists(program_path):
""" """
Function that checks whether a binary exists. Function that checks whether a binary exists.
:param pathlib.Path program_path: The full path to the binary to check. :param openlp.core.common.path.Path program_path: The full path to the binary to check.
:return: program output to be parsed :return: program output to be parsed
:rtype: bytes :rtype: bytes
""" """
@ -466,7 +464,7 @@ def get_file_encoding(file_path):
""" """
Utility function to incrementally detect the file encoding. Utility function to incrementally detect the file encoding.
:param pathlib.Path file_path: Filename for the file to determine the encoding for. :param openlp.core.common.path.Path file_path: Filename for the file to determine the encoding for.
:return: A dict with the keys 'encoding' and 'confidence' :return: A dict with the keys 'encoding' and 'confidence'
:rtype: dict[str, float] :rtype: dict[str, float]
""" """

View File

@ -25,9 +25,9 @@ The :mod:`openlp.core.common.applocation` module provides an utility for OpenLP
import logging import logging
import os import os
import sys import sys
from pathlib import Path
from openlp.core.common import Settings, is_win, is_macosx from openlp.core.common import Settings, is_win, is_macosx
from openlp.core.common.path import Path
if not is_win() and not is_macosx(): if not is_win() and not is_macosx():
@ -64,10 +64,8 @@ class AppLocation(object):
Return the appropriate directory according to the directory type. Return the appropriate directory according to the directory type.
:param dir_type: The directory type you want, for instance the data directory. Default *AppLocation.AppDir* :param dir_type: The directory type you want, for instance the data directory. Default *AppLocation.AppDir*
:type dir_type: AppLocation Enum
:return: The requested path :return: The requested path
:rtype: pathlib.Path :rtype: openlp.core.common.path.Path
""" """
if dir_type == AppLocation.AppDir or dir_type == AppLocation.VersionDir: if dir_type == AppLocation.AppDir or dir_type == AppLocation.VersionDir:
return get_frozen_path(FROZEN_APP_PATH, APP_PATH) return get_frozen_path(FROZEN_APP_PATH, APP_PATH)
@ -84,11 +82,11 @@ class AppLocation(object):
Return the path OpenLP stores all its data under. Return the path OpenLP stores all its data under.
:return: The data path to use. :return: The data path to use.
:rtype: pathlib.Path :rtype: openlp.core.common.path.Path
""" """
# Check if we have a different data location. # Check if we have a different data location.
if Settings().contains('advanced/data path'): if Settings().contains('advanced/data path'):
path = Path(Settings().value('advanced/data path')) path = Settings().value('advanced/data path')
else: else:
path = AppLocation.get_directory(AppLocation.DataDir) path = AppLocation.get_directory(AppLocation.DataDir)
check_directory_exists(path) check_directory_exists(path)
@ -104,7 +102,7 @@ class AppLocation(object):
:param str extension: Defaults to ''. The extension to search for. For example:: :param str extension: Defaults to ''. The extension to search for. For example::
'.png' '.png'
:return: List of files found. :return: List of files found.
:rtype: list[pathlib.Path] :rtype: list[openlp.core.common.path.Path]
""" """
path = AppLocation.get_data_path() path = AppLocation.get_data_path()
if section: if section:
@ -120,9 +118,8 @@ class AppLocation(object):
""" """
Return the path a particular module stores its data under. Return the path a particular module stores its data under.
:type section: str :param str section:
:rtype: openlp.core.common.path.Path
:rtype: pathlib.Path
""" """
path = AppLocation.get_data_path() / section path = AppLocation.get_data_path() / section
check_directory_exists(path) check_directory_exists(path)
@ -135,7 +132,7 @@ def _get_os_dir_path(dir_type):
:param dir_type: AppLocation Enum of the requested path type :param dir_type: AppLocation Enum of the requested path type
:return: The requested path :return: The requested path
:rtype: pathlib.Path :rtype: openlp.core.common.path.Path
""" """
# If running from source, return the language directory from the source directory # If running from source, return the language directory from the source directory
if dir_type == AppLocation.LanguageDir: if dir_type == AppLocation.LanguageDir:

View File

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2017 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from json import JSONDecoder, JSONEncoder
from openlp.core.common.path import Path
class OpenLPJsonDecoder(JSONDecoder):
"""
Implement a custom JSONDecoder to handle Path objects
Example Usage:
object = json.loads(json_string, cls=OpenLPJsonDecoder)
"""
def __init__(self, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True,
object_pairs_hook=None, **kwargs):
"""
Re-implement __init__ so that we can pass in our object_hook method. Any additional kwargs, are stored in the
instance and are passed to custom objects upon encoding or decoding.
"""
self.kwargs = kwargs
if object_hook is None:
object_hook = self.custom_object_hook
super().__init__(object_hook, parse_float, parse_int, parse_constant, strict, object_pairs_hook)
def custom_object_hook(self, obj):
"""
Implement a custom Path object decoder.
:param dict obj: A decoded JSON object
:return: The original object literal, or a Path object if the object literal contains a key '__Path__'
:rtype: dict | openlp.core.common.path.Path
"""
if '__Path__' in obj:
obj = Path.encode_json(obj, **self.kwargs)
return obj
class OpenLPJsonEncoder(JSONEncoder):
"""
Implement a custom JSONEncoder to handle Path objects
Example Usage:
json_string = json.dumps(object, cls=OpenLPJsonEncoder)
"""
def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False,
indent=None, separators=None, default=None, **kwargs):
"""
Re-implement __init__ so that we can pass in additional kwargs, which are stored in the instance and are passed
to custom objects upon encoding or decoding.
"""
self.kwargs = kwargs
if default is None:
default = self.custom_default
super().__init__(skipkeys, ensure_ascii, check_circular, allow_nan, sort_keys, indent, separators, default)
def custom_default(self, obj):
"""
Convert any Path objects into a dictionary object which can be serialized.
:param object obj: The object to convert
:return: The serializable object
:rtype: dict
"""
if isinstance(obj, Path):
return obj.json_object(**self.kwargs)
return super().default(obj)

View File

@ -19,17 +19,21 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from contextlib import suppress
from pathlib import Path from openlp.core.common import is_win
if is_win():
from pathlib import WindowsPath as PathVariant
else:
from pathlib import PosixPath as PathVariant
def path_to_str(path): def path_to_str(path=None):
""" """
A utility function to convert a Path object or NoneType to a string equivalent. A utility function to convert a Path object or NoneType to a string equivalent.
:param path: The value to convert to a string :param openlp.core.common.path.Path | None path: The value to convert to a string
:type: pathlib.Path or None
:return: An empty string if :param:`path` is None, else a string representation of the :param:`path` :return: An empty string if :param:`path` is None, else a string representation of the :param:`path`
:rtype: str :rtype: str
""" """
@ -48,14 +52,49 @@ def str_to_path(string):
This function is of particular use because initating a Path object with an empty string causes the Path object to This function is of particular use because initating a Path object with an empty string causes the Path object to
point to the current working directory. point to the current working directory.
:param string: The string to convert :param str string: The string to convert
:type string: str
:return: None if :param:`string` is empty, or a Path object representation of :param:`string` :return: None if :param:`string` is empty, or a Path object representation of :param:`string`
:rtype: pathlib.Path or None :rtype: openlp.core.common.path.Path | None
""" """
if not isinstance(string, str): if not isinstance(string, str):
raise TypeError('parameter \'string\' must be of type str') raise TypeError('parameter \'string\' must be of type str')
if string == '': if string == '':
return None return None
return Path(string) return Path(string)
class Path(PathVariant):
"""
Subclass pathlib.Path, so we can add json conversion methods
"""
@staticmethod
def encode_json(obj, base_path=None, **kwargs):
"""
Create a Path object from a dictionary representation. The dictionary has been constructed by JSON encoding of
a JSON reprensation of a Path object.
:param dict[str] obj: The dictionary representation
:param openlp.core.common.path.Path base_path: If specified, an absolute path to base the relative path off of.
:param kwargs: Contains any extra parameters. Not used!
:return: The reconstructed Path object
:rtype: openlp.core.common.path.Path
"""
path = Path(*obj['__Path__'])
if base_path and not path.is_absolute():
return base_path / path
return path
def json_object(self, base_path=None, **kwargs):
"""
Create a dictionary that can be JSON decoded.
:param openlp.core.common.path.Path base_path: If specified, an absolute path to make a relative path from.
:param kwargs: Contains any extra parameters. Not used!
:return: The dictionary representation of this Path object.
:rtype: dict[tuple]
"""
path = self
if base_path:
with suppress(ValueError):
path = path.relative_to(base_path)
return {'__Path__': path.parts}

View File

@ -24,15 +24,19 @@ This class contains the core default settings.
""" """
import datetime import datetime
import logging import logging
import json
import os import os
from tempfile import gettempdir
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import ThemeLevel, SlideLimits, UiStrings, is_win, is_linux
from openlp.core.common import SlideLimits, ThemeLevel, UiStrings, is_linux, is_win, translate
from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
from openlp.core.common.path import Path, str_to_path
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
__version__ = 2
# Fix for bug #1014422. # Fix for bug #1014422.
X11_BYPASS_DEFAULT = True X11_BYPASS_DEFAULT = True
@ -44,21 +48,6 @@ if is_linux():
X11_BYPASS_DEFAULT = False X11_BYPASS_DEFAULT = False
def recent_files_conv(value):
"""
If the value is not a list convert it to a list
:param value: Value to convert
:return: value as a List
"""
if isinstance(value, list):
return value
elif isinstance(value, str):
return [value]
elif isinstance(value, bytes):
return [value.decode()]
return []
def media_players_conv(string): def media_players_conv(string):
""" """
If phonon is in the setting string replace it with system If phonon is in the setting string replace it with system
@ -73,14 +62,25 @@ def media_players_conv(string):
return string return string
def file_names_conv(file_names):
"""
Convert a list of file names in to a list of file paths.
:param list[str] file_names: The list of file names to convert.
:return: The list converted to file paths
:rtype: openlp.core.common.path.Path
"""
if file_names:
return [str_to_path(file_name) for file_name in file_names]
class Settings(QtCore.QSettings): class Settings(QtCore.QSettings):
""" """
Class to wrap QSettings. Class to wrap QSettings.
* Exposes all the methods of QSettings. * Exposes all the methods of QSettings.
* Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to ``IniFormat``, and the path to the Ini
``IniFormat``, and the path to the Ini file is set using ``set_filename``, file is set using ``set_filename``, then the Settings constructor (without any arguments) will create a Settings
then the Settings constructor (without any arguments) will create a Settings
object for accessing settings stored in that Ini file. object for accessing settings stored in that Ini file.
``__default_settings__`` ``__default_settings__``
@ -91,7 +91,7 @@ class Settings(QtCore.QSettings):
('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]) ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)])
The first entry is the *old key*; it will be removed. The first entry is the *old key*; if it is different from the *new key* it will be removed.
The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove
the old key. The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made. the old key. The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made.
@ -105,11 +105,12 @@ class Settings(QtCore.QSettings):
So, if the type of the old value is bool, then there must be two rules. So, if the type of the old value is bool, then there must be two rules.
""" """
__default_settings__ = { __default_settings__ = {
'settings/version': 0,
'advanced/add page break': False, 'advanced/add page break': False,
'advanced/alternate rows': not is_win(), 'advanced/alternate rows': not is_win(),
'advanced/autoscrolling': {'dist': 1, 'pos': 0}, 'advanced/autoscrolling': {'dist': 1, 'pos': 0},
'advanced/current media plugin': -1, 'advanced/current media plugin': -1,
'advanced/data path': '', 'advanced/data path': None,
# 7 stands for now, 0 to 6 is Monday to Sunday. # 7 stands for now, 0 to 6 is Monday to Sunday.
'advanced/default service day': 7, 'advanced/default service day': 7,
'advanced/default service enabled': True, 'advanced/default service enabled': True,
@ -143,7 +144,7 @@ class Settings(QtCore.QSettings):
'api/authentication enabled': False, 'api/authentication enabled': False,
'api/ip address': '0.0.0.0', 'api/ip address': '0.0.0.0',
'api/thumbnails': True, 'api/thumbnails': True,
'crashreport/last directory': '', 'crashreport/last directory': None,
'formattingTags/html_tags': '', 'formattingTags/html_tags': '',
'core/audio repeat list': False, 'core/audio repeat list': False,
'core/auto open': False, 'core/auto open': False,
@ -162,7 +163,7 @@ class Settings(QtCore.QSettings):
'core/screen blank': False, 'core/screen blank': False,
'core/show splash': True, 'core/show splash': True,
'core/logo background color': '#ffffff', 'core/logo background color': '#ffffff',
'core/logo file': ':/graphics/openlp-splash-screen.png', 'core/logo file': Path(':/graphics/openlp-splash-screen.png'),
'core/logo hide on startup': False, 'core/logo hide on startup': False,
'core/songselect password': '', 'core/songselect password': '',
'core/songselect username': '', 'core/songselect username': '',
@ -177,17 +178,17 @@ class Settings(QtCore.QSettings):
'media/players': 'system,webkit', 'media/players': 'system,webkit',
'media/override player': QtCore.Qt.Unchecked, 'media/override player': QtCore.Qt.Unchecked,
'players/background color': '#000000', 'players/background color': '#000000',
'servicemanager/last directory': '', 'servicemanager/last directory': None,
'servicemanager/last file': '', 'servicemanager/last file': None,
'servicemanager/service theme': '', 'servicemanager/service theme': None,
'SettingsImport/file_date_created': datetime.datetime.now().strftime("%Y-%m-%d %H:%M"), 'SettingsImport/file_date_created': datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
'SettingsImport/Make_Changes': 'At_Own_RISK', 'SettingsImport/Make_Changes': 'At_Own_RISK',
'SettingsImport/type': 'OpenLP_settings_export', 'SettingsImport/type': 'OpenLP_settings_export',
'SettingsImport/version': '', 'SettingsImport/version': '',
'themes/global theme': '', 'themes/global theme': '',
'themes/last directory': '', 'themes/last directory': None,
'themes/last directory export': '', 'themes/last directory export': None,
'themes/last directory import': '', 'themes/last directory import': None,
'themes/theme level': ThemeLevel.Song, 'themes/theme level': ThemeLevel.Song,
'themes/wrap footer': False, 'themes/wrap footer': False,
'user interface/live panel': True, 'user interface/live panel': True,
@ -208,22 +209,20 @@ class Settings(QtCore.QSettings):
'projector/db database': '', 'projector/db database': '',
'projector/enable': True, 'projector/enable': True,
'projector/connect on start': False, 'projector/connect on start': False,
'projector/last directory import': '', 'projector/last directory import': None,
'projector/last directory export': '', 'projector/last directory export': None,
'projector/poll time': 20, # PJLink timeout is 30 seconds 'projector/poll time': 20, # PJLink timeout is 30 seconds
'projector/socket timeout': 5, # 5 second socket timeout 'projector/socket timeout': 5, # 5 second socket timeout
'projector/source dialog type': 0 # Source select dialog box type 'projector/source dialog type': 0 # Source select dialog box type
} }
__file_path__ = '' __file_path__ = ''
__obsolete_settings__ = [ __setting_upgrade_1__ = [
# Changed during 2.2.x development. # Changed during 2.2.x development.
# ('advanced/stylesheet fix', '', []),
# ('general/recent files', 'core/recent files', [(recent_files_conv, None)]),
('songs/search as type', 'advanced/search as type', []), ('songs/search as type', 'advanced/search as type', []),
('media/players', 'media/players_temp', [(media_players_conv, None)]), # Convert phonon to system ('media/players', 'media/players_temp', [(media_players_conv, None)]), # Convert phonon to system
('media/players_temp', 'media/players', []), # Move temp setting from above to correct setting ('media/players_temp', 'media/players', []), # Move temp setting from above to correct setting
('advanced/default color', 'core/logo background color', []), # Default image renamed + moved to general > 2.4. ('advanced/default color', 'core/logo background color', []), # Default image renamed + moved to general > 2.4.
('advanced/default image', '/core/logo file', []), # Default image renamed + moved to general after 2.4. ('advanced/default image', 'core/logo file', []), # Default image renamed + moved to general after 2.4.
('remotes/https enabled', '', []), ('remotes/https enabled', '', []),
('remotes/https port', '', []), ('remotes/https port', '', []),
('remotes/twelve hour', 'api/twelve hour', []), ('remotes/twelve hour', 'api/twelve hour', []),
@ -234,7 +233,6 @@ class Settings(QtCore.QSettings):
('remotes/authentication enabled', 'api/authentication enabled', []), ('remotes/authentication enabled', 'api/authentication enabled', []),
('remotes/ip address', 'api/ip address', []), ('remotes/ip address', 'api/ip address', []),
('remotes/thumbnails', 'api/thumbnails', []), ('remotes/thumbnails', 'api/thumbnails', []),
('advanced/default image', 'core/logo file', []), # Default image renamed + moved to general after 2.4.
('shortcuts/escapeItem', 'shortcuts/desktopScreenEnable', []), # Escape item was removed in 2.6. ('shortcuts/escapeItem', 'shortcuts/desktopScreenEnable', []), # Escape item was removed in 2.6.
('shortcuts/offlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6. ('shortcuts/offlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6.
('shortcuts/onlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6. ('shortcuts/onlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6.
@ -243,7 +241,28 @@ class Settings(QtCore.QSettings):
# Last search type was renamed to last used search type in 2.6 since Bible search value type changed in 2.6. # Last search type was renamed to last used search type in 2.6 since Bible search value type changed in 2.6.
('songs/last search type', 'songs/last used search type', []), ('songs/last search type', 'songs/last used search type', []),
('bibles/last search type', '', []), ('bibles/last search type', '', []),
('custom/last search type', 'custom/last used search type', []) ('custom/last search type', 'custom/last used search type', [])]
__setting_upgrade_2__ = [
# The following changes are being made for the conversion to using Path objects made in 2.6 development
('advanced/data path', 'advanced/data path', [(str_to_path, None)]),
('crashreport/last directory', 'crashreport/last directory', [(str_to_path, None)]),
('servicemanager/last directory', 'servicemanager/last directory', [(str_to_path, None)]),
('servicemanager/last file', 'servicemanager/last file', [(str_to_path, None)]),
('themes/last directory', 'themes/last directory', [(str_to_path, None)]),
('themes/last directory export', 'themes/last directory export', [(str_to_path, None)]),
('themes/last directory import', 'themes/last directory import', [(str_to_path, None)]),
('projector/last directory import', 'projector/last directory import', [(str_to_path, None)]),
('projector/last directory export', 'projector/last directory export', [(str_to_path, None)]),
('bibles/last directory import', 'bibles/last directory import', [(str_to_path, None)]),
('presentations/pdf_program', 'presentations/pdf_program', [(str_to_path, None)]),
('songs/last directory import', 'songs/last directory import', [(str_to_path, None)]),
('songs/last directory export', 'songs/last directory export', [(str_to_path, None)]),
('songusage/last directory export', 'songusage/last directory export', [(str_to_path, None)]),
('core/recent files', 'core/recent files', [(file_names_conv, None)]),
('media/media files', 'media/media files', [(file_names_conv, None)]),
('presentations/presentations files', 'presentations/presentations files', [(file_names_conv, None)]),
('core/logo file', 'core/logo file', [(str_to_path, None)])
] ]
@staticmethod @staticmethod
@ -256,13 +275,16 @@ class Settings(QtCore.QSettings):
Settings.__default_settings__.update(default_values) Settings.__default_settings__.update(default_values)
@staticmethod @staticmethod
def set_filename(ini_file): def set_filename(ini_path):
""" """
Sets the complete path to an Ini file to be used by Settings objects. Sets the complete path to an Ini file to be used by Settings objects.
Does not affect existing Settings objects. Does not affect existing Settings objects.
:param openlp.core.common.path.Path ini_path: ini file path
:rtype: None
""" """
Settings.__file_path__ = ini_file Settings.__file_path__ = str(ini_path)
@staticmethod @staticmethod
def set_up_default_values(): def set_up_default_values():
@ -431,14 +453,28 @@ class Settings(QtCore.QSettings):
key = self.group() + '/' + key key = self.group() + '/' + key
return Settings.__default_settings__[key] return Settings.__default_settings__[key]
def remove_obsolete_settings(self): def can_upgrade(self):
"""
Can / should the settings be upgraded
:rtype: bool
"""
return __version__ != self.value('settings/version')
def upgrade_settings(self):
""" """
This method is only called to clean up the config. It removes old settings and it renames settings. See This method is only called to clean up the config. It removes old settings and it renames settings. See
``__obsolete_settings__`` for more details. ``__obsolete_settings__`` for more details.
""" """
for old_key, new_key, rules in Settings.__obsolete_settings__: current_version = self.value('settings/version')
# Once removed we don't have to do this again. for version in range(current_version, __version__):
if self.contains(old_key): version += 1
upgrade_list = getattr(self, '__setting_upgrade_{version}__'.format(version=version))
for old_key, new_key, rules in upgrade_list:
# Once removed we don't have to do this again. - Can be removed once fully switched to the versioning
# system.
if not self.contains(old_key):
continue
if new_key: if new_key:
# Get the value of the old_key. # Get the value of the old_key.
old_value = super(Settings, self).value(old_key) old_value = super(Settings, self).value(old_key)
@ -457,14 +493,17 @@ class Settings(QtCore.QSettings):
old_value = new old_value = new
break break
self.setValue(new_key, old_value) self.setValue(new_key, old_value)
self.remove(old_key) if new_key != old_key:
self.remove(old_key)
self.setValue('settings/version', version)
def value(self, key): def value(self, key):
""" """
Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the
*Settings.__default_settings__* dict. *Settings.__default_settings__* dict.
:param key: The key to return the value from. :param str key: The key to return the value from.
:return: The value stored by the setting.
""" """
# if group() is not empty the group has not been specified together with the key. # if group() is not empty the group has not been specified together with the key.
if self.group(): if self.group():
@ -474,6 +513,18 @@ class Settings(QtCore.QSettings):
setting = super(Settings, self).value(key, default_value) setting = super(Settings, self).value(key, default_value)
return self._convert_value(setting, default_value) return self._convert_value(setting, default_value)
def setValue(self, key, value):
"""
Reimplement the setValue method to handle Path objects.
:param str key: The key of the setting to save
:param value: The value to save
:rtype: None
"""
if isinstance(value, Path) or (isinstance(value, list) and value and isinstance(value[0], Path)):
value = json.dumps(value, cls=OpenLPJsonEncoder)
super().setValue(key, value)
def _convert_value(self, setting, default_value): def _convert_value(self, setting, default_value):
""" """
This converts the given ``setting`` to the type of the given ``default_value``. This converts the given ``setting`` to the type of the given ``default_value``.
@ -491,8 +542,11 @@ class Settings(QtCore.QSettings):
if isinstance(default_value, str): if isinstance(default_value, str):
return '' return ''
# An empty list saved to the settings results in a None type being returned. # An empty list saved to the settings results in a None type being returned.
else: elif isinstance(default_value, list):
return [] return []
elif isinstance(setting, str):
if '__Path__' in setting:
return json.loads(setting, cls=OpenLPJsonDecoder)
# Convert the setting to the correct type. # Convert the setting to the correct type.
if isinstance(default_value, bool): if isinstance(default_value, bool):
if isinstance(setting, bool): if isinstance(setting, bool):
@ -502,3 +556,59 @@ class Settings(QtCore.QSettings):
if isinstance(default_value, int): if isinstance(default_value, int):
return int(setting) return int(setting)
return setting return setting
def export(self, dest_path):
"""
Export the settings to file.
:param openlp.core.common.path.Path dest_path: The file path to create the export file.
:return: Success
:rtype: bool
"""
temp_path = Path(gettempdir(), 'openlp', 'exportConf.tmp')
# Delete old files if found.
if temp_path.exists():
temp_path.unlink()
if dest_path.exists():
dest_path.unlink()
self.remove('SettingsImport')
# Get the settings.
keys = self.allKeys()
export_settings = QtCore.QSettings(str(temp_path), Settings.IniFormat)
# Add a header section.
# This is to insure it's our conf file for import.
now = datetime.datetime.now()
# Write INI format using QSettings.
# Write our header.
export_settings.beginGroup('SettingsImport')
export_settings.setValue('Make_Changes', 'At_Own_RISK')
export_settings.setValue('type', 'OpenLP_settings_export')
export_settings.setValue('file_date_created', now.strftime("%Y-%m-%d %H:%M"))
export_settings.endGroup()
# Write all the sections and keys.
for section_key in keys:
# FIXME: We are conflicting with the standard "General" section.
if 'eneral' in section_key:
section_key = section_key.lower()
key_value = super().value(section_key)
if key_value is not None:
export_settings.setValue(section_key, key_value)
export_settings.sync()
# Temp CONF file has been written. Blanks in keys are now '%20'.
# Read the temp file and output the user's CONF file with blanks to
# make it more readable.
try:
with dest_path.open('w') as export_conf_file, temp_path.open('r') as temp_conf:
for file_record in temp_conf:
# Get rid of any invalid entries.
if file_record.find('@Invalid()') == -1:
file_record = file_record.replace('%20', ' ')
export_conf_file.write(file_record)
except OSError as ose:
QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
translate('OpenLP.MainWindow',
'An error occurred while exporting the settings: {err}'
).format(err=ose.strerror),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
finally:
temp_path.unlink()

View File

@ -89,7 +89,7 @@ def get_text_file_string(text_file_path):
returns False. If there is an error loading the file or the content can't be decoded then the function will return returns False. If there is an error loading the file or the content can't be decoded then the function will return
None. None.
:param pathlib.Path text_file_path: The path to the file. :param openlp.core.common.path.Path text_file_path: The path to the file.
:return: The contents of the file, False if the file does not exist, or None if there is an Error reading or :return: The contents of the file, False if the file does not exist, or None if there is an Error reading or
decoding the file. decoding the file.
:rtype: str | False | None :rtype: str | False | None
@ -610,17 +610,11 @@ def replace_params(args, kwargs, params):
""" """
Apply a transformation function to the specified args or kwargs Apply a transformation function to the specified args or kwargs
:param args: Positional arguments :param tuple args: Positional arguments
:type args: (,) :param dict kwargs: Key Word arguments
:param kwargs: Key Word arguments
:type kwargs: dict
:param params: A tuple of tuples with the position and the key word to replace. :param params: A tuple of tuples with the position and the key word to replace.
:type params: ((int, str, path_to_str),)
:return: The modified positional and keyword arguments :return: The modified positional and keyword arguments
:rtype: (tuple, dict) :rtype: tuple[tuple, dict]
Usage: Usage:

View File

@ -29,7 +29,7 @@ import re
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib import ServiceItem, StringContent, ServiceItemContext from openlp.core.lib import ServiceItem, StringContent, ServiceItemContext
from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.searchedit import SearchEdit
from openlp.core.lib.ui import create_widget_action, critical_error_message_box from openlp.core.lib.ui import create_widget_action, critical_error_message_box
@ -313,7 +313,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
""" """
file_paths, selected_filter = FileDialog.getOpenFileNames( file_paths, selected_filter = FileDialog.getOpenFileNames(
self, self.on_new_prompt, self, self.on_new_prompt,
str_to_path(Settings().value(self.settings_section + '/last directory')), Settings().value(self.settings_section + '/last directory'),
self.on_new_file_masks) self.on_new_file_masks)
log.info('New files(s) {file_paths}'.format(file_paths=file_paths)) log.info('New files(s) {file_paths}'.format(file_paths=file_paths))
if file_paths: if file_paths:
@ -377,9 +377,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
self.list_view.clear() self.list_view.clear()
self.load_list(full_list, target_group) self.load_list(full_list, target_group)
last_dir = os.path.split(files[0])[0] last_dir = os.path.split(files[0])[0]
Settings().setValue(self.settings_section + '/last directory', last_dir) Settings().setValue(self.settings_section + '/last directory', Path(last_dir))
Settings().setValue('{section}/{section} files'.format(section=self.settings_section), Settings().setValue('{section}/{section} files'.format(section=self.settings_section), self.get_file_list())
self.get_file_list())
if duplicates_found: if duplicates_found:
critical_error_message_box(UiStrings().Duplicate, critical_error_message_box(UiStrings().Duplicate,
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
@ -400,13 +399,15 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
def get_file_list(self): def get_file_list(self):
""" """
Return the current list of files Return the current list of files
:rtype: list[openlp.core.common.path.Path]
""" """
file_list = [] file_paths = []
for index in range(self.list_view.count()): for index in range(self.list_view.count()):
list_item = self.list_view.item(index) list_item = self.list_view.item(index)
filename = list_item.data(QtCore.Qt.UserRole) filename = list_item.data(QtCore.Qt.UserRole)
file_list.append(filename) file_paths.append(str_to_path(filename))
return file_list return file_paths
def load_list(self, load_list, target_group): def load_list(self, load_list, target_group):
""" """

View File

@ -1,61 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2017 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from pathlib import Path
def path_to_str(path):
"""
A utility function to convert a Path object or NoneType to a string equivalent.
:param path: The value to convert to a string
:type: pathlib.Path or None
:return: An empty string if :param:`path` is None, else a string representation of the :param:`path`
:rtype: str
"""
if not isinstance(path, Path) and path is not None:
raise TypeError('parameter \'path\' must be of type Path or NoneType')
if path is None:
return ''
else:
return str(path)
def str_to_path(string):
"""
A utility function to convert a str object to a Path or NoneType.
This function is of particular use because initating a Path object with an empty string causes the Path object to
point to the current working directory.
:param string: The string to convert
:type string: str
:return: None if :param:`string` is empty, or a Path object representation of :param:`string`
:rtype: pathlib.Path or None
"""
if not isinstance(string, str):
raise TypeError('parameter \'string\' must be of type str')
if string == '':
return None
return Path(string)

View File

@ -150,7 +150,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
self.status = PluginStatus.Inactive self.status = PluginStatus.Inactive
# Add the default status to the default settings. # Add the default status to the default settings.
default_settings[name + '/status'] = PluginStatus.Inactive default_settings[name + '/status'] = PluginStatus.Inactive
default_settings[name + '/last directory'] = '' default_settings[name + '/last directory'] = None
# Append a setting for files in the mediamanager (note not all plugins # Append a setting for files in the mediamanager (note not all plugins
# which have a mediamanager need this). # which have a mediamanager need this).
if media_item_class is not None: if media_item_class is not None:

View File

@ -70,9 +70,9 @@ try:
except ImportError: except ImportError:
VLC_VERSION = '-' VLC_VERSION = '-'
from openlp.core.common import Settings, UiStrings, translate from openlp.core.common import RegistryProperties, Settings, UiStrings, is_linux, translate
from openlp.core.common.versionchecker import get_application_version from openlp.core.common.versionchecker import get_application_version
from openlp.core.common import RegistryProperties, is_linux from openlp.core.ui.lib.filedialog import FileDialog
from .exceptiondialog import Ui_ExceptionDialog from .exceptiondialog import Ui_ExceptionDialog
@ -139,17 +139,17 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
""" """
Saving exception log and system information to a file. Saving exception log and system information to a file.
""" """
filename = QtWidgets.QFileDialog.getSaveFileName( file_path, filter_used = FileDialog.getSaveFileName(
self, self,
translate('OpenLP.ExceptionForm', 'Save Crash Report'), translate('OpenLP.ExceptionForm', 'Save Crash Report'),
Settings().value(self.settings_section + '/last directory'), Settings().value(self.settings_section + '/last directory'),
translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))[0] translate('OpenLP.ExceptionForm', 'Text files (*.txt *.log *.text)'))
if filename: if file_path:
filename = str(filename).replace('/', os.path.sep) Settings().setValue(self.settings_section + '/last directory', file_path.parent)
Settings().setValue(self.settings_section + '/last directory', os.path.dirname(filename))
opts = self._create_report() opts = self._create_report()
report_text = self.report_text.format(version=opts['version'], description=opts['description'], report_text = self.report_text.format(version=opts['version'], description=opts['description'],
traceback=opts['traceback'], libs=opts['libs'], system=opts['system']) traceback=opts['traceback'], libs=opts['libs'], system=opts['system'])
filename = str(file_path)
try: try:
report_file = open(filename, 'w') report_file = open(filename, 'w')
try: try:
@ -212,17 +212,16 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
def on_attach_file_button_clicked(self): def on_attach_file_button_clicked(self):
""" """
Attache files to the bug report e-mail. Attach files to the bug report e-mail.
""" """
files, filter_used = QtWidgets.QFileDialog.getOpenFileName(self, file_path, filter_used = \
translate('ImagePlugin.ExceptionDialog', FileDialog.getOpenFileName(self,
'Select Attachment'), translate('ImagePlugin.ExceptionDialog', 'Select Attachment'),
Settings().value(self.settings_section + Settings().value(self.settings_section + '/last directory'),
'/last directory'), '{text} (*)'.format(text=UiStrings().AllFiles))
'{text} (*)'.format(text=UiStrings().AllFiles)) log.info('New file {file}'.format(file=file_path))
log.info('New files(s) {files}'.format(files=str(files))) if file_path:
if files: self.file_attachment = str(file_path)
self.file_attachment = str(files)
def __button_state(self, state): def __button_state(self, state):
""" """

View File

@ -30,13 +30,13 @@ import urllib.request
import urllib.parse import urllib.parse
import urllib.error import urllib.error
from configparser import ConfigParser, MissingSectionHeaderError, NoOptionError, NoSectionError from configparser import ConfigParser, MissingSectionHeaderError, NoOptionError, NoSectionError
from pathlib import Path
from tempfile import gettempdir from tempfile import gettempdir
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, \ from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, \
translate, clean_button_text, trace_error_handler translate, clean_button_text, trace_error_handler
from openlp.core.common.path import Path
from openlp.core.lib import PluginStatus, build_icon from openlp.core.lib import PluginStatus, build_icon
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.core.common.httputils import get_web_page, get_url_file_size, url_get_file, CONNECTION_TIMEOUT from openlp.core.common.httputils import get_web_page, get_url_file_size, url_get_file, CONNECTION_TIMEOUT

View File

@ -23,12 +23,11 @@
The general tab of the configuration dialog. The general tab of the configuration dialog.
""" """
import logging import logging
from pathlib import Path
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, Settings, UiStrings, translate, get_images_filter from openlp.core.common import Registry, Settings, UiStrings, translate, get_images_filter
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib import SettingsTab, ScreenList from openlp.core.lib import SettingsTab, ScreenList
from openlp.core.ui.lib import ColorButton, PathEdit from openlp.core.ui.lib import ColorButton, PathEdit
@ -294,7 +293,7 @@ class GeneralTab(SettingsTab):
self.auto_open_check_box.setChecked(settings.value('auto open')) self.auto_open_check_box.setChecked(settings.value('auto open'))
self.show_splash_check_box.setChecked(settings.value('show splash')) self.show_splash_check_box.setChecked(settings.value('show splash'))
self.logo_background_color = settings.value('logo background color') self.logo_background_color = settings.value('logo background color')
self.logo_file_path_edit.path = str_to_path(settings.value('logo file')) self.logo_file_path_edit.path = settings.value('logo file')
self.logo_hide_on_startup_check_box.setChecked(settings.value('logo hide on startup')) self.logo_hide_on_startup_check_box.setChecked(settings.value('logo hide on startup'))
self.logo_color_button.color = self.logo_background_color self.logo_color_button.color = self.logo_background_color
self.check_for_updates_check_box.setChecked(settings.value('update check')) self.check_for_updates_check_box.setChecked(settings.value('update check'))
@ -328,7 +327,7 @@ class GeneralTab(SettingsTab):
settings.setValue('auto open', self.auto_open_check_box.isChecked()) settings.setValue('auto open', self.auto_open_check_box.isChecked())
settings.setValue('show splash', self.show_splash_check_box.isChecked()) settings.setValue('show splash', self.show_splash_check_box.isChecked())
settings.setValue('logo background color', self.logo_background_color) settings.setValue('logo background color', self.logo_background_color)
settings.setValue('logo file', path_to_str(self.logo_file_path_edit.path)) settings.setValue('logo file', self.logo_file_path_edit.path)
settings.setValue('logo hide on startup', self.logo_hide_on_startup_check_box.isChecked()) settings.setValue('logo hide on startup', self.logo_hide_on_startup_check_box.isChecked())
settings.setValue('update check', self.check_for_updates_check_box.isChecked()) settings.setValue('update check', self.check_for_updates_check_box.isChecked())
settings.setValue('save prompt', self.save_check_service_check_box.isChecked()) settings.setValue('save prompt', self.save_check_service_check_box.isChecked())

View File

@ -20,11 +20,9 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" Patch the QFileDialog so it accepts and returns Path objects""" """ Patch the QFileDialog so it accepts and returns Path objects"""
from pathlib import Path
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib import replace_params from openlp.core.lib import replace_params
@ -36,7 +34,7 @@ class FileDialog(QtWidgets.QFileDialog):
:type parent: QtWidgets.QWidget or None :type parent: QtWidgets.QWidget or None
:type caption: str :type caption: str
:type directory: pathlib.Path :type directory: openlp.core.common.path.Path
:type options: QtWidgets.QFileDialog.Options :type options: QtWidgets.QFileDialog.Options
:rtype: tuple[Path, str] :rtype: tuple[Path, str]
""" """
@ -55,7 +53,7 @@ class FileDialog(QtWidgets.QFileDialog):
:type parent: QtWidgets.QWidget or None :type parent: QtWidgets.QWidget or None
:type caption: str :type caption: str
:type directory: pathlib.Path :type directory: openlp.core.common.path.Path
:type filter: str :type filter: str
:type initialFilter: str :type initialFilter: str
:type options: QtWidgets.QFileDialog.Options :type options: QtWidgets.QFileDialog.Options
@ -76,7 +74,7 @@ class FileDialog(QtWidgets.QFileDialog):
:type parent: QtWidgets.QWidget or None :type parent: QtWidgets.QWidget or None
:type caption: str :type caption: str
:type directory: pathlib.Path :type directory: openlp.core.common.path.Path
:type filter: str :type filter: str
:type initialFilter: str :type initialFilter: str
:type options: QtWidgets.QFileDialog.Options :type options: QtWidgets.QFileDialog.Options
@ -98,7 +96,7 @@ class FileDialog(QtWidgets.QFileDialog):
:type parent: QtWidgets.QWidget or None :type parent: QtWidgets.QWidget or None
:type caption: str :type caption: str
:type directory: pathlib.Path :type directory: openlp.core.common.path.Path
:type filter: str :type filter: str
:type initialFilter: str :type initialFilter: str
:type options: QtWidgets.QFileDialog.Options :type options: QtWidgets.QFileDialog.Options

View File

@ -20,12 +20,11 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from enum import Enum from enum import Enum
from pathlib import Path
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import UiStrings, translate from openlp.core.common import UiStrings, translate
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
from openlp.core.ui.lib.filedialog import FileDialog from openlp.core.ui.lib.filedialog import FileDialog
@ -46,19 +45,11 @@ class PathEdit(QtWidgets.QWidget):
""" """
Initialise the PathEdit widget Initialise the PathEdit widget
:param parent: The parent of the widget. This is just passed to the super method. :param QtWidget.QWidget | None: The parent of the widget. This is just passed to the super method.
:type parent: QWidget or None :param str dialog_caption: Used to customise the caption in the QFileDialog.
:param openlp.core.common.path.Path default_path: The default path. This is set as the path when the revert
:param dialog_caption: Used to customise the caption in the QFileDialog. button is clicked
:type dialog_caption: str :param bool show_revert: Used to determine if the 'revert button' should be visible.
:param default_path: The default path. This is set as the path when the revert button is clicked
:type default_path: pathlib.Path
:param show_revert: Used to determine if the 'revert button' should be visible.
:type show_revert: bool
:return: None
:rtype: None :rtype: None
""" """
super().__init__(parent) super().__init__(parent)
@ -72,10 +63,7 @@ class PathEdit(QtWidgets.QWidget):
def _setup(self, show_revert): def _setup(self, show_revert):
""" """
Set up the widget Set up the widget
:param show_revert: Show or hide the revert button :param bool show_revert: Show or hide the revert button
:type show_revert: bool
:return: None
:rtype: None :rtype: None
""" """
widget_layout = QtWidgets.QHBoxLayout() widget_layout = QtWidgets.QHBoxLayout()
@ -102,7 +90,7 @@ class PathEdit(QtWidgets.QWidget):
A property getter method to return the selected path. A property getter method to return the selected path.
:return: The selected path :return: The selected path
:rtype: pathlib.Path :rtype: openlp.core.common.path.Path
""" """
return self._path return self._path
@ -111,10 +99,7 @@ class PathEdit(QtWidgets.QWidget):
""" """
A Property setter method to set the selected path A Property setter method to set the selected path
:param path: The path to set the widget to :param openlp.core.common.path.Path path: The path to set the widget to
:type path: pathlib.Path
:return: None
:rtype: None :rtype: None
""" """
self._path = path self._path = path
@ -138,10 +123,7 @@ class PathEdit(QtWidgets.QWidget):
""" """
A Property setter method to set the path type A Property setter method to set the path type
:param path_type: The type of path to select :param PathType path_type: The type of path to select
:type path_type: PathType
:return: None
:rtype: None :rtype: None
""" """
self._path_type = path_type self._path_type = path_type
@ -151,7 +133,6 @@ class PathEdit(QtWidgets.QWidget):
""" """
Called to update the tooltips on the buttons. This is changing path types, and when the widget is initalised Called to update the tooltips on the buttons. This is changing path types, and when the widget is initalised
:return: None
:rtype: None :rtype: None
""" """
if self._path_type == PathType.Directories: if self._path_type == PathType.Directories:
@ -167,7 +148,6 @@ class PathEdit(QtWidgets.QWidget):
Show the QFileDialog and process the input from the user Show the QFileDialog and process the input from the user
:return: None
:rtype: None :rtype: None
""" """
caption = self.dialog_caption caption = self.dialog_caption
@ -189,7 +169,6 @@ class PathEdit(QtWidgets.QWidget):
Set the new path to the value of the default_path instance variable. Set the new path to the value of the default_path instance variable.
:return: None
:rtype: None :rtype: None
""" """
self.on_new_path(self.default_path) self.on_new_path(self.default_path)
@ -198,7 +177,6 @@ class PathEdit(QtWidgets.QWidget):
""" """
A handler to handle when the line edit has finished being edited. A handler to handle when the line edit has finished being edited.
:return: None
:rtype: None :rtype: None
""" """
path = str_to_path(self.line_edit.text()) path = str_to_path(self.line_edit.text())
@ -210,10 +188,7 @@ class PathEdit(QtWidgets.QWidget):
Emits the pathChanged Signal Emits the pathChanged Signal
:param path: The new path :param openlp.core.common.path.Path path: The new path
:type path: pathlib.Path
:return: None
:rtype: None :rtype: None
""" """
if self._path != path: if self._path != path:

View File

@ -30,6 +30,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate, is_macosx from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate, is_macosx
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
from openlp.core.lib.ui import add_welcome_page from openlp.core.lib.ui import add_welcome_page
from openlp.core.ui.lib.filedialog import FileDialog
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -278,37 +279,38 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
def get_file_name(self, title, editbox, setting_name, filters=''): def get_file_name(self, title, editbox, setting_name, filters=''):
""" """
Opens a QFileDialog and saves the filename to the given editbox. Opens a FileDialog and saves the filename to the given editbox.
:param title: The title of the dialog (unicode). :param str title: The title of the dialog.
:param editbox: An editbox (QLineEdit). :param QtWidgets.QLineEdit editbox: An QLineEdit.
:param setting_name: The place where to save the last opened directory. :param str setting_name: The place where to save the last opened directory.
:param filters: The file extension filters. It should contain the file description :param str filters: The file extension filters. It should contain the file description
as well as the file extension. For example:: as well as the file extension. For example::
'OpenLP 2 Databases (*.sqlite)' 'OpenLP 2 Databases (*.sqlite)'
:rtype: None
""" """
if filters: if filters:
filters += ';;' filters += ';;'
filters += '%s (*)' % UiStrings().AllFiles filters += '%s (*)' % UiStrings().AllFiles
filename, filter_used = QtWidgets.QFileDialog.getOpenFileName( file_path, filter_used = FileDialog.getOpenFileName(
self, title, os.path.dirname(Settings().value(self.plugin.settings_section + '/' + setting_name)), self, title, Settings().value(self.plugin.settings_section + '/' + setting_name), filters)
filters) if file_path:
if filename: editbox.setText(str(file_path))
editbox.setText(filename) Settings().setValue(self.plugin.settings_section + '/' + setting_name, file_path.parent)
Settings().setValue(self.plugin.settings_section + '/' + setting_name, filename)
def get_folder(self, title, editbox, setting_name): def get_folder(self, title, editbox, setting_name):
""" """
Opens a QFileDialog and saves the selected folder to the given editbox. Opens a FileDialog and saves the selected folder to the given editbox.
:param title: The title of the dialog (unicode). :param str title: The title of the dialog.
:param editbox: An editbox (QLineEdit). :param QtWidgets.QLineEdit editbox: An QLineEditbox.
:param setting_name: The place where to save the last opened directory. :param str setting_name: The place where to save the last opened directory.
:rtype: None
""" """
folder = QtWidgets.QFileDialog.getExistingDirectory( folder_path = FileDialog.getExistingDirectory(
self, title, Settings().value(self.plugin.settings_section + '/' + setting_name), self, title, Settings().value(self.plugin.settings_section + '/' + setting_name),
QtWidgets.QFileDialog.ShowDirsOnly) QtWidgets.QFileDialog.ShowDirsOnly)
if folder: if folder_path:
editbox.setText(folder) editbox.setText(str(folder_path))
Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder) Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder_path)

View File

@ -37,6 +37,7 @@ from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtGui, QtMultime
from openlp.core.common import AppLocation, Registry, RegistryProperties, OpenLPMixin, Settings, translate,\ from openlp.core.common import AppLocation, Registry, RegistryProperties, OpenLPMixin, Settings, translate,\
is_macosx, is_win is_macosx, is_win
from openlp.core.common.path import path_to_str
from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte
from openlp.core.lib.theme import BackgroundType from openlp.core.lib.theme import BackgroundType
from openlp.core.ui import HideMode, AlertLocation, DisplayControllerType from openlp.core.ui import HideMode, AlertLocation, DisplayControllerType
@ -259,7 +260,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
background_color.setNamedColor(Settings().value('core/logo background color')) background_color.setNamedColor(Settings().value('core/logo background color'))
if not background_color.isValid(): if not background_color.isValid():
background_color = QtCore.Qt.white background_color = QtCore.Qt.white
image_file = Settings().value('core/logo file') image_file = path_to_str(Settings().value('core/logo file'))
splash_image = QtGui.QImage(image_file) splash_image = QtGui.QImage(image_file)
self.initial_fame = QtGui.QImage( self.initial_fame = QtGui.QImage(
self.screen['size'].width(), self.screen['size'].width(),

View File

@ -30,7 +30,6 @@ import time
from datetime import datetime from datetime import datetime
from distutils import dir_util from distutils import dir_util
from distutils.errors import DistutilsFileError from distutils.errors import DistutilsFileError
from pathlib import Path
from tempfile import gettempdir from tempfile import gettempdir
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -40,6 +39,7 @@ from openlp.core.api.http import server
from openlp.core.common import Registry, RegistryProperties, AppLocation, LanguageManager, Settings, UiStrings, \ from openlp.core.common import Registry, RegistryProperties, AppLocation, LanguageManager, Settings, UiStrings, \
check_directory_exists, translate, is_win, is_macosx, add_actions check_directory_exists, translate, is_win, is_macosx, add_actions
from openlp.core.common.actions import ActionList, CategoryOrder from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.common.versionchecker import get_application_version from openlp.core.common.versionchecker import get_application_version
from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon
from openlp.core.lib.ui import create_action from openlp.core.lib.ui import create_action
@ -50,6 +50,7 @@ from openlp.core.ui.media import MediaController
from openlp.core.ui.printserviceform import PrintServiceForm from openlp.core.ui.printserviceform import PrintServiceForm
from openlp.core.ui.projector.manager import ProjectorManager from openlp.core.ui.projector.manager import ProjectorManager
from openlp.core.ui.lib.dockwidget import OpenLPDockWidget from openlp.core.ui.lib.dockwidget import OpenLPDockWidget
from openlp.core.ui.lib.filedialog import FileDialog
from openlp.core.ui.lib.mediadockmanager import MediaDockManager from openlp.core.ui.lib.mediadockmanager import MediaDockManager
@ -876,11 +877,12 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
shutil.copyfile(import_file_name, temp_config) shutil.copyfile(import_file_name, temp_config)
settings = Settings() settings = Settings()
import_settings = Settings(temp_config, Settings.IniFormat) import_settings = Settings(temp_config, Settings.IniFormat)
# Convert image files
log.info('hook upgrade_plugin_settings') log.info('hook upgrade_plugin_settings')
self.plugin_manager.hook_upgrade_plugin_settings(import_settings) self.plugin_manager.hook_upgrade_plugin_settings(import_settings)
# Remove/rename old settings to prepare the import. # Upgrade settings to prepare the import.
import_settings.remove_obsolete_settings() if import_settings.can_upgrade():
import_settings.upgrade_settings()
# Lets do a basic sanity check. If it contains this string we can assume it was created by OpenLP and so we'll # Lets do a basic sanity check. If it contains this string we can assume it was created by OpenLP and so we'll
# load what we can from it, and just silently ignore anything we don't recognise. # load what we can from it, and just silently ignore anything we don't recognise.
if import_settings.value('SettingsImport/type') != 'OpenLP_settings_export': if import_settings.value('SettingsImport/type') != 'OpenLP_settings_export':
@ -936,89 +938,17 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
""" """
Export settings to a .conf file in INI format Export settings to a .conf file in INI format
""" """
export_file_name, filter_used = QtWidgets.QFileDialog.getSaveFileName( export_file_path, filter_used = FileDialog.getSaveFileName(
self, self,
translate('OpenLP.MainWindow', 'Export Settings File'), translate('OpenLP.MainWindow', 'Export Settings File'),
'', None,
translate('OpenLP.MainWindow', 'OpenLP Settings (*.conf)')) translate('OpenLP.MainWindow', 'OpenLP Settings (*.conf)'))
if not export_file_name: if not export_file_path:
return return
# Make sure it's a .conf file. # Make sure it's a .conf file.
if not export_file_name.endswith('conf'): export_file_path = export_file_path.with_suffix('.conf')
export_file_name += '.conf'
temp_file = os.path.join(gettempdir(), 'openlp', 'exportConf.tmp')
self.save_settings() self.save_settings()
setting_sections = [] Settings().export(export_file_path)
# Add main sections.
setting_sections.extend([self.general_settings_section])
setting_sections.extend([self.advanced_settings_section])
setting_sections.extend([self.ui_settings_section])
setting_sections.extend([self.shortcuts_settings_section])
setting_sections.extend([self.service_manager_settings_section])
setting_sections.extend([self.themes_settings_section])
setting_sections.extend([self.display_tags_section])
# Add plugin sections.
for plugin in self.plugin_manager.plugins:
setting_sections.extend([plugin.name])
# Delete old files if found.
if os.path.exists(temp_file):
os.remove(temp_file)
if os.path.exists(export_file_name):
os.remove(export_file_name)
settings = Settings()
settings.remove(self.header_section)
# Get the settings.
keys = settings.allKeys()
export_settings = Settings(temp_file, Settings.IniFormat)
# Add a header section.
# This is to insure it's our conf file for import.
now = datetime.now()
application_version = get_application_version()
# Write INI format using Qsettings.
# Write our header.
export_settings.beginGroup(self.header_section)
export_settings.setValue('Make_Changes', 'At_Own_RISK')
export_settings.setValue('type', 'OpenLP_settings_export')
export_settings.setValue('file_date_created', now.strftime("%Y-%m-%d %H:%M"))
export_settings.setValue('version', application_version['full'])
export_settings.endGroup()
# Write all the sections and keys.
for section_key in keys:
# FIXME: We are conflicting with the standard "General" section.
if 'eneral' in section_key:
section_key = section_key.lower()
try:
key_value = settings.value(section_key)
except KeyError:
QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
translate('OpenLP.MainWindow', 'The key "{key}" does not have a default '
'value so it will be skipped in this '
'export.').format(key=section_key),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
key_value = None
if key_value is not None:
export_settings.setValue(section_key, key_value)
export_settings.sync()
# Temp CONF file has been written. Blanks in keys are now '%20'.
# Read the temp file and output the user's CONF file with blanks to
# make it more readable.
temp_conf = open(temp_file, 'r')
try:
export_conf = open(export_file_name, 'w')
for file_record in temp_conf:
# Get rid of any invalid entries.
if file_record.find('@Invalid()') == -1:
file_record = file_record.replace('%20', ' ')
export_conf.write(file_record)
temp_conf.close()
export_conf.close()
os.remove(temp_file)
except OSError as ose:
QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
translate('OpenLP.MainWindow',
'An error occurred while exporting the '
'settings: {err}').format(err=ose.strerror),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
def on_mode_default_item_clicked(self): def on_mode_default_item_clicked(self):
""" """
@ -1277,7 +1207,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
settings.remove('custom slide') settings.remove('custom slide')
settings.remove('service') settings.remove('service')
settings.beginGroup(self.general_settings_section) settings.beginGroup(self.general_settings_section)
self.recent_files = settings.value('recent files') self.recent_files = [path_to_str(file_path) for file_path in settings.value('recent files')]
settings.endGroup() settings.endGroup()
settings.beginGroup(self.ui_settings_section) settings.beginGroup(self.ui_settings_section)
self.move(settings.value('main window position')) self.move(settings.value('main window position'))
@ -1301,7 +1231,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
log.debug('Saving QSettings') log.debug('Saving QSettings')
settings = Settings() settings = Settings()
settings.beginGroup(self.general_settings_section) settings.beginGroup(self.general_settings_section)
settings.setValue('recent files', self.recent_files) settings.setValue('recent files', [str_to_path(file) for file in self.recent_files])
settings.endGroup() settings.endGroup()
settings.beginGroup(self.ui_settings_section) settings.beginGroup(self.ui_settings_section)
settings.setValue('main window position', self.pos()) settings.setValue('main window position', self.pos())
@ -1443,7 +1373,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
log.info('No data copy requested') log.info('No data copy requested')
# Change the location of data directory in config file. # Change the location of data directory in config file.
settings = QtCore.QSettings() settings = QtCore.QSettings()
settings.setValue('advanced/data path', self.new_data_path) settings.setValue('advanced/data path', Path(self.new_data_path))
# Check if the new data path is our default. # Check if the new data path is our default.
if self.new_data_path == str(AppLocation.get_directory(AppLocation.DataDir)): if self.new_data_path == str(AppLocation.get_directory(AppLocation.DataDir)):
settings.remove('advanced/data path') settings.remove('advanced/data path')

View File

@ -28,7 +28,6 @@ import os
import shutil import shutil
import zipfile import zipfile
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path
from tempfile import mkstemp from tempfile import mkstemp
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
@ -36,11 +35,13 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, ThemeLevel, OpenLPMixin, \ from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, ThemeLevel, OpenLPMixin, \
RegistryMixin, check_directory_exists, UiStrings, translate, split_filename, delete_file RegistryMixin, check_directory_exists, UiStrings, translate, split_filename, delete_file
from openlp.core.common.actions import ActionList, CategoryOrder from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.common.languagemanager import format_time
from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon
from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
from openlp.core.ui.lib import OpenLPToolbar from openlp.core.ui.lib import OpenLPToolbar
from openlp.core.common.languagemanager import format_time from openlp.core.ui.lib.filedialog import FileDialog
class ServiceManagerList(QtWidgets.QTreeWidget): class ServiceManagerList(QtWidgets.QTreeWidget):
@ -373,7 +374,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
""" """
self._file_name = str(file_name) self._file_name = str(file_name)
self.main_window.set_service_modified(self.is_modified(), self.short_file_name()) self.main_window.set_service_modified(self.is_modified(), self.short_file_name())
Settings().setValue('servicemanager/last file', file_name) Settings().setValue('servicemanager/last file', Path(file_name))
self._save_lite = self._file_name.endswith('.oszl') self._save_lite = self._file_name.endswith('.oszl')
def file_name(self): def file_name(self):
@ -435,18 +436,17 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
elif result == QtWidgets.QMessageBox.Save: elif result == QtWidgets.QMessageBox.Save:
self.decide_save_method() self.decide_save_method()
if not load_file: if not load_file:
file_name, filter_used = QtWidgets.QFileDialog.getOpenFileName( file_path, filter_used = FileDialog.getOpenFileName(
self.main_window, self.main_window,
translate('OpenLP.ServiceManager', 'Open File'), translate('OpenLP.ServiceManager', 'Open File'),
Settings().value(self.main_window.service_manager_settings_section + '/last directory'), Settings().value(self.main_window.service_manager_settings_section + '/last directory'),
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz *.oszl)')) translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz *.oszl)'))
if not file_name: if not file_path:
return False return False
else: else:
file_name = load_file file_path = str_to_path(load_file)
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
split_filename(file_name)[0]) self.load_file(str(file_path))
self.load_file(file_name)
def save_modified_service(self): def save_modified_service(self):
""" """
@ -477,7 +477,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
self.set_file_name('') self.set_file_name('')
self.service_id += 1 self.service_id += 1
self.set_modified(False) self.set_modified(False)
Settings().setValue('servicemanager/last file', '') Settings().setValue('servicemanager/last file', None)
self.plugin_manager.new_service_created() self.plugin_manager.new_service_created()
def create_basic_service(self): def create_basic_service(self):
@ -513,7 +513,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
base_name = os.path.splitext(file_name)[0] base_name = os.path.splitext(file_name)[0]
service_file_name = '{name}.osj'.format(name=base_name) service_file_name = '{name}.osj'.format(name=base_name)
self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name)) self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path) Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
service = self.create_basic_service() service = self.create_basic_service()
write_list = [] write_list = []
missing_list = [] missing_list = []
@ -634,7 +634,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
base_name = os.path.splitext(file_name)[0] base_name = os.path.splitext(file_name)[0]
service_file_name = '{name}.osj'.format(name=base_name) service_file_name = '{name}.osj'.format(name=base_name)
self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name)) self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path) Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
service = self.create_basic_service() service = self.create_basic_service()
self.application.set_busy_cursor() self.application.set_busy_cursor()
# Number of items + 1 to zip it # Number of items + 1 to zip it
@ -695,7 +695,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
default_file_name = format_time(default_pattern, local_time) default_file_name = format_time(default_pattern, local_time)
else: else:
default_file_name = '' default_file_name = ''
directory = Settings().value(self.main_window.service_manager_settings_section + '/last directory') directory = path_to_str(Settings().value(self.main_window.service_manager_settings_section + '/last directory'))
path = os.path.join(directory, default_file_name) path = os.path.join(directory, default_file_name)
# SaveAs from osz to oszl is not valid as the files will be deleted on exit which is not sensible or usable in # SaveAs from osz to oszl is not valid as the files will be deleted on exit which is not sensible or usable in
# the long term. # the long term.
@ -778,7 +778,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
delete_file(Path(p_file)) delete_file(Path(p_file))
self.main_window.add_recent_file(file_name) self.main_window.add_recent_file(file_name)
self.set_modified(False) self.set_modified(False)
Settings().setValue('servicemanager/last file', file_name) Settings().setValue('servicemanager/last file', Path(file_name))
else: else:
critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.')) critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
self.log_error('File contains no service data') self.log_error('File contains no service data')
@ -843,7 +843,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
Load the last service item from the service manager when the service was last closed. Can be blank if there was Load the last service item from the service manager when the service was last closed. Can be blank if there was
no service present. no service present.
""" """
file_name = Settings().value('servicemanager/last file') file_name = str_to_path(Settings().value('servicemanager/last file'))
if file_name: if file_name:
self.load_file(file_name) self.load_file(file_name)

View File

@ -24,12 +24,11 @@ The Theme wizard
""" """
import logging import logging
import os import os
from pathlib import Path
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui import ThemeLayoutForm from openlp.core.ui import ThemeLayoutForm

View File

@ -25,14 +25,13 @@ The Theme Manager manages adding, deleteing and modifying of themes.
import os import os
import zipfile import zipfile
import shutil import shutil
from pathlib import Path
from xml.etree.ElementTree import ElementTree, XML from xml.etree.ElementTree import ElementTree, XML
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \ from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \
UiStrings, check_directory_exists, translate, is_win, get_filesystem_encoding, delete_file UiStrings, check_directory_exists, translate, is_win, get_filesystem_encoding, delete_file
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \ from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \
check_item_selected, create_thumb, validate_thumb check_item_selected, create_thumb, validate_thumb
from openlp.core.lib.theme import Theme, BackgroundType from openlp.core.lib.theme import Theme, BackgroundType
@ -379,16 +378,16 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.')) critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.'))
return return
theme = item.data(QtCore.Qt.UserRole) theme = item.data(QtCore.Qt.UserRole)
path, filter_used = \ export_path, filter_used = \
QtWidgets.QFileDialog.getSaveFileName(self.main_window, FileDialog.getSaveFileName(self.main_window,
translate('OpenLP.ThemeManager', 'Save Theme - ({name})'). translate('OpenLP.ThemeManager', 'Save Theme - ({name})').
format(name=theme), format(name=theme),
Settings().value(self.settings_section + '/last directory export'), Settings().value(self.settings_section + '/last directory export'),
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)')) translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
self.application.set_busy_cursor() self.application.set_busy_cursor()
if path: if export_path:
Settings().setValue(self.settings_section + '/last directory export', path) Settings().setValue(self.settings_section + '/last directory export', export_path.parent)
if self._export_theme(path, theme): if self._export_theme(str(export_path), theme):
QtWidgets.QMessageBox.information(self, QtWidgets.QMessageBox.information(self,
translate('OpenLP.ThemeManager', 'Theme Exported'), translate('OpenLP.ThemeManager', 'Theme Exported'),
translate('OpenLP.ThemeManager', translate('OpenLP.ThemeManager',
@ -429,16 +428,15 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
file_paths, selected_filter = FileDialog.getOpenFileNames( file_paths, selected_filter = FileDialog.getOpenFileNames(
self, self,
translate('OpenLP.ThemeManager', 'Select Theme Import File'), translate('OpenLP.ThemeManager', 'Select Theme Import File'),
str_to_path(Settings().value(self.settings_section + '/last directory import')), Settings().value(self.settings_section + '/last directory import'),
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)')) translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
self.log_info('New Themes {file_paths}'.format(file_paths=file_paths)) self.log_info('New Themes {file_paths}'.format(file_paths=file_paths))
if not file_paths: if not file_paths:
return return
self.application.set_busy_cursor() self.application.set_busy_cursor()
for file_path in file_paths: for file_path in file_paths:
file_name = path_to_str(file_path) self.unzip_theme(path_to_str(file_path), self.path)
Settings().setValue(self.settings_section + '/last directory import', str(file_name)) Settings().setValue(self.settings_section + '/last directory import', file_path)
self.unzip_theme(file_name, self.path)
self.load_themes() self.load_themes()
self.application.set_normal_cursor() self.application.set_normal_cursor()

View File

@ -22,11 +22,10 @@
""" """
The Create/Edit theme wizard The Create/Edit theme wizard
""" """
from pathlib import Path
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import UiStrings, translate, is_macosx from openlp.core.common import UiStrings, translate, is_macosx
from openlp.core.common.path import Path
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets

View File

@ -59,7 +59,7 @@ __default_settings__ = {
'bibles/range separator': '', 'bibles/range separator': '',
'bibles/list separator': '', 'bibles/list separator': '',
'bibles/end separator': '', 'bibles/end separator': '',
'bibles/last directory import': '', 'bibles/last directory import': None,
'bibles/hide combined quick error': False, 'bibles/hide combined quick error': False,
'bibles/is search while typing enabled': True 'bibles/is search while typing enabled': True
} }

View File

@ -51,9 +51,9 @@ All CSV files are expected to use a comma (',') as the delimiter and double quot
""" """
import csv import csv
from collections import namedtuple from collections import namedtuple
from pathlib import Path
from openlp.core.common import get_file_encoding, translate from openlp.core.common import get_file_encoding, translate
from openlp.core.common.path import Path
from openlp.core.lib.exceptions import ValidationError from openlp.core.lib.exceptions import ValidationError
from openlp.plugins.bibles.lib.bibleimport import BibleImport from openlp.plugins.bibles.lib.bibleimport import BibleImport

View File

@ -21,10 +21,9 @@
############################################################################### ###############################################################################
import logging import logging
import os
from pathlib import Path
from openlp.core.common import AppLocation, OpenLPMixin, RegistryProperties, Settings, translate, delete_file, UiStrings from openlp.core.common import AppLocation, OpenLPMixin, RegistryProperties, Settings, translate, delete_file, UiStrings
from openlp.core.common.path import Path
from openlp.plugins.bibles.lib import LanguageSelection, parse_reference from openlp.plugins.bibles.lib import LanguageSelection, parse_reference
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
from .importers.csvbible import CSVBible from .importers.csvbible import CSVBible
@ -306,13 +305,10 @@ class BibleManager(OpenLPMixin, RegistryProperties):
""" """
Does a verse search for the given bible and text. Does a verse search for the given bible and text.
:param bible: The bible to search :param str bible: The bible to search
:type bible: str :param str text: The text to search for
:param text: The text to search for
:type text: str
:return: The search results if valid, or None if the search is invalid. :return: The search results if valid, or None if the search is invalid.
:rtype: None, list :rtype: None | list
""" """
log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text)) log.debug('BibleManager.verse_search("{bible}", "{text}")'.format(bible=bible, text=text))
if not text: if not text:

View File

@ -465,8 +465,7 @@ class BibleMediaItem(MediaManagerItem):
""" """
Show the selected tab and set focus to it Show the selected tab and set focus to it
:param index: The tab selected :param int index: The tab selected
:type index: int
:return: None :return: None
""" """
if index == SearchTabs.Search or index == SearchTabs.Select: if index == SearchTabs.Search or index == SearchTabs.Select:
@ -483,7 +482,7 @@ class BibleMediaItem(MediaManagerItem):
Update list_widget with the contents of the selected list Update list_widget with the contents of the selected list
:param index: The index of the tab that has been changed to. (int) :param index: The index of the tab that has been changed to. (int)
:return: None :rtype: None
""" """
if index == ResultsTab.Saved: if index == ResultsTab.Saved:
self.add_built_results_to_list_widget(self.saved_results) self.add_built_results_to_list_widget(self.saved_results)

View File

@ -71,14 +71,6 @@ class ImagePlugin(Plugin):
'provided by the theme.') 'provided by the theme.')
return about_text return about_text
def upgrade_settings(self, settings):
"""
Upgrade the settings of this plugin.
:param settings: The Settings object containing the old settings.
"""
pass
def set_plugin_text_strings(self): def set_plugin_text_strings(self):
""" """
Called to define all translatable texts of the plugin. Called to define all translatable texts of the plugin.

View File

@ -22,12 +22,12 @@
import logging import logging
import os import os
from pathlib import Path
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists, translate, \ from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists, translate, \
delete_file, get_images_filter delete_file, get_images_filter
from openlp.core.common.path import Path
from openlp.core.lib import ItemCapabilities, MediaManagerItem, ServiceItemContext, StringContent, build_icon, \ from openlp.core.lib import ItemCapabilities, MediaManagerItem, ServiceItemContext, StringContent, build_icon, \
check_item_selected, create_thumb, validate_thumb check_item_selected, create_thumb, validate_thumb
from openlp.core.lib.ui import create_widget_action, critical_error_message_box from openlp.core.lib.ui import create_widget_action, critical_error_message_box
@ -390,7 +390,7 @@ class ImageMediaItem(MediaManagerItem):
self.application.set_normal_cursor() self.application.set_normal_cursor()
self.load_list(files, target_group) self.load_list(files, target_group)
last_dir = os.path.split(files[0])[0] last_dir = os.path.split(files[0])[0]
Settings().setValue(self.settings_section + '/last directory', last_dir) Settings().setValue(self.settings_section + '/last directory', Path(last_dir))
def load_list(self, images, target_group=None, initial_load=False): def load_list(self, images, target_group=None, initial_load=False):
""" """

View File

@ -22,12 +22,12 @@
import logging import logging
import os import os
from pathlib import Path
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, UiStrings,\ from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, UiStrings,\
translate translate
from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \ from openlp.core.lib import ItemCapabilities, MediaManagerItem, MediaType, ServiceItem, ServiceItemContext, \
build_icon, check_item_selected build_icon, check_item_selected
from openlp.core.lib.ui import create_widget_action, critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.lib.ui import create_widget_action, critical_error_message_box, create_horizontal_adjusting_combo_box
@ -303,7 +303,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
self.list_view.clear() self.list_view.clear()
self.service_path = os.path.join(str(AppLocation.get_section_data_path(self.settings_section)), 'thumbnails') self.service_path = os.path.join(str(AppLocation.get_section_data_path(self.settings_section)), 'thumbnails')
check_directory_exists(Path(self.service_path)) check_directory_exists(Path(self.service_path))
self.load_list(Settings().value(self.settings_section + '/media files')) self.load_list([path_to_str(file) for file in Settings().value(self.settings_section + '/media files')])
self.rebuild_players() self.rebuild_players()
def rebuild_players(self): def rebuild_players(self):
@ -401,14 +401,14 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
:param media_type: Type to get, defaults to audio. :param media_type: Type to get, defaults to audio.
:return: The media list :return: The media list
""" """
media = Settings().value(self.settings_section + '/media files') media_file_paths = Settings().value(self.settings_section + '/media files')
media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1])) media_file_paths.sort(key=lambda file_path: get_locale_key(file_path.name))
if media_type == MediaType.Audio: if media_type == MediaType.Audio:
extension = self.media_controller.audio_extensions_list extension = self.media_controller.audio_extensions_list
else: else:
extension = self.media_controller.video_extensions_list extension = self.media_controller.video_extensions_list
extension = [x[1:] for x in extension] extension = [x[1:] for x in extension]
media = [x for x in media if os.path.splitext(x)[1] in extension] media = [x for x in media_file_paths if x.suffix in extension]
return media return media
def search(self, string, show_error): def search(self, string, show_error):
@ -419,13 +419,12 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
:param show_error: Should the error be shown (True) :param show_error: Should the error be shown (True)
:return: The search result. :return: The search result.
""" """
files = Settings().value(self.settings_section + '/media files')
results = [] results = []
string = string.lower() string = string.lower()
for file in files: for file_path in Settings().value(self.settings_section + '/media files'):
filename = os.path.split(str(file))[1] file_name = file_path.name
if filename.lower().find(string) > -1: if file_name.lower().find(string) > -1:
results.append([file, filename]) results.append([str(file_path), file_name])
return results return results
def on_load_optical(self): def on_load_optical(self):
@ -446,13 +445,13 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
:param optical: The clip to add. :param optical: The clip to add.
""" """
full_list = self.get_file_list() file_paths = self.get_file_list()
# If the clip already is in the media list it isn't added and an error message is displayed. # If the clip already is in the media list it isn't added and an error message is displayed.
if optical in full_list: if optical in file_paths:
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Mediaclip already saved'), critical_error_message_box(translate('MediaPlugin.MediaItem', 'Mediaclip already saved'),
translate('MediaPlugin.MediaItem', 'This mediaclip has already been saved')) translate('MediaPlugin.MediaItem', 'This mediaclip has already been saved'))
return return
# Append the optical string to the media list # Append the optical string to the media list
full_list.append(optical) file_paths.append(optical)
self.load_list([optical]) self.load_list([optical])
Settings().setValue(self.settings_section + '/media files', self.get_file_list()) Settings().setValue(self.settings_section + '/media files', file_paths)

View File

@ -26,12 +26,11 @@ The Media plugin
import logging import logging
import os import os
import re import re
from pathlib import Path
from PyQt5 import QtCore from PyQt5 import QtCore
from openlp.core.api.http import register_endpoint from openlp.core.api.http import register_endpoint
from openlp.core.common import AppLocation, translate, check_binary_exists from openlp.core.common import AppLocation, translate, check_binary_exists
from openlp.core.common.path import Path
from openlp.core.lib import Plugin, StringContent, build_icon from openlp.core.lib import Plugin, StringContent, build_icon
from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
from openlp.plugins.media.lib import MediaMediaItem, MediaTab from openlp.plugins.media.lib import MediaMediaItem, MediaTab

View File

@ -34,9 +34,9 @@
import logging import logging
import os import os
import time import time
from pathlib import Path
from openlp.core.common import is_win, Registry, get_uno_command, get_uno_instance, delete_file from openlp.core.common import is_win, Registry, delete_file
from openlp.core.common.path import Path
if is_win(): if is_win():
from win32com.client import Dispatch from win32com.client import Dispatch

View File

@ -26,10 +26,11 @@ import os
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, Settings, UiStrings, translate from openlp.core.common import Registry, Settings, UiStrings, translate
from openlp.core.common.languagemanager import get_locale_key
from openlp.core.common.path import path_to_str
from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext,\ from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext,\
build_icon, check_item_selected, create_thumb, validate_thumb build_icon, check_item_selected, create_thumb, validate_thumb
from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
from openlp.core.common.languagemanager import get_locale_key
from openlp.plugins.presentations.lib import MessageListener from openlp.plugins.presentations.lib import MessageListener
from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
@ -126,8 +127,8 @@ class PresentationMediaItem(MediaManagerItem):
Populate the media manager tab Populate the media manager tab
""" """
self.list_view.setIconSize(QtCore.QSize(88, 50)) self.list_view.setIconSize(QtCore.QSize(88, 50))
files = Settings().value(self.settings_section + '/presentations files') file_paths = Settings().value(self.settings_section + '/presentations files')
self.load_list(files, initial_load=True) self.load_list([path_to_str(file) for file in file_paths], initial_load=True)
self.populate_display_types() self.populate_display_types()
def populate_display_types(self): def populate_display_types(self):
@ -157,7 +158,7 @@ class PresentationMediaItem(MediaManagerItem):
existing files, and when the user adds new files via the media manager. existing files, and when the user adds new files via the media manager.
""" """
current_list = self.get_file_list() current_list = self.get_file_list()
titles = [os.path.split(file)[1] for file in current_list] titles = [file_path.name for file_path in current_list]
self.application.set_busy_cursor() self.application.set_busy_cursor()
if not initial_load: if not initial_load:
self.main_window.display_progress_bar(len(files)) self.main_window.display_progress_bar(len(files))
@ -410,11 +411,11 @@ class PresentationMediaItem(MediaManagerItem):
:param show_error: not used :param show_error: not used
:return: :return:
""" """
files = Settings().value(self.settings_section + '/presentations files') file_paths = Settings().value(self.settings_section + '/presentations files')
results = [] results = []
string = string.lower() string = string.lower()
for file in files: for file_path in file_paths:
filename = os.path.split(str(file))[1] file_name = file_path.name
if filename.lower().find(string) > -1: if file_name.lower().find(string) > -1:
results.append([file, filename]) results.append([path_to_str(file_path), file_name])
return results return results

View File

@ -23,12 +23,12 @@
import os import os
import logging import logging
import re import re
from pathlib import Path
from shutil import which from shutil import which
from subprocess import check_output, CalledProcessError from subprocess import check_output, CalledProcessError
from openlp.core.common import AppLocation, check_binary_exists from openlp.core.common import AppLocation, check_binary_exists
from openlp.core.common import Settings, is_win from openlp.core.common import Settings, is_win
from openlp.core.common.path import Path, path_to_str
from openlp.core.lib import ScreenList from openlp.core.lib import ScreenList
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
@ -113,7 +113,7 @@ class PdfController(PresentationController):
self.also_supports = [] self.also_supports = []
# Use the user defined program if given # Use the user defined program if given
if Settings().value('presentations/enable_pdf_program'): if Settings().value('presentations/enable_pdf_program'):
pdf_program = Settings().value('presentations/pdf_program') pdf_program = path_to_str(Settings().value('presentations/pdf_program'))
program_type = self.process_check_binary(pdf_program) program_type = self.process_check_binary(pdf_program)
if program_type == 'gs': if program_type == 'gs':
self.gsbin = pdf_program self.gsbin = pdf_program

View File

@ -23,11 +23,11 @@
import logging import logging
import os import os
import shutil import shutil
from pathlib import Path
from PyQt5 import QtCore from PyQt5 import QtCore
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, md5_hash from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, md5_hash
from openlp.core.common.path import Path
from openlp.core.lib import create_thumb, validate_thumb from openlp.core.lib import create_thumb, validate_thumb
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -155,9 +155,7 @@ class PresentationTab(SettingsTab):
enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program') enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program')
self.pdf_program_check_box.setChecked(enable_pdf_program) self.pdf_program_check_box.setChecked(enable_pdf_program)
self.program_path_edit.setEnabled(enable_pdf_program) self.program_path_edit.setEnabled(enable_pdf_program)
pdf_program = Settings().value(self.settings_section + '/pdf_program') self.program_path_edit.path = Settings().value(self.settings_section + '/pdf_program')
if pdf_program:
self.program_path_edit.path = str_to_path(pdf_program)
def save(self): def save(self):
""" """
@ -193,13 +191,13 @@ class PresentationTab(SettingsTab):
Settings().setValue(setting_key, self.ppt_window_check_box.checkState()) Settings().setValue(setting_key, self.ppt_window_check_box.checkState())
changed = True changed = True
# Save pdf-settings # Save pdf-settings
pdf_program = path_to_str(self.program_path_edit.path) pdf_program_path = self.program_path_edit.path
enable_pdf_program = self.pdf_program_check_box.checkState() enable_pdf_program = self.pdf_program_check_box.checkState()
# If the given program is blank disable using the program # If the given program is blank disable using the program
if pdf_program == '': if not pdf_program_path:
enable_pdf_program = 0 enable_pdf_program = 0
if pdf_program != Settings().value(self.settings_section + '/pdf_program'): if pdf_program_path != Settings().value(self.settings_section + '/pdf_program'):
Settings().setValue(self.settings_section + '/pdf_program', pdf_program) Settings().setValue(self.settings_section + '/pdf_program', pdf_program_path)
changed = True changed = True
if enable_pdf_program != Settings().value(self.settings_section + '/enable_pdf_program'): if enable_pdf_program != Settings().value(self.settings_section + '/enable_pdf_program'):
Settings().setValue(self.settings_section + '/enable_pdf_program', enable_pdf_program) Settings().setValue(self.settings_section + '/enable_pdf_program', enable_pdf_program)

View File

@ -39,7 +39,7 @@ log = logging.getLogger(__name__)
__default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked, __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked,
'presentations/enable_pdf_program': QtCore.Qt.Unchecked, 'presentations/enable_pdf_program': QtCore.Qt.Unchecked,
'presentations/pdf_program': '', 'presentations/pdf_program': None,
'presentations/Impress': QtCore.Qt.Checked, 'presentations/Impress': QtCore.Qt.Checked,
'presentations/Powerpoint': QtCore.Qt.Checked, 'presentations/Powerpoint': QtCore.Qt.Checked,
'presentations/Powerpoint Viewer': QtCore.Qt.Checked, 'presentations/Powerpoint Viewer': QtCore.Qt.Checked,

View File

@ -28,12 +28,11 @@ import logging
import re import re
import os import os
import shutil import shutil
from pathlib import Path
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStrings, check_directory_exists, translate from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStrings, check_directory_exists, translate
from openlp.core.common.path import path_to_str from openlp.core.common.path import Path, path_to_str
from openlp.core.lib import PluginStatus, MediaType, create_separated_list from openlp.core.lib import PluginStatus, MediaType, create_separated_list
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
from openlp.core.ui.lib.filedialog import FileDialog from openlp.core.ui.lib.filedialog import FileDialog

View File

@ -239,13 +239,11 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
filters += ';;' filters += ';;'
filters += '{text} (*)'.format(text=UiStrings().AllFiles) filters += '{text} (*)'.format(text=UiStrings().AllFiles)
file_paths, selected_filter = FileDialog.getOpenFileNames( file_paths, selected_filter = FileDialog.getOpenFileNames(
self, title, self, title, Settings().value(self.plugin.settings_section + '/last directory import'), filters)
str_to_path(Settings().value(self.plugin.settings_section + '/last directory import')), filters)
if file_paths: if file_paths:
file_names = [path_to_str(file_path) for file_path in file_paths] file_names = [path_to_str(file_path) for file_path in file_paths]
listbox.addItems(file_names) listbox.addItems(file_names)
Settings().setValue(self.plugin.settings_section + '/last directory import', Settings().setValue(self.plugin.settings_section + '/last directory import', file_paths[0].parent)
os.path.split(str(file_names[0]))[0])
def get_list_of_files(self, list_box): def get_list_of_files(self, list_box):
""" """
@ -363,14 +361,15 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
def on_error_save_to_button_clicked(self): def on_error_save_to_button_clicked(self):
""" """
Save the error report to a file. Save the error report to a file.
:rtype: None
""" """
filename, filter_used = QtWidgets.QFileDialog.getSaveFileName( file_path, filter_used = FileDialog.getSaveFileName(
self, Settings().value(self.plugin.settings_section + '/last directory import')) self, Settings().value(self.plugin.settings_section + '/last directory import'))
if not filename: if not file_path:
return return
report_file = codecs.open(filename, 'w', 'utf-8') with file_path.open('w', encoding='utf-8') as report_file:
report_file.write(self.error_report_text_edit.toPlainText()) report_file.write(self.error_report_text_edit.toPlainText())
report_file.close()
def add_file_select_item(self): def add_file_select_item(self):
""" """

View File

@ -27,11 +27,11 @@ import os
import re import re
import base64 import base64
import math import math
from pathlib import Path
from openlp.core.common import Settings, is_win, is_macosx, get_file_encoding
from openlp.core.common.path import Path
from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.importers.songimport import SongImport from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.core.common import Settings, is_win, is_macosx, get_file_encoding
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -24,11 +24,11 @@ import logging
import re import re
import shutil import shutil
import os import os
from pathlib import Path
from PyQt5 import QtCore from PyQt5 import QtCore
from openlp.core.common import Registry, AppLocation, check_directory_exists, translate from openlp.core.common import Registry, AppLocation, check_directory_exists, translate
from openlp.core.common.path import Path
from openlp.core.ui.lib.wizard import WizardStrings from openlp.core.ui.lib.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile

View File

@ -23,12 +23,12 @@
import logging import logging
import os import os
import shutil import shutil
from pathlib import Path
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from sqlalchemy.sql import and_, or_ from sqlalchemy.sql import and_, or_
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
from openlp.core.common.path import Path
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \ from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
check_item_selected, create_separated_list check_item_selected, create_separated_list
from openlp.core.lib.ui import create_widget_action from openlp.core.lib.ui import create_widget_action

View File

@ -25,11 +25,11 @@ format.
""" """
import logging import logging
import os import os
from pathlib import Path
from lxml import etree from lxml import etree
from openlp.core.common import RegistryProperties, check_directory_exists, translate, clean_filename from openlp.core.common import RegistryProperties, check_directory_exists, translate, clean_filename
from openlp.core.common.path import Path
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@ -66,8 +66,8 @@ __default_settings__ = {
'songs/display songbook': False, 'songs/display songbook': False,
'songs/display written by': True, 'songs/display written by': True,
'songs/display copyright symbol': False, 'songs/display copyright symbol': False,
'songs/last directory import': '', 'songs/last directory import': None,
'songs/last directory export': '', 'songs/last directory export': None,
'songs/songselect username': '', 'songs/songselect username': '',
'songs/songselect password': '', 'songs/songselect password': '',
'songs/songselect searches': '', 'songs/songselect searches': '',

View File

@ -22,13 +22,12 @@
import logging import logging
import os import os
from pathlib import Path
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from sqlalchemy.sql import and_ from sqlalchemy.sql import and_
from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.songusage.lib.db import SongUsageItem from openlp.plugins.songusage.lib.db import SongUsageItem
from .songusagedetaildialog import Ui_SongUsageDetailDialog from .songusagedetaildialog import Ui_SongUsageDetailDialog
@ -57,14 +56,16 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP
""" """
self.from_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/from date')) self.from_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/from date'))
self.to_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/to date')) self.to_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/to date'))
self.report_path_edit.path = str_to_path( self.report_path_edit.path = Settings().value(self.plugin.settings_section + '/last directory export')
Settings().value(self.plugin.settings_section + '/last directory export'))
def on_report_path_edit_path_changed(self, file_path): def on_report_path_edit_path_changed(self, file_path):
""" """
Triggered when the Directory selection button is clicked Called when the path in the `PathEdit` has changed
:param openlp.core.common.path.Path file_path: The new path.
:rtype: None
""" """
Settings().setValue(self.plugin.settings_section + '/last directory export', path_to_str(file_path)) Settings().setValue(self.plugin.settings_section + '/last directory export', file_path)
def accept(self): def accept(self):
""" """

View File

@ -50,7 +50,7 @@ __default_settings__ = {
'songusage/active': False, 'songusage/active': False,
'songusage/to date': QtCore.QDate(YEAR, 8, 31), 'songusage/to date': QtCore.QDate(YEAR, 8, 31),
'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1), 'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1),
'songusage/last directory export': '' 'songusage/last directory export': None
} }

View File

@ -22,13 +22,12 @@
""" """
Functional tests to test the AppLocation class and related methods. Functional tests to test the AppLocation class and related methods.
""" """
import copy
import os import os
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from openlp.core.common import AppLocation, get_frozen_path from openlp.core.common import AppLocation, get_frozen_path
from openlp.core.common.path import Path
FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3'] FILE_LIST = ['file1', 'file2', 'file3.txt', 'file4.txt', 'file5.mp3', 'file6.mp3']
@ -43,12 +42,14 @@ class TestAppLocation(TestCase):
""" """
with patch('openlp.core.common.applocation.Settings') as mocked_class, \ with patch('openlp.core.common.applocation.Settings') as mocked_class, \
patch('openlp.core.common.AppLocation.get_directory') as mocked_get_directory, \ patch('openlp.core.common.AppLocation.get_directory') as mocked_get_directory, \
patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists: patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists, \
patch('openlp.core.common.applocation.os') as mocked_os:
# GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory()
mocked_settings = mocked_class.return_value mocked_settings = mocked_class.return_value
mocked_settings.contains.return_value = False mocked_settings.contains.return_value = False
mocked_get_directory.return_value = Path('test', 'dir') mocked_get_directory.return_value = os.path.join('test', 'dir')
mocked_check_directory_exists.return_value = True mocked_check_directory_exists.return_value = True
mocked_os.path.normpath.return_value = os.path.join('test', 'dir')
# WHEN: we call AppLocation.get_data_path() # WHEN: we call AppLocation.get_data_path()
data_path = AppLocation.get_data_path() data_path = AppLocation.get_data_path()
@ -56,8 +57,8 @@ class TestAppLocation(TestCase):
# THEN: check that all the correct methods were called, and the result is correct # THEN: check that all the correct methods were called, and the result is correct
mocked_settings.contains.assert_called_with('advanced/data path') mocked_settings.contains.assert_called_with('advanced/data path')
mocked_get_directory.assert_called_with(AppLocation.DataDir) mocked_get_directory.assert_called_with(AppLocation.DataDir)
mocked_check_directory_exists.assert_called_with(Path('test', 'dir')) mocked_check_directory_exists.assert_called_with(os.path.join('test', 'dir'))
self.assertEqual(Path('test', 'dir'), data_path, 'Result should be "test/dir"') self.assertEqual(os.path.join('test', 'dir'), data_path, 'Result should be "test/dir"')
def test_get_data_path_with_custom_location(self): def test_get_data_path_with_custom_location(self):
""" """

View File

@ -22,13 +22,12 @@
""" """
Functional tests to test the AppLocation class and related methods. Functional tests to test the AppLocation class and related methods.
""" """
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, call, patch from unittest.mock import MagicMock, call, patch
from openlp.core import common
from openlp.core.common import check_directory_exists, clean_button_text, de_hump, extension_loader, is_macosx, \ from openlp.core.common import check_directory_exists, clean_button_text, de_hump, extension_loader, is_macosx, \
is_linux, is_win, path_to_module, trace_error_handler, translate is_linux, is_win, path_to_module, trace_error_handler, translate
from openlp.core.common.path import Path
class TestCommonFunctions(TestCase): class TestCommonFunctions(TestCase):

View File

@ -24,12 +24,12 @@ Functional tests to test the AppLocation class and related methods.
""" """
import os import os
from io import BytesIO from io import BytesIO
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, PropertyMock, call, patch from unittest.mock import MagicMock, PropertyMock, call, patch
from openlp.core.common import add_actions, clean_filename, delete_file, get_file_encoding, get_filesystem_encoding, \ from openlp.core.common import add_actions, clean_filename, delete_file, get_file_encoding, get_filesystem_encoding, \
get_uno_command, get_uno_instance, split_filename get_uno_command, get_uno_instance, split_filename
from openlp.core.common.path import Path
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin

View File

@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2017 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Package to test the openlp.core.common.json package.
"""
import json
from unittest import TestCase
from unittest.mock import patch
from openlp.core.common.path import Path
from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
class TestOpenLPJsonDecoder(TestCase):
"""
Test the OpenLPJsonDecoder class
"""
def test_object_hook_path_object(self):
"""
Test the object_hook method when called with a decoded Path JSON object
"""
# GIVEN: An instance of OpenLPJsonDecoder
instance = OpenLPJsonDecoder()
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
result = instance.object_hook({'__Path__': ['test', 'path']})
# THEN: A Path object should be returned
self.assertEqual(result, Path('test', 'path'))
def test_object_hook_non_path_object(self):
"""
Test the object_hook method when called with a decoded JSON object
"""
# GIVEN: An instance of OpenLPJsonDecoder
instance = OpenLPJsonDecoder()
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
with patch('openlp.core.common.json.Path') as mocked_path:
result = instance.object_hook({'key': 'value'})
# THEN: The object should be returned unchanged and a Path object should not have been initiated
self.assertEqual(result, {'key': 'value'})
self.assertFalse(mocked_path.called)
def test_json_decode(self):
"""
Test the OpenLPJsonDecoder when decoding a JSON string
"""
# GIVEN: A JSON encoded string
json_string = '[{"__Path__": ["test", "path1"]}, {"__Path__": ["test", "path2"]}]'
# WHEN: Decoding the string using the OpenLPJsonDecoder class
obj = json.loads(json_string, cls=OpenLPJsonDecoder)
# THEN: The object returned should be a python version of the JSON string
self.assertEqual(obj, [Path('test', 'path1'), Path('test', 'path2')])
class TestOpenLPJsonEncoder(TestCase):
"""
Test the OpenLPJsonEncoder class
"""
def test_default_path_object(self):
"""
Test the default method when called with a Path object
"""
# GIVEN: An instance of OpenLPJsonEncoder
instance = OpenLPJsonEncoder()
# WHEN: Calling the default method with a Path object
result = instance.default(Path('test', 'path'))
# THEN: A dictionary object that can be JSON encoded should be returned
self.assertEqual(result, {'__Path__': ('test', 'path')})
def test_default_non_path_object(self):
"""
Test the default method when called with a object other than a Path object
"""
with patch('openlp.core.common.json.JSONEncoder.default') as mocked_super_default:
# GIVEN: An instance of OpenLPJsonEncoder
instance = OpenLPJsonEncoder()
# WHEN: Calling the default method with a object other than a Path object
instance.default('invalid object')
# THEN: default method of the super class should have been called
mocked_super_default.assert_called_once_with('invalid object')
def test_json_encode(self):
"""
Test the OpenLPJsonDEncoder when encoding an object conatining Path objects
"""
# GIVEN: A list of Path objects
obj = [Path('test', 'path1'), Path('test', 'path2')]
# WHEN: Encoding the object using the OpenLPJsonEncoder class
json_string = json.dumps(obj, cls=OpenLPJsonEncoder)
# THEN: The JSON string return should be a representation of the object encoded
self.assertEqual(json_string, '[{"__Path__": ["test", "path1"]}, {"__Path__": ["test", "path2"]}]')

View File

@ -23,10 +23,9 @@
Package to test the openlp.core.common.path package. Package to test the openlp.core.common.path package.
""" """
import os import os
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
class TestPath(TestCase): class TestPath(TestCase):
@ -86,3 +85,54 @@ class TestPath(TestCase):
# THEN: `path_to_str` should return None # THEN: `path_to_str` should return None
self.assertEqual(result, None) self.assertEqual(result, None)
def test_path_encode_json(self):
"""
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
from JSON
"""
# GIVEN: A Path object from openlp.core.common.path
# WHEN: Calling encode_json, with a dictionary representation
path = Path.encode_json({'__Path__': ['path', 'to', 'fi.le']}, extra=1, args=2)
# THEN: A Path object should have been returned
self.assertEqual(path, Path('path', 'to', 'fi.le'))
def test_path_encode_json_base_path(self):
"""
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
from JSON when the base_path arg is supplied.
"""
# GIVEN: A Path object from openlp.core.common.path
# WHEN: Calling encode_json, with a dictionary representation
path = Path.encode_json({'__Path__': ['path', 'to', 'fi.le']}, base_path=Path('/base'))
# THEN: A Path object should have been returned with an absolute path
self.assertEqual(path, Path('/', 'base', 'path', 'to', 'fi.le'))
def test_path_json_object(self):
"""
Test that `Path.json_object` creates a JSON decode-able object from a Path object
"""
# GIVEN: A Path object from openlp.core.common.path
path = Path('/base', 'path', 'to', 'fi.le')
# WHEN: Calling json_object
obj = path.json_object(extra=1, args=2)
# THEN: A JSON decodable object should have been returned.
self.assertEqual(obj, {'__Path__': ('/', 'base', 'path', 'to', 'fi.le')})
def test_path_json_object_base_path(self):
"""
Test that `Path.json_object` creates a JSON decode-able object from a Path object, that is relative to the
base_path
"""
# GIVEN: A Path object from openlp.core.common.path
path = Path('/base', 'path', 'to', 'fi.le')
# WHEN: Calling json_object with a base_path
obj = path.json_object(base_path=Path('/', 'base'))
# THEN: A JSON decodable object should have been returned.
self.assertEqual(obj, {'__Path__': ('path', 'to', 'fi.le')})

View File

@ -26,7 +26,6 @@ from unittest import TestCase
from unittest.mock import patch from unittest.mock import patch
from openlp.core.common import Settings from openlp.core.common import Settings
from openlp.core.common.settings import recent_files_conv
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
@ -48,25 +47,6 @@ class TestSettings(TestCase, TestMixin):
""" """
self.destroy_settings() self.destroy_settings()
def test_recent_files_conv(self):
"""
Test that recent_files_conv, converts various possible types of values correctly.
"""
# GIVEN: A list of possible value types and the expected results
possible_values = [(['multiple', 'values'], ['multiple', 'values']),
(['single value'], ['single value']),
('string value', ['string value']),
(b'bytes value', ['bytes value']),
([], []),
(None, [])]
# WHEN: Calling recent_files_conv with the possible values
for value, expected_result in possible_values:
actual_result = recent_files_conv(value)
# THEN: The actual result should be the same as the expected result
self.assertEqual(actual_result, expected_result)
def test_settings_basic(self): def test_settings_basic(self):
""" """
Test the Settings creation and its default usage Test the Settings creation and its default usage

View File

@ -22,9 +22,7 @@
""" """
Package to test the openlp.core.lib package. Package to test the openlp.core.lib package.
""" """
import os
import shutil import shutil
from pathlib import Path
from tempfile import mkdtemp from tempfile import mkdtemp
from unittest import TestCase from unittest import TestCase
@ -34,6 +32,7 @@ from sqlalchemy.pool import NullPool
from sqlalchemy.orm.scoping import ScopedSession from sqlalchemy.orm.scoping import ScopedSession
from sqlalchemy import MetaData from sqlalchemy import MetaData
from openlp.core.common.path import Path
from openlp.core.lib.db import init_db, get_upgrade_op, delete_database, upgrade_db from openlp.core.lib.db import init_db, get_upgrade_op, delete_database, upgrade_db
from openlp.core.lib.projector import upgrade as pjlink_upgrade from openlp.core.lib.projector import upgrade as pjlink_upgrade

View File

@ -24,12 +24,12 @@ Package to test the openlp.core.lib package.
""" """
import os import os
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
from openlp.core.common.path import Path
from openlp.core.lib import FormattingTags, build_icon, check_item_selected, clean_tags, compare_chord_lyric, \ from openlp.core.lib import FormattingTags, build_icon, check_item_selected, clean_tags, compare_chord_lyric, \
create_separated_list, create_thumb, expand_chords, expand_chords_for_printing, expand_tags, find_formatting_tags, \ create_separated_list, create_thumb, expand_chords, expand_chords_for_printing, expand_tags, find_formatting_tags, \
get_text_file_string, image_to_byte, replace_params, resize_image, str_to_bool, validate_thumb get_text_file_string, image_to_byte, replace_params, resize_image, str_to_bool, validate_thumb

View File

@ -23,10 +23,9 @@
Package to test the openlp.core.lib.path package. Package to test the openlp.core.lib.path package.
""" """
import os import os
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from openlp.core.lib.path import path_to_str, str_to_path from openlp.core.common.path import Path, path_to_str, str_to_path
class TestPath(TestCase): class TestPath(TestCase):

View File

@ -29,6 +29,7 @@ from unittest import TestCase
from unittest.mock import mock_open, patch from unittest.mock import mock_open, patch
from openlp.core.common import Registry from openlp.core.common import Registry
from openlp.core.common.path import Path
from openlp.core.ui import exceptionform from openlp.core.ui import exceptionform
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
@ -154,7 +155,7 @@ class TestExceptionForm(TestMixin, TestCase):
# THEN: Verify strings were formatted properly # THEN: Verify strings were formatted properly
mocked_add_query_item.assert_called_with('body', MAIL_ITEM_TEXT) mocked_add_query_item.assert_called_with('body', MAIL_ITEM_TEXT)
@patch("openlp.core.ui.exceptionform.QtWidgets.QFileDialog.getSaveFileName") @patch("openlp.core.ui.exceptionform.FileDialog.getSaveFileName")
@patch("openlp.core.ui.exceptionform.Qt") @patch("openlp.core.ui.exceptionform.Qt")
def test_on_save_report_button_clicked(self, def test_on_save_report_button_clicked(self,
mocked_qt, mocked_qt,
@ -181,7 +182,7 @@ class TestExceptionForm(TestMixin, TestCase):
mocked_qt.PYQT_VERSION_STR = 'PyQt5 Test' mocked_qt.PYQT_VERSION_STR = 'PyQt5 Test'
mocked_is_linux.return_value = False mocked_is_linux.return_value = False
mocked_application_version.return_value = 'Trunk Test' mocked_application_version.return_value = 'Trunk Test'
mocked_save_filename.return_value = ['testfile.txt', ] mocked_save_filename.return_value = (Path('testfile.txt'), 'filter')
test_form = exceptionform.ExceptionForm() test_form = exceptionform.ExceptionForm()
test_form.file_attachment = None test_form.file_attachment = None

View File

@ -25,11 +25,11 @@ Package to test the openlp.core.ui.firsttimeform package.
import os import os
import tempfile import tempfile
import urllib import urllib
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from openlp.core.common import Registry from openlp.core.common import Registry
from openlp.core.common.path import Path
from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.firsttimeform import FirstTimeForm
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin

View File

@ -22,10 +22,10 @@
""" """
Package to test the openlp.core.ui.themeform package. Package to test the openlp.core.ui.themeform package.
""" """
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from openlp.core.common.path import Path
from openlp.core.ui import ThemeForm from openlp.core.ui import ThemeForm

View File

@ -1,10 +1,10 @@
import os import os
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch from unittest.mock import patch
from pathlib import Path
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from openlp.core.common.path import Path
from openlp.core.ui.lib.filedialog import FileDialog from openlp.core.ui.lib.filedialog import FileDialog

View File

@ -23,10 +23,10 @@
This module contains tests for the openlp.core.ui.lib.pathedit module This module contains tests for the openlp.core.ui.lib.pathedit module
""" """
import os import os
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, PropertyMock, patch from unittest.mock import MagicMock, PropertyMock, patch
from openlp.core.common.path import Path
from openlp.core.ui.lib import PathEdit, PathType from openlp.core.ui.lib import PathEdit, PathType
from openlp.core.ui.lib.filedialog import FileDialog from openlp.core.ui.lib.filedialog import FileDialog

View File

@ -22,10 +22,10 @@
""" """
This module contains tests for the manager submodule of the Bibles plugin. This module contains tests for the manager submodule of the Bibles plugin.
""" """
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from openlp.core.common.path import Path
from openlp.plugins.bibles.lib.manager import BibleManager from openlp.plugins.bibles.lib.manager import BibleManager

View File

@ -28,6 +28,7 @@ from unittest.mock import ANY, MagicMock, patch
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry from openlp.core.common import Registry
from openlp.core.common.path import Path
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
from openlp.plugins.images.lib.mediaitem import ImageMediaItem from openlp.plugins.images.lib.mediaitem import ImageMediaItem
@ -65,7 +66,7 @@ class TestImageMediaItem(TestCase):
# THEN: load_list should have been called with the file list and None, # THEN: load_list should have been called with the file list and None,
# the directory should have been saved to the settings # the directory should have been saved to the settings
mocked_load_list.assert_called_once_with(file_list, None) mocked_load_list.assert_called_once_with(file_list, None)
mocked_settings().setValue.assert_called_once_with(ANY, '/path1') mocked_settings().setValue.assert_called_once_with(ANY, Path('/', 'path1'))
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_list') @patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_list')
@patch('openlp.plugins.images.lib.mediaitem.Settings') @patch('openlp.plugins.images.lib.mediaitem.Settings')
@ -82,7 +83,7 @@ class TestImageMediaItem(TestCase):
# THEN: load_list should have been called with the file list and the group name, # THEN: load_list should have been called with the file list and the group name,
# the directory should have been saved to the settings # the directory should have been saved to the settings
mocked_load_list.assert_called_once_with(file_list, 'group') mocked_load_list.assert_called_once_with(file_list, 'group')
mocked_settings().setValue.assert_called_once_with(ANY, '/path1') mocked_settings().setValue.assert_called_once_with(ANY, Path('/', 'path1'))
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') @patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list')
def test_save_new_images_list_empty_list(self, mocked_load_full_list): def test_save_new_images_list_empty_list(self, mocked_load_full_list):

View File

@ -28,6 +28,7 @@ from unittest.mock import MagicMock, patch
from PyQt5 import QtCore from PyQt5 import QtCore
from openlp.core import Settings from openlp.core import Settings
from openlp.core.common.path import Path
from openlp.plugins.media.lib.mediaitem import MediaMediaItem from openlp.plugins.media.lib.mediaitem import MediaMediaItem
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
@ -66,7 +67,7 @@ class MediaItemTest(TestCase, TestMixin):
Media Remote Search Successful find Media Remote Search Successful find
""" """
# GIVEN: The Mediaitem set up a list of media # GIVEN: The Mediaitem set up a list of media
Settings().setValue(self.media_item.settings_section + '/media files', ['test.mp3', 'test.mp4']) Settings().setValue(self.media_item.settings_section + '/media files', [Path('test.mp3'), Path('test.mp4')])
# WHEN: Retrieving the test file # WHEN: Retrieving the test file
result = self.media_item.search('test.mp4', False) result = self.media_item.search('test.mp4', False)
# THEN: a file should be found # THEN: a file should be found
@ -77,7 +78,7 @@ class MediaItemTest(TestCase, TestMixin):
Media Remote Search not find Media Remote Search not find
""" """
# GIVEN: The Mediaitem set up a list of media # GIVEN: The Mediaitem set up a list of media
Settings().setValue(self.media_item.settings_section + '/media files', ['test.mp3', 'test.mp4']) Settings().setValue(self.media_item.settings_section + '/media files', [Path('test.mp3'), Path('test.mp4')])
# WHEN: Retrieving the test file # WHEN: Retrieving the test file
result = self.media_item.search('test.mpx', False) result = self.media_item.search('test.mpx', False)
# THEN: a file should be found # THEN: a file should be found

View File

@ -24,10 +24,10 @@ Functional tests to test the PresentationController and PresentationDocument
classes and related methods. classes and related methods.
""" """
import os import os
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, mock_open, patch from unittest.mock import MagicMock, mock_open, patch
from openlp.core.common.path import Path
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
FOLDER_TO_PATCH = 'openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder' FOLDER_TO_PATCH = 'openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder'

View File

@ -22,10 +22,10 @@
""" """
Functional tests to test the AppLocation class and related methods. Functional tests to test the AppLocation class and related methods.
""" """
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from openlp.core.common import is_not_image_file from openlp.core.common import is_not_image_file
from openlp.core.common.path import Path
from tests.utils.constants import TEST_RESOURCES_PATH from tests.utils.constants import TEST_RESOURCES_PATH
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin

View File

@ -32,6 +32,7 @@ from unittest.mock import MagicMock, patch
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from openlp.core.common import Registry, Settings from openlp.core.common import Registry, Settings
from openlp.core.common.path import Path
from openlp.core.lib.pluginmanager import PluginManager from openlp.core.lib.pluginmanager import PluginManager
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
@ -48,7 +49,7 @@ class TestPluginManager(TestCase, TestMixin):
""" """
self.setup_application() self.setup_application()
self.build_settings() self.build_settings()
self.temp_dir = mkdtemp('openlp') self.temp_dir = Path(mkdtemp('openlp'))
Settings().setValue('advanced/data path', self.temp_dir) Settings().setValue('advanced/data path', self.temp_dir)
Registry.create() Registry.create()
Registry().register('service_list', MagicMock()) Registry().register('service_list', MagicMock())
@ -62,7 +63,7 @@ class TestPluginManager(TestCase, TestMixin):
# On windows we need to manually garbage collect to close sqlalchemy files # On windows we need to manually garbage collect to close sqlalchemy files
# to avoid errors when temporary files are deleted. # to avoid errors when temporary files are deleted.
gc.collect() gc.collect()
shutil.rmtree(self.temp_dir) shutil.rmtree(str(self.temp_dir))
@patch('openlp.plugins.songusage.lib.db.init_schema') @patch('openlp.plugins.songusage.lib.db.init_schema')
@patch('openlp.plugins.songs.lib.db.init_schema') @patch('openlp.plugins.songs.lib.db.init_schema')