Update the path edit component to use the pathlib module. Add a few utility methods

This commit is contained in:
Philip Ridout 2017-08-04 18:40:57 +01:00
parent cb451b88af
commit 03bcc194ea
17 changed files with 650 additions and 110 deletions

View File

@ -0,0 +1,61 @@
# -*- 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

@ -608,6 +608,41 @@ def create_separated_list(string_list):
return list_to_string return list_to_string
def replace_params(args, kwargs, params):
"""
Apply a transformation function to the specified args or kwargs
:param args: Positional arguments
:type args: (,)
:param kwargs: Key Word arguments
:type kwargs: dict
: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
:rtype: (tuple, dict)
Usage:
Take a method with the following signature, and assume we which to apply the str function to arg2:
def method(arg1=None, arg2=None, arg3=None)
As arg2 can be specified postitionally as the second argument (1 with a zero index) or as a keyword, the we
would call this function as follows:
replace_params(args, kwargs, ((1, 'arg2', str),))
"""
args = list(args)
for position, key_word, transform in params:
if len(args) > position:
args[position] = transform(args[position])
elif key_word in kwargs:
kwargs[key_word] = transform(kwargs[key_word])
return tuple(args), kwargs
from .exceptions import ValidationError from .exceptions import ValidationError
from .filedialog import FileDialog from .filedialog import FileDialog
from .screen import ScreenList from .screen import ScreenList

View File

@ -30,6 +30,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import AppLocation, Settings, SlideLimits, UiStrings, translate from openlp.core.common import AppLocation, Settings, SlideLimits, UiStrings, translate
from openlp.core.common.languagemanager import format_time from openlp.core.common.languagemanager import format_time
from openlp.core.common.path import path_to_str
from openlp.core.lib import SettingsTab, build_icon from openlp.core.lib import SettingsTab, build_icon
from openlp.core.ui.lib import PathEdit, PathType from openlp.core.ui.lib import PathEdit, PathType
@ -156,7 +157,7 @@ class AdvancedTab(SettingsTab):
self.data_directory_new_label = QtWidgets.QLabel(self.data_directory_group_box) self.data_directory_new_label = QtWidgets.QLabel(self.data_directory_group_box)
self.data_directory_new_label.setObjectName('data_directory_current_label') self.data_directory_new_label.setObjectName('data_directory_current_label')
self.data_directory_path_edit = PathEdit(self.data_directory_group_box, path_type=PathType.Directories, self.data_directory_path_edit = PathEdit(self.data_directory_group_box, path_type=PathType.Directories,
default_path=str(AppLocation.get_directory(AppLocation.DataDir))) default_path=AppLocation.get_directory(AppLocation.DataDir))
self.data_directory_layout.addRow(self.data_directory_new_label, self.data_directory_path_edit) self.data_directory_layout.addRow(self.data_directory_new_label, self.data_directory_path_edit)
self.new_data_directory_has_files_label = QtWidgets.QLabel(self.data_directory_group_box) self.new_data_directory_has_files_label = QtWidgets.QLabel(self.data_directory_group_box)
self.new_data_directory_has_files_label.setObjectName('new_data_directory_has_files_label') self.new_data_directory_has_files_label.setObjectName('new_data_directory_has_files_label')
@ -373,7 +374,7 @@ class AdvancedTab(SettingsTab):
self.new_data_directory_has_files_label.hide() self.new_data_directory_has_files_label.hide()
self.data_directory_cancel_button.hide() self.data_directory_cancel_button.hide()
# Since data location can be changed, make sure the path is present. # Since data location can be changed, make sure the path is present.
self.data_directory_path_edit.path = str(AppLocation.get_data_path()) self.data_directory_path_edit.path = AppLocation.get_data_path()
# Don't allow data directory move if running portable. # Don't allow data directory move if running portable.
if settings.value('advanced/is portable'): if settings.value('advanced/is portable'):
self.data_directory_group_box.hide() self.data_directory_group_box.hide()
@ -497,12 +498,12 @@ class AdvancedTab(SettingsTab):
'closed.').format(path=new_data_path), 'closed.').format(path=new_data_path),
defaultButton=QtWidgets.QMessageBox.No) defaultButton=QtWidgets.QMessageBox.No)
if answer != QtWidgets.QMessageBox.Yes: if answer != QtWidgets.QMessageBox.Yes:
self.data_directory_path_edit.path = str(AppLocation.get_data_path()) self.data_directory_path_edit.path = AppLocation.get_data_path()
return return
# Check if data already exists here. # Check if data already exists here.
self.check_data_overwrite(new_data_path) self.check_data_overwrite(path_to_str(new_data_path))
# Save the new location. # Save the new location.
self.main_window.set_new_data_path(new_data_path) self.main_window.set_new_data_path(path_to_str(new_data_path))
self.data_directory_cancel_button.show() self.data_directory_cancel_button.show()
def on_data_directory_copy_check_box_toggled(self): def on_data_directory_copy_check_box_toggled(self):
@ -550,7 +551,7 @@ class AdvancedTab(SettingsTab):
""" """
Cancel the data directory location change Cancel the data directory location change
""" """
self.data_directory_path_edit.path = str(AppLocation.get_data_path()) self.data_directory_path_edit.path = AppLocation.get_data_path()
self.data_directory_copy_check_box.setChecked(False) self.data_directory_copy_check_box.setChecked(False)
self.main_window.set_new_data_path(None) self.main_window.set_new_data_path(None)
self.main_window.set_copy_data(False) self.main_window.set_copy_data(False)

View File

@ -23,6 +23,7 @@
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
@ -172,7 +173,8 @@ class GeneralTab(SettingsTab):
self.logo_layout.setObjectName('logo_layout') self.logo_layout.setObjectName('logo_layout')
self.logo_file_label = QtWidgets.QLabel(self.logo_group_box) self.logo_file_label = QtWidgets.QLabel(self.logo_group_box)
self.logo_file_label.setObjectName('logo_file_label') self.logo_file_label.setObjectName('logo_file_label')
self.logo_file_path_edit = PathEdit(self.logo_group_box, default_path=':/graphics/openlp-splash-screen.png') self.logo_file_path_edit = PathEdit(self.logo_group_box,
default_path=Path(':/graphics/openlp-splash-screen.png'))
self.logo_layout.addRow(self.logo_file_label, self.logo_file_path_edit) self.logo_layout.addRow(self.logo_file_label, self.logo_file_path_edit)
self.logo_color_label = QtWidgets.QLabel(self.logo_group_box) self.logo_color_label = QtWidgets.QLabel(self.logo_group_box)
self.logo_color_label.setObjectName('logo_color_label') self.logo_color_label.setObjectName('logo_color_label')
@ -266,7 +268,7 @@ class GeneralTab(SettingsTab):
self.audio_group_box.setTitle(translate('OpenLP.GeneralTab', 'Background Audio')) self.audio_group_box.setTitle(translate('OpenLP.GeneralTab', 'Background Audio'))
self.start_paused_check_box.setText(translate('OpenLP.GeneralTab', 'Start background audio paused')) self.start_paused_check_box.setText(translate('OpenLP.GeneralTab', 'Start background audio paused'))
self.repeat_list_check_box.setText(translate('OpenLP.GeneralTab', 'Repeat track list')) self.repeat_list_check_box.setText(translate('OpenLP.GeneralTab', 'Repeat track list'))
self.logo_file_path_edit.dialog_caption = dialog_caption = translate('OpenLP.AdvancedTab', 'Select Logo File') self.logo_file_path_edit.dialog_caption = translate('OpenLP.AdvancedTab', 'Select Logo File')
self.logo_file_path_edit.filters = '{text};;{names} (*)'.format( self.logo_file_path_edit.filters = '{text};;{names} (*)'.format(
text=get_images_filter(), names=UiStrings().AllFiles) text=get_images_filter(), names=UiStrings().AllFiles)
@ -325,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', self.logo_file_path_edit.path) settings.setValue('logo file', str(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

@ -0,0 +1,118 @@
# -*- 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 #
###############################################################################
""" Patch the QFileDialog so it accepts and returns Path objects"""
from functools import wraps
from pathlib import Path
from PyQt5 import QtWidgets
from openlp.core.common.path import path_to_str, str_to_path
from openlp.core.lib import replace_params
class PQFileDialog(QtWidgets.QFileDialog):
@classmethod
@wraps(QtWidgets.QFileDialog.getExistingDirectory)
def getExistingDirectory(cls, *args, **kwargs):
"""
Reimplement `getExistingDirectory` so that it can be called with, and return Path objects
:type parent: QtWidgets.QWidget or None
:type caption: str
:type directory: pathlib.Path
:type options: QtWidgets.QFileDialog.Options
:rtype: tuple[Path, str]
"""
args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),))
return_value = super().getExistingDirectory(*args, **kwargs)
# getExistingDirectory returns a str that represents the path. The string is empty if the user cancels the
# dialog.
return str_to_path(return_value)
@classmethod
@wraps(QtWidgets.QFileDialog.getOpenFileName)
def getOpenFileName(cls, *args, **kwargs):
"""
Reimplement `getOpenFileName` so that it can be called with, and return Path objects
:type parent: QtWidgets.QWidget or None
:type caption: str
:type directory: pathlib.Path
:type filter: str
:type initialFilter: str
:type options: QtWidgets.QFileDialog.Options
:rtype: tuple[Path, str]
"""
args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),))
return_value = super().getOpenFileName(*args, **kwargs)
# getOpenFileName returns a tuple. The first item is a of str's that represents the path. The string is empty if
# the user cancels the dialog.
return str_to_path(return_value[0]), return_value[1]
@classmethod
def getOpenFileNames(cls, *args, **kwargs):
"""
Reimplement `getOpenFileNames` so that it can be called with, and return Path objects
:type parent: QtWidgets.QWidget or None
:type caption: str
:type directory: pathlib.Path
:type filter: str
:type initialFilter: str
:type options: QtWidgets.QFileDialog.Options
:rtype: tuple[list[Path], str]
"""
args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),))
return_value = super().getOpenFileNames(*args, **kwargs)
# getSaveFileName returns a tuple. The first item is a list of str's that represents the path. The list is
# empty if the user cancels the dialog.
paths = [str_to_path(path) for path in return_value[0]]
return paths, return_value[1]
@classmethod
def getSaveFileName(cls, *args, **kwargs):
"""
Reimplement `getSaveFileName` so that it can be called with, and return Path objects
:type parent: QtWidgets.QWidget or None
:type caption: str
:type directory: pathlib.Path
:type filter: str
:type initialFilter: str
:type options: QtWidgets.QFileDialog.Options
:rtype: tuple[Path or None, str]
"""
args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),))
return_value = super().getSaveFileName(*args, **kwargs)
# getSaveFileName returns a tuple. The first item represents the path as a str. The string is empty if the user
# cancels the dialog.
return str_to_path(return_value[0]), return_value[1]

View File

@ -20,12 +20,14 @@
# 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
import os.path 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.lib import build_icon from openlp.core.lib import build_icon
from openlp.core.ui.lib.filedialogpatches import PQFileDialog
class PathType(Enum): class PathType(Enum):
@ -38,11 +40,12 @@ class PathEdit(QtWidgets.QWidget):
The :class:`~openlp.core.ui.lib.pathedit.PathEdit` class subclasses QWidget to create a custom widget for use when The :class:`~openlp.core.ui.lib.pathedit.PathEdit` class subclasses QWidget to create a custom widget for use when
a file or directory needs to be selected. a file or directory needs to be selected.
""" """
pathChanged = QtCore.pyqtSignal(str)
pathChanged = QtCore.pyqtSignal(Path)
def __init__(self, parent=None, path_type=PathType.Files, default_path=None, dialog_caption=None, show_revert=True): def __init__(self, parent=None, path_type=PathType.Files, default_path=None, dialog_caption=None, show_revert=True):
""" """
Initalise the PathEdit widget Initialise the PathEdit widget
:param parent: The parent of the widget. This is just passed to the super method. :param parent: The parent of the widget. This is just passed to the super method.
:type parent: QWidget or None :type parent: QWidget or None
@ -51,9 +54,9 @@ class PathEdit(QtWidgets.QWidget):
:type dialog_caption: str :type dialog_caption: str
:param default_path: The default path. This is set as the path when the revert button is clicked :param default_path: The default path. This is set as the path when the revert button is clicked
:type default_path: str :type default_path: pathlib.Path
:param show_revert: Used to determin if the 'revert button' should be visible. :param show_revert: Used to determine if the 'revert button' should be visible.
:type show_revert: bool :type show_revert: bool
:return: None :return: None
@ -79,7 +82,6 @@ class PathEdit(QtWidgets.QWidget):
widget_layout = QtWidgets.QHBoxLayout() widget_layout = QtWidgets.QHBoxLayout()
widget_layout.setContentsMargins(0, 0, 0, 0) widget_layout.setContentsMargins(0, 0, 0, 0)
self.line_edit = QtWidgets.QLineEdit(self) self.line_edit = QtWidgets.QLineEdit(self)
self.line_edit.setText(self._path)
widget_layout.addWidget(self.line_edit) widget_layout.addWidget(self.line_edit)
self.browse_button = QtWidgets.QToolButton(self) self.browse_button = QtWidgets.QToolButton(self)
self.browse_button.setIcon(build_icon(':/general/general_open.png')) self.browse_button.setIcon(build_icon(':/general/general_open.png'))
@ -101,7 +103,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: str :rtype: pathlib.Path
""" """
return self._path return self._path
@ -111,11 +113,15 @@ 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 path: The path to set the widget to
:type path: str :type path: pathlib.Path
:return: None
:rtype: None
""" """
self._path = path self._path = path
self.line_edit.setText(path) text = path_to_str(path)
self.line_edit.setToolTip(path) self.line_edit.setText(text)
self.line_edit.setToolTip(text)
@property @property
def path_type(self): def path_type(self):
@ -124,7 +130,7 @@ class PathEdit(QtWidgets.QWidget):
selecting a file or directory. selecting a file or directory.
:return: The type selected :return: The type selected
:rtype: Enum of PathEdit :rtype: PathType
""" """
return self._path_type return self._path_type
@ -133,8 +139,11 @@ class PathEdit(QtWidgets.QWidget):
""" """
A Property setter method to set the path type A Property setter method to set the path type
:param path: The type of path to select :param path_type: The type of path to select
:type path: Enum of PathEdit :type path_type: PathType
:return: None
:rtype: None
""" """
self._path_type = path_type self._path_type = path_type
self.update_button_tool_tips() self.update_button_tool_tips()
@ -142,7 +151,9 @@ class PathEdit(QtWidgets.QWidget):
def update_button_tool_tips(self): def update_button_tool_tips(self):
""" """
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 :return: None
:rtype: None
""" """
if self._path_type == PathType.Directories: if self._path_type == PathType.Directories:
self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for directory.')) self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for directory.'))
@ -156,21 +167,21 @@ class PathEdit(QtWidgets.QWidget):
A handler to handle a click on the browse button. A handler to handle a click on the browse button.
Show the QFileDialog and process the input from the user Show the QFileDialog and process the input from the user
:return: None :return: None
:rtype: None
""" """
caption = self.dialog_caption caption = self.dialog_caption
path = '' path = None
if self._path_type == PathType.Directories: if self._path_type == PathType.Directories:
if not caption: if not caption:
caption = translate('OpenLP.PathEdit', 'Select Directory') caption = translate('OpenLP.PathEdit', 'Select Directory')
path = QtWidgets.QFileDialog.getExistingDirectory(self, caption, path = PQFileDialog.getExistingDirectory(self, caption, self._path, PQFileDialog.ShowDirsOnly)
self._path, QtWidgets.QFileDialog.ShowDirsOnly)
elif self._path_type == PathType.Files: elif self._path_type == PathType.Files:
if not caption: if not caption:
caption = self.dialog_caption = translate('OpenLP.PathEdit', 'Select File') caption = self.dialog_caption = translate('OpenLP.PathEdit', 'Select File')
path, filter_used = QtWidgets.QFileDialog.getOpenFileName(self, caption, self._path, self.filters) path, filter_used = PQFileDialog.getOpenFileName(self, caption, self._path, self.filters)
if path: if path:
path = os.path.normpath(path)
self.on_new_path(path) self.on_new_path(path)
def on_revert_button_clicked(self): def on_revert_button_clicked(self):
@ -178,16 +189,21 @@ class PathEdit(QtWidgets.QWidget):
A handler to handle a click on the revert button. A handler to handle a click on the revert button.
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 :return: None
:rtype: None
""" """
self.on_new_path(self.default_path) self.on_new_path(self.default_path)
def on_line_edit_editing_finished(self): def on_line_edit_editing_finished(self):
""" """
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 :return: None
:rtype: None
""" """
self.on_new_path(self.line_edit.text()) path = str_to_path(self.line_edit.text())
self.on_new_path(path)
def on_new_path(self, path): def on_new_path(self, path):
""" """
@ -196,9 +212,10 @@ class PathEdit(QtWidgets.QWidget):
Emits the pathChanged Signal Emits the pathChanged Signal
:param path: The new path :param path: The new path
:type path: str :type path: pathlib.Path
:return: None :return: None
:rtype: None
""" """
if self._path != path: if self._path != path:
self.path = path self.path = path

View File

@ -28,12 +28,15 @@ import os
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
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
from openlp.core.ui.media.webkitplayer import VIDEO_EXT from openlp.core.ui.media.webkitplayer import VIDEO_EXT
from .themewizard import Ui_ThemeWizard from .themewizard import Ui_ThemeWizard
from pathlib import Path
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -316,11 +319,11 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.setField('background_type', 1) self.setField('background_type', 1)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image): elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
self.image_color_button.color = self.theme.background_border_color self.image_color_button.color = self.theme.background_border_color
self.image_path_edit.path = self.theme.background_filename self.image_path_edit.path = path_to_str(self.theme.background_filename)
self.setField('background_type', 2) self.setField('background_type', 2)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
self.video_color_button.color = self.theme.background_border_color self.video_color_button.color = self.theme.background_border_color
self.video_path_edit.path = self.theme.background_filename self.video_path_edit.path = path_to_str(self.theme.background_filename)
self.setField('background_type', 4) self.setField('background_type', 4)
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent): elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
self.setField('background_type', 3) self.setField('background_type', 3)
@ -448,18 +451,18 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
""" """
self.theme.background_end_color = color self.theme.background_end_color = color
def on_image_path_edit_path_changed(self, filename): def on_image_path_edit_path_changed(self, file_path):
""" """
Background Image button pushed. Background Image button pushed.
""" """
self.theme.background_filename = filename self.theme.background_filename = path_to_str(file_path)
self.set_background_page_values() self.set_background_page_values()
def on_video_path_edit_path_changed(self, filename): def on_video_path_edit_path_changed(self, file_path):
""" """
Background video button pushed. Background video button pushed.
""" """
self.theme.background_filename = filename self.theme.background_filename = path_to_str(file_path)
self.set_background_page_values() self.set_background_page_values()
def on_main_color_changed(self, color): def on_main_color_changed(self, color):

View File

@ -30,6 +30,8 @@ from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGrad
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
from openlp.core.ui.lib import ColorButton, PathEdit from openlp.core.ui.lib import ColorButton, PathEdit
from pathlib import Path
class Ui_ThemeWizard(object): class Ui_ThemeWizard(object):
""" """

View File

@ -255,7 +255,7 @@ class BGExtract(RegistryProperties):
chapter=chapter, chapter=chapter,
version=version) version=version)
soup = get_soup_for_bible_ref( soup = get_soup_for_bible_ref(
'http://biblegateway.com/passage/?{url}'.format(url=url_params), 'http://www.biblegateway.com/passage/?{url}'.format(url=url_params),
pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='') pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='')
if not soup: if not soup:
return None return None
@ -284,7 +284,7 @@ class BGExtract(RegistryProperties):
""" """
log.debug('BGExtract.get_books_from_http("{version}")'.format(version=version)) log.debug('BGExtract.get_books_from_http("{version}")'.format(version=version))
url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '{version}'.format(version=version)}) url_params = urllib.parse.urlencode({'action': 'getVersionInfo', 'vid': '{version}'.format(version=version)})
reference_url = 'http://biblegateway.com/versions/?{url}#books'.format(url=url_params) reference_url = 'http://www.biblegateway.com/versions/?{url}#books'.format(url=url_params)
page = get_web_page(reference_url) page = get_web_page(reference_url)
if not page: if not page:
send_error_message('download') send_error_message('download')

View File

@ -20,10 +20,11 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from PyQt5 import QtGui, QtWidgets from PyQt5 import QtWidgets
from openlp.core.common import Settings, UiStrings, translate from openlp.core.common import Settings, UiStrings, translate
from openlp.core.lib import SettingsTab, build_icon from openlp.core.common.path import path_to_str, str_to_path
from openlp.core.lib import SettingsTab
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.lib import PathEdit from openlp.core.ui.lib import PathEdit
from openlp.plugins.presentations.lib.pdfcontroller import PdfController from openlp.plugins.presentations.lib.pdfcontroller import PdfController
@ -156,7 +157,7 @@ class PresentationTab(SettingsTab):
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') pdf_program = Settings().value(self.settings_section + '/pdf_program')
if pdf_program: if pdf_program:
self.program_path_edit.path = pdf_program self.program_path_edit.path = str_to_path(pdf_program)
def save(self): def save(self):
""" """
@ -192,7 +193,7 @@ 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 = self.program_path_edit.path pdf_program = path_to_str(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 pdf_program == '':
@ -219,12 +220,12 @@ class PresentationTab(SettingsTab):
checkbox.setEnabled(controller.is_available()) checkbox.setEnabled(controller.is_available())
self.set_controller_text(checkbox, controller) self.set_controller_text(checkbox, controller)
def on_program_path_edit_path_changed(self, filename): def on_program_path_edit_path_changed(self, new_path):
""" """
Select the mudraw or ghostscript binary that should be used. Select the mudraw or ghostscript binary that should be used.
""" """
if filename: if new_path:
if not PdfController.process_check_binary(filename): if not PdfController.process_check_binary(new_path):
critical_error_message_box(UiStrings().Error, critical_error_message_box(UiStrings().Error,
translate('PresentationPlugin.PresentationTab', translate('PresentationPlugin.PresentationTab',
'The program is not ghostscript or mudraw which is required.')) 'The program is not ghostscript or mudraw which is required.'))

View File

@ -27,6 +27,7 @@ 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.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
@ -55,20 +56,21 @@ 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 = Settings().value(self.plugin.settings_section + '/last directory export') self.report_path_edit.path = str_to_path(
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 Triggered when the Directory selection button is clicked
""" """
Settings().setValue(self.plugin.settings_section + '/last directory export', file_path) Settings().setValue(self.plugin.settings_section + '/last directory export', path_to_str(file_path))
def accept(self): def accept(self):
""" """
Ok was triggered so lets save the data and run the report Ok was triggered so lets save the data and run the report
""" """
log.debug('accept') log.debug('accept')
path = self.report_path_edit.path path = path_to_str(self.report_path_edit.path)
if not path: if not path:
self.main_window.error_message( self.main_window.error_message(
translate('SongUsagePlugin.SongUsageDetailForm', 'Output Path Not Selected'), translate('SongUsagePlugin.SongUsageDetailForm', 'Output Path Not Selected'),

View File

@ -0,0 +1,88 @@
# -*- 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.path package.
"""
import os
from pathlib import Path
from unittest import TestCase
from openlp.core.common.path import path_to_str, str_to_path
class TestPath(TestCase):
"""
Tests for the :mod:`openlp.core.common.path` module
"""
def test_path_to_str_type_error(self):
"""
Test that `path_to_str` raises a type error when called with an invalid type
"""
# GIVEN: The `path_to_str` function
# WHEN: Calling `path_to_str` with an invalid Type
# THEN: A TypeError should have been raised
with self.assertRaises(TypeError):
path_to_str(str())
def test_path_to_str_none(self):
"""
Test that `path_to_str` correctly converts the path parameter when passed with None
"""
# GIVEN: The `path_to_str` function
# WHEN: Calling the `path_to_str` function with None
result = path_to_str(None)
# THEN: `path_to_str` should return an empty string
self.assertEqual(result, '')
def test_path_to_str_path_object(self):
"""
Test that `path_to_str` correctly converts the path parameter when passed a Path object
"""
# GIVEN: The `path_to_str` function
# WHEN: Calling the `path_to_str` function with a Path object
result = path_to_str(Path('test/path'))
# THEN: `path_to_str` should return a string representation of the Path object
self.assertEqual(result, os.path.join('test', 'path'))
def test_str_to_path_type_error(self):
"""
Test that `str_to_path` raises a type error when called with an invalid type
"""
# GIVEN: The `str_to_path` function
# WHEN: Calling `str_to_path` with an invalid Type
# THEN: A TypeError should have been raised
with self.assertRaises(TypeError):
str_to_path(Path())
def test_str_to_path_empty_str(self):
"""
Test that `str_to_path` correctly converts the string parameter when passed with and empty string
"""
# GIVEN: The `str_to_path` function
# WHEN: Calling the `str_to_path` function with None
result = str_to_path('')
# THEN: `path_to_str` should return None
self.assertEqual(result, None)

View File

@ -29,10 +29,9 @@ from unittest.mock import MagicMock, patch
from PyQt5 import QtCore, QtGui from PyQt5 import QtCore, QtGui
from openlp.core.lib import FormattingTags, expand_chords_for_printing from openlp.core.lib import FormattingTags, build_icon, check_item_selected, clean_tags, compare_chord_lyric, \
from openlp.core.lib import build_icon, check_item_selected, clean_tags, create_thumb, create_separated_list, \ create_separated_list, create_thumb, expand_chords, expand_chords_for_printing, expand_tags, find_formatting_tags, \
expand_tags, get_text_file_string, image_to_byte, resize_image, str_to_bool, validate_thumb, expand_chords, \ get_text_file_string, image_to_byte, replace_params, resize_image, str_to_bool, validate_thumb
compare_chord_lyric, find_formatting_tags
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
@ -652,6 +651,38 @@ class TestLib(TestCase):
mocked_os.stat.assert_any_call(thumb_path) mocked_os.stat.assert_any_call(thumb_path)
assert result is False, 'The result should be False' assert result is False, 'The result should be False'
def test_replace_params_no_params(self):
"""
Test replace_params when called with and empty tuple instead of parameters to replace
"""
# GIVEN: Some test data
test_args = (1, 2)
test_kwargs = {'arg3': 3, 'arg4': 4}
test_params = tuple()
# WHEN: Calling replace_params
result_args, result_kwargs = replace_params(test_args, test_kwargs, test_params)
# THEN: The positional and keyword args should not have changed
self.assertEqual(test_args, result_args)
self.assertEqual(test_kwargs, result_kwargs)
def test_replace_params_params(self):
"""
Test replace_params when given a positional and a keyword argument to change
"""
# GIVEN: Some test data
test_args = (1, 2)
test_kwargs = {'arg3': 3, 'arg4': 4}
test_params = ((1, 'arg2', str), (2, 'arg3', str))
# WHEN: Calling replace_params
result_args, result_kwargs = replace_params(test_args, test_kwargs, test_params)
# THEN: The positional and keyword args should have have changed
self.assertEqual(result_args, (1, '2'))
self.assertEqual(result_kwargs, {'arg3': '3', 'arg4': 4})
def test_resize_thumb(self): def test_resize_thumb(self):
""" """
Test the resize_thumb() function Test the resize_thumb() function

View File

@ -22,6 +22,7 @@
""" """
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
@ -45,7 +46,7 @@ class TestThemeManager(TestCase):
self.instance.theme = MagicMock() self.instance.theme = MagicMock()
# WHEN: `on_image_path_edit_path_changed` is clicked # WHEN: `on_image_path_edit_path_changed` is clicked
self.instance.on_image_path_edit_path_changed('/new/pat.h') self.instance.on_image_path_edit_path_changed(Path('/', 'new', 'pat.h'))
# THEN: The theme background file should be set and `set_background_page_values` should have been called # THEN: The theme background file should be set and `set_background_page_values` should have been called
self.assertEqual(self.instance.theme.background_filename, '/new/pat.h') self.assertEqual(self.instance.theme.background_filename, '/new/pat.h')

View File

@ -0,0 +1,191 @@
import os
import sys
from unittest import TestCase
from unittest.mock import patch
from pathlib import Path
from PyQt5 import QtWidgets
from openlp.core.ui.lib.filedialogpatches import PQFileDialog
class TestFileDialogPatches(TestCase):
"""
Tests for the :mod:`openlp.core.ui.lib.filedialogpatches` module
"""
def test_pq_file_dialog(self):
"""
Test that the :class:`PQFileDialog` instantiates correctly
"""
#app = QtWidgets.QApplication(sys.argv)
# GIVEN: The PQFileDialog class
# WHEN: Creating an instance
instance = PQFileDialog()
# THEN: The instance should be an instance of QFileDialog
self.assertIsInstance(instance, QtWidgets.QFileDialog)
def test_get_existing_directory_user_abort(self):
"""
Test that `getExistingDirectory` handles the case when the user cancels the dialog
"""
# GIVEN: PQFileDialog with a mocked QDialog.getExistingDirectory method
# WHEN: Calling PQFileDialog.getExistingDirectory and the user cancels the dialog returns a empty string
with patch('PyQt5.QtWidgets.QFileDialog.getExistingDirectory', return_value=''):
result = PQFileDialog.getExistingDirectory()
# THEN: The result should be None
self.assertEqual(result, None)
def test_get_existing_directory_user_accepts(self):
"""
Test that `getExistingDirectory` handles the case when the user accepts the dialog
"""
# GIVEN: PQFileDialog with a mocked QDialog.getExistingDirectory method
# WHEN: Calling PQFileDialog.getExistingDirectory, the user chooses a file and accepts the dialog (it returns a
# string pointing to the directory)
with patch('PyQt5.QtWidgets.QFileDialog.getExistingDirectory', return_value=os.path.join('test', 'dir')):
result = PQFileDialog.getExistingDirectory()
# THEN: getExistingDirectory() should return a Path object pointing to the chosen file
self.assertEqual(result, Path('test', 'dir'))
def test_get_existing_directory_param_order(self):
"""
Test that `getExistingDirectory` passes the parameters to `QFileDialog.getExistingDirectory` in the correct
order
"""
# GIVEN: PQFileDialog
with patch('openlp.core.ui.lib.filedialogpatches.QtWidgets.QFileDialog.getExistingDirectory', return_value='') \
as mocked_get_existing_directory:
# WHEN: Calling the getExistingDirectory method with all parameters set
PQFileDialog.getExistingDirectory('Parent', 'Caption', Path('test', 'dir'), 'Options')
# THEN: The `QFileDialog.getExistingDirectory` should have been called with the parameters in the correct
# order
mocked_get_existing_directory.assert_called_once_with('Parent', 'Caption', os.path.join('test', 'dir'),
'Options')
def test_get_open_file_name_user_abort(self):
"""
Test that `getOpenFileName` handles the case when the user cancels the dialog
"""
# GIVEN: PQFileDialog with a mocked QDialog.getOpenFileName method
# WHEN: Calling PQFileDialog.getOpenFileName and the user cancels the dialog (it returns a tuple with the first
# value set as an empty string)
with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')):
result = PQFileDialog.getOpenFileName()
# THEN: First value should be None
self.assertEqual(result[0], None)
def test_get_open_file_name_user_accepts(self):
"""
Test that `getOpenFileName` handles the case when the user accepts the dialog
"""
# GIVEN: PQFileDialog with a mocked QDialog.getOpenFileName method
# WHEN: Calling PQFileDialog.getOpenFileName, the user chooses a file and accepts the dialog (it returns a
# tuple with the first value set as an string pointing to the file)
with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName',
return_value=(os.path.join('test', 'chosen.file'), '')):
result = PQFileDialog.getOpenFileName()
# THEN: getOpenFileName() should return a tuple with the first value set to a Path object pointing to the
# chosen file
self.assertEqual(result[0], Path('test', 'chosen.file'))
def test_get_open_file_name_selected_filter(self):
"""
Test that `getOpenFileName` does not modify the selectedFilter as returned by `QFileDialog.getOpenFileName`
"""
# GIVEN: PQFileDialog with a mocked QDialog.get_save_file_name method
# WHEN: Calling PQFileDialog.getOpenFileName, and `QFileDialog.getOpenFileName` returns a known `selectedFilter`
with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('', 'selected filter')):
result = PQFileDialog.getOpenFileName()
# THEN: getOpenFileName() should return a tuple with the second value set to a the selected filter
self.assertEqual(result[1], 'selected filter')
def test_get_open_file_names_user_abort(self):
"""
Test that `getOpenFileNames` handles the case when the user cancels the dialog
"""
# GIVEN: PQFileDialog with a mocked QDialog.getOpenFileNames method
# WHEN: Calling PQFileDialog.getOpenFileNames and the user cancels the dialog (it returns a tuple with the first
# value set as an empty list)
with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', return_value=([], '')):
result = PQFileDialog.getOpenFileNames()
# THEN: First value should be an empty list
self.assertEqual(result[0], [])
def test_get_open_file_names_user_accepts(self):
"""
Test that `getOpenFileNames` handles the case when the user accepts the dialog
"""
# GIVEN: PQFileDialog with a mocked QDialog.getOpenFileNames method
# WHEN: Calling PQFileDialog.getOpenFileNames, the user chooses some files and accepts the dialog (it returns a
# tuple with the first value set as a list of strings pointing to the file)
with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames',
return_value=([os.path.join('test', 'chosen.file1'), os.path.join('test', 'chosen.file2')], '')):
result = PQFileDialog.getOpenFileNames()
# THEN: getOpenFileNames() should return a tuple with the first value set to a list of Path objects pointing
# to the chosen file
self.assertEqual(result[0], [Path('test', 'chosen.file1'), Path('test', 'chosen.file2')])
def test_get_open_file_names_selected_filter(self):
"""
Test that `getOpenFileNames` does not modify the selectedFilter as returned by `QFileDialog.getOpenFileNames`
"""
# GIVEN: PQFileDialog with a mocked QDialog.getOpenFileNames method
# WHEN: Calling PQFileDialog.getOpenFileNames, and `QFileDialog.getOpenFileNames` returns a known
# `selectedFilter`
with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', return_value=([], 'selected filter')):
result = PQFileDialog.getOpenFileNames()
# THEN: getOpenFileNames() should return a tuple with the second value set to a the selected filter
self.assertEqual(result[1], 'selected filter')
def test_get_save_file_name_user_abort(self):
"""
Test that `getSaveFileName` handles the case when the user cancels the dialog
"""
# GIVEN: PQFileDialog with a mocked QDialog.get_save_file_name method
# WHEN: Calling PQFileDialog.getSaveFileName and the user cancels the dialog (it returns a tuple with the first
# value set as an empty string)
with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', return_value=('', '')):
result = PQFileDialog.getSaveFileName()
# THEN: First value should be None
self.assertEqual(result[0], None)
def test_get_save_file_name_user_accepts(self):
"""
Test that `getSaveFileName` handles the case when the user accepts the dialog
"""
# GIVEN: PQFileDialog with a mocked QDialog.getSaveFileName method
# WHEN: Calling PQFileDialog.getSaveFileName, the user chooses a file and accepts the dialog (it returns a
# tuple with the first value set as an string pointing to the file)
with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName',
return_value=(os.path.join('test', 'chosen.file'), '')):
result = PQFileDialog.getSaveFileName()
# THEN: getSaveFileName() should return a tuple with the first value set to a Path object pointing to the
# chosen file
self.assertEqual(result[0], Path('test', 'chosen.file'))
def test_get_save_file_name_selected_filter(self):
"""
Test that `getSaveFileName` does not modify the selectedFilter as returned by `QFileDialog.getSaveFileName`
"""
# GIVEN: PQFileDialog with a mocked QDialog.get_save_file_name method
# WHEN: Calling PQFileDialog.getSaveFileName, and `QFileDialog.getSaveFileName` returns a known `selectedFilter`
with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', return_value=('', 'selected filter')):
result = PQFileDialog.getSaveFileName()
# THEN: getSaveFileName() should return a tuple with the second value set to a the selected filter
self.assertEqual(result[1], 'selected filter')

View File

@ -22,12 +22,13 @@
""" """
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
from pathlib import Path
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, PropertyMock, patch
from PyQt5 import QtWidgets
from openlp.core.ui.lib import PathEdit, PathType from openlp.core.ui.lib import PathEdit, PathType
from unittest.mock import MagicMock, PropertyMock, patch from openlp.core.ui.lib.filedialogpatches import PQFileDialog
class TestPathEdit(TestCase): class TestPathEdit(TestCase):
@ -43,11 +44,11 @@ class TestPathEdit(TestCase):
Test the `path` property getter. Test the `path` property getter.
""" """
# GIVEN: An instance of PathEdit with the `_path` instance variable set # GIVEN: An instance of PathEdit with the `_path` instance variable set
self.widget._path = 'getter/test/pat.h' self.widget._path = Path('getter', 'test', 'pat.h')
# WHEN: Reading the `path` property # WHEN: Reading the `path` property
# THEN: The value that we set should be returned # THEN: The value that we set should be returned
self.assertEqual(self.widget.path, 'getter/test/pat.h') self.assertEqual(self.widget.path, Path('getter', 'test', 'pat.h'))
def test_path_setter(self): def test_path_setter(self):
""" """
@ -57,13 +58,13 @@ class TestPathEdit(TestCase):
self.widget.line_edit = MagicMock() self.widget.line_edit = MagicMock()
# WHEN: Writing to the `path` property # WHEN: Writing to the `path` property
self.widget.path = 'setter/test/pat.h' self.widget.path = Path('setter', 'test', 'pat.h')
# THEN: The `_path` instance variable should be set with the test data. The `line_edit` text and tooltip # THEN: The `_path` instance variable should be set with the test data. The `line_edit` text and tooltip
# should have also been set. # should have also been set.
self.assertEqual(self.widget._path, 'setter/test/pat.h') self.assertEqual(self.widget._path, Path('setter', 'test', 'pat.h'))
self.widget.line_edit.setToolTip.assert_called_once_with('setter/test/pat.h') self.widget.line_edit.setToolTip.assert_called_once_with(os.path.join('setter', 'test', 'pat.h'))
self.widget.line_edit.setText.assert_called_once_with('setter/test/pat.h') self.widget.line_edit.setText.assert_called_once_with(os.path.join('setter', 'test', 'pat.h'))
def test_path_type_getter(self): def test_path_type_getter(self):
""" """
@ -125,22 +126,20 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked
# QFileDialog.getExistingDirectory # QFileDialog.getExistingDirectory
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory', return_value='') as \ with patch('openlp.core.ui.lib.pathedit.PQFileDialog.getExistingDirectory', return_value=None) as \
mocked_get_existing_directory, \ mocked_get_existing_directory, \
patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName') as \ patch('openlp.core.ui.lib.pathedit.PQFileDialog.getOpenFileName') as mocked_get_open_file_name:
mocked_get_open_file_name, \
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
self.widget._path_type = PathType.Directories self.widget._path_type = PathType.Directories
self.widget._path = 'test/path/' self.widget._path = Path('test', 'path')
# WHEN: Calling on_browse_button_clicked # WHEN: Calling on_browse_button_clicked
self.widget.on_browse_button_clicked() self.widget.on_browse_button_clicked()
# THEN: The FileDialog.getExistingDirectory should have been called with the default caption # THEN: The FileDialog.getExistingDirectory should have been called with the default caption
mocked_get_existing_directory.assert_called_once_with(self.widget, 'Select Directory', 'test/path/', mocked_get_existing_directory.assert_called_once_with(self.widget, 'Select Directory',
QtWidgets.QFileDialog.ShowDirsOnly) Path('test', 'path'),
PQFileDialog.ShowDirsOnly)
self.assertFalse(mocked_get_open_file_name.called) self.assertFalse(mocked_get_open_file_name.called)
self.assertFalse(mocked_normpath.called)
def test_on_browse_button_clicked_directory_custom_caption(self): def test_on_browse_button_clicked_directory_custom_caption(self):
""" """
@ -149,45 +148,40 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked
# QFileDialog.getExistingDirectory with `default_caption` set. # QFileDialog.getExistingDirectory with `default_caption` set.
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory', return_value='') as \ with patch('openlp.core.ui.lib.pathedit.PQFileDialog.getExistingDirectory', return_value=None) as \
mocked_get_existing_directory, \ mocked_get_existing_directory, \
patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName') as \ patch('openlp.core.ui.lib.pathedit.PQFileDialog.getOpenFileName') as mocked_get_open_file_name:
mocked_get_open_file_name, \
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
self.widget._path_type = PathType.Directories self.widget._path_type = PathType.Directories
self.widget._path = 'test/path/' self.widget._path = Path('test', 'path')
self.widget.dialog_caption = 'Directory Caption' self.widget.dialog_caption = 'Directory Caption'
# WHEN: Calling on_browse_button_clicked # WHEN: Calling on_browse_button_clicked
self.widget.on_browse_button_clicked() self.widget.on_browse_button_clicked()
# THEN: The FileDialog.getExistingDirectory should have been called with the custom caption # THEN: The FileDialog.getExistingDirectory should have been called with the custom caption
mocked_get_existing_directory.assert_called_once_with(self.widget, 'Directory Caption', 'test/path/', mocked_get_existing_directory.assert_called_once_with(self.widget, 'Directory Caption',
QtWidgets.QFileDialog.ShowDirsOnly) Path('test', 'path'),
PQFileDialog.ShowDirsOnly)
self.assertFalse(mocked_get_open_file_name.called) self.assertFalse(mocked_get_open_file_name.called)
self.assertFalse(mocked_normpath.called)
def test_on_browse_button_clicked_file(self): def test_on_browse_button_clicked_file(self):
""" """
Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files. Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files.
""" """
# GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName # GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory') as \ with patch('openlp.core.ui.lib.pathedit.PQFileDialog.getExistingDirectory') as mocked_get_existing_directory, \
mocked_get_existing_directory, \ patch('openlp.core.ui.lib.pathedit.PQFileDialog.getOpenFileName', return_value=(None, '')) as \
patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \ mocked_get_open_file_name:
mocked_get_open_file_name, \
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
self.widget._path_type = PathType.Files self.widget._path_type = PathType.Files
self.widget._path = 'test/pat.h' self.widget._path = Path('test', 'pat.h')
# WHEN: Calling on_browse_button_clicked # WHEN: Calling on_browse_button_clicked
self.widget.on_browse_button_clicked() self.widget.on_browse_button_clicked()
# THEN: The FileDialog.getOpenFileName should have been called with the default caption # THEN: The FileDialog.getOpenFileName should have been called with the default caption
mocked_get_open_file_name.assert_called_once_with(self.widget, 'Select File', 'test/pat.h', mocked_get_open_file_name.assert_called_once_with(self.widget, 'Select File', Path('test', 'pat.h'),
self.widget.filters) self.widget.filters)
self.assertFalse(mocked_get_existing_directory.called) self.assertFalse(mocked_get_existing_directory.called)
self.assertFalse(mocked_normpath.called)
def test_on_browse_button_clicked_file_custom_caption(self): def test_on_browse_button_clicked_file_custom_caption(self):
""" """
@ -196,23 +190,20 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName # GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName
# with `default_caption` set. # with `default_caption` set.
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory') as \ with patch('openlp.core.ui.lib.pathedit.PQFileDialog.getExistingDirectory') as mocked_get_existing_directory, \
mocked_get_existing_directory, \ patch('openlp.core.ui.lib.pathedit.PQFileDialog.getOpenFileName', return_value=(None, '')) as \
patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \ mocked_get_open_file_name:
mocked_get_open_file_name, \
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
self.widget._path_type = PathType.Files self.widget._path_type = PathType.Files
self.widget._path = 'test/pat.h' self.widget._path = Path('test', 'pat.h')
self.widget.dialog_caption = 'File Caption' self.widget.dialog_caption = 'File Caption'
# WHEN: Calling on_browse_button_clicked # WHEN: Calling on_browse_button_clicked
self.widget.on_browse_button_clicked() self.widget.on_browse_button_clicked()
# THEN: The FileDialog.getOpenFileName should have been called with the custom caption # THEN: The FileDialog.getOpenFileName should have been called with the custom caption
mocked_get_open_file_name.assert_called_once_with(self.widget, 'File Caption', 'test/pat.h', mocked_get_open_file_name.assert_called_once_with(self.widget, 'File Caption', Path('test', 'pat.h'),
self.widget.filters) self.widget.filters)
self.assertFalse(mocked_get_existing_directory.called) self.assertFalse(mocked_get_existing_directory.called)
self.assertFalse(mocked_normpath.called)
def test_on_browse_button_clicked_user_cancels(self): def test_on_browse_button_clicked_user_cancels(self):
""" """
@ -221,16 +212,14 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns an empty str for the # GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns an empty str for the
# file path. # file path.
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \ with patch('openlp.core.ui.lib.pathedit.PQFileDialog.getOpenFileName', return_value=(None, '')) as \
mocked_get_open_file_name, \ mocked_get_open_file_name:
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
# WHEN: Calling on_browse_button_clicked # WHEN: Calling on_browse_button_clicked
self.widget.on_browse_button_clicked() self.widget.on_browse_button_clicked()
# THEN: normpath should not have been called # THEN: normpath should not have been called
self.assertTrue(mocked_get_open_file_name.called) self.assertTrue(mocked_get_open_file_name.called)
self.assertFalse(mocked_normpath.called)
def test_on_browse_button_clicked_user_accepts(self): def test_on_browse_button_clicked_user_accepts(self):
""" """
@ -239,9 +228,8 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns a str for the file # GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns a str for the file
# path. # path.
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', with patch('openlp.core.ui.lib.pathedit.PQFileDialog.getOpenFileName',
return_value=('/test/pat.h', '')) as mocked_get_open_file_name, \ return_value=(Path('test', 'pat.h'), '')) as mocked_get_open_file_name, \
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath, \
patch.object(self.widget, 'on_new_path'): patch.object(self.widget, 'on_new_path'):
# WHEN: Calling on_browse_button_clicked # WHEN: Calling on_browse_button_clicked
@ -249,7 +237,6 @@ class TestPathEdit(TestCase):
# THEN: normpath and `on_new_path` should have been called # THEN: normpath and `on_new_path` should have been called
self.assertTrue(mocked_get_open_file_name.called) self.assertTrue(mocked_get_open_file_name.called)
mocked_normpath.assert_called_once_with('/test/pat.h')
self.assertTrue(self.widget.on_new_path.called) self.assertTrue(self.widget.on_new_path.called)
def test_on_revert_button_clicked(self): def test_on_revert_button_clicked(self):
@ -258,13 +245,13 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with a mocked `on_new_path`, and the `default_path` set. # GIVEN: An instance of PathEdit with a mocked `on_new_path`, and the `default_path` set.
with patch.object(self.widget, 'on_new_path') as mocked_on_new_path: with patch.object(self.widget, 'on_new_path') as mocked_on_new_path:
self.widget.default_path = '/default/pat.h' self.widget.default_path = Path('default', 'pat.h')
# WHEN: Calling `on_revert_button_clicked` # WHEN: Calling `on_revert_button_clicked`
self.widget.on_revert_button_clicked() self.widget.on_revert_button_clicked()
# THEN: on_new_path should have been called with the default path # THEN: on_new_path should have been called with the default path
mocked_on_new_path.assert_called_once_with('/default/pat.h') mocked_on_new_path.assert_called_once_with(Path('default', 'pat.h'))
def test_on_line_edit_editing_finished(self): def test_on_line_edit_editing_finished(self):
""" """
@ -272,13 +259,13 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with a mocked `line_edit` and `on_new_path`. # GIVEN: An instance of PathEdit with a mocked `line_edit` and `on_new_path`.
with patch.object(self.widget, 'on_new_path') as mocked_on_new_path: with patch.object(self.widget, 'on_new_path') as mocked_on_new_path:
self.widget.line_edit = MagicMock(**{'text.return_value': '/test/pat.h'}) self.widget.line_edit = MagicMock(**{'text.return_value': 'test/pat.h'})
# WHEN: Calling `on_line_edit_editing_finished` # WHEN: Calling `on_line_edit_editing_finished`
self.widget.on_line_edit_editing_finished() self.widget.on_line_edit_editing_finished()
# THEN: on_new_path should have been called with the path enetered in `line_edit` # THEN: on_new_path should have been called with the path enetered in `line_edit`
mocked_on_new_path.assert_called_once_with('/test/pat.h') mocked_on_new_path.assert_called_once_with(Path('test', 'pat.h'))
def test_on_new_path_no_change(self): def test_on_new_path_no_change(self):
""" """
@ -286,11 +273,11 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal # GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal
with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock): with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock):
self.widget._path = '/old/test/pat.h' self.widget._path = Path('/old', 'test', 'pat.h')
self.widget.pathChanged = MagicMock() self.widget.pathChanged = MagicMock()
# WHEN: Calling `on_new_path` with the same path as the existing path # WHEN: Calling `on_new_path` with the same path as the existing path
self.widget.on_new_path('/old/test/pat.h') self.widget.on_new_path(Path('/old', 'test', 'pat.h'))
# THEN: The `pathChanged` signal should not be emitted # THEN: The `pathChanged` signal should not be emitted
self.assertFalse(self.widget.pathChanged.emit.called) self.assertFalse(self.widget.pathChanged.emit.called)
@ -301,11 +288,11 @@ class TestPathEdit(TestCase):
""" """
# GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal # GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal
with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock): with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock):
self.widget._path = '/old/test/pat.h' self.widget._path = Path('/old', 'test', 'pat.h')
self.widget.pathChanged = MagicMock() self.widget.pathChanged = MagicMock()
# WHEN: Calling `on_new_path` with the a new path # WHEN: Calling `on_new_path` with the a new path
self.widget.on_new_path('/new/test/pat.h') self.widget.on_new_path(Path('/new', 'test', 'pat.h'))
# THEN: The `pathChanged` signal should be emitted # THEN: The `pathChanged` signal should be emitted
self.widget.pathChanged.emit.assert_called_once_with('/new/test/pat.h') self.widget.pathChanged.emit.assert_called_once_with(Path('/new', 'test', 'pat.h'))