diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 92dd5a05a..c9fbb28f6 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -35,6 +35,9 @@ from openlp.core.common.i18n import UiStrings, translate log = logging.getLogger(__name__ + '.__init__') +DEFAULT_THUMBNAIL_HEIGHT = 88 + + class DataType(IntEnum): U8 = 1 U16 = 2 @@ -301,8 +304,8 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None): :param Path image_path: The image file to create the icon from. :param Path thumb_path: The filename to save the thumbnail to. :param return_icon: States if an icon should be build and returned from the thumb. Defaults to ``True``. - :param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default - height of 88 is used. + :param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means it uses the value + from DEFAULT_THUMBNAIL_HEIGHT. :return: The final icon. """ reader = QtGui.QImageReader(str(image_path)) @@ -312,7 +315,7 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None): ratio = 1 else: ratio = reader.size().width() / reader.size().height() - reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88)) + reader.setScaledSize(QtCore.QSize(int(ratio * DEFAULT_THUMBNAIL_HEIGHT), DEFAULT_THUMBNAIL_HEIGHT)) elif size.isValid(): # Complete size given reader.setScaledSize(size) @@ -330,7 +333,7 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None): reader.setScaledSize(QtCore.QSize(int(ratio * size.height()), size.height())) else: # Invalid; use default height of 88 - reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88)) + reader.setScaledSize(QtCore.QSize(int(ratio * DEFAULT_THUMBNAIL_HEIGHT), DEFAULT_THUMBNAIL_HEIGHT)) thumb = reader.read() thumb.save(str(thumb_path), thumb_path.suffix[1:].lower()) if not return_icon: diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index cfe11ed14..7538f4fce 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -21,6 +21,7 @@ import logging from pathlib import Path +from typing import Union from PyQt5 import QtCore, QtWidgets @@ -159,7 +160,11 @@ class ImageMediaItem(FolderLibraryItem): if validate_thumb(file_path, thumbnail_path): icon = build_icon(thumbnail_path) else: - icon = create_thumb(file_path, thumbnail_path) + size: Union[QtCore.QSize, None] = None + slide_height: Union[int, None] = self.settings.value('advanced/slide max height') + if slide_height and slide_height > 0: + size = QtCore.QSize(-1, slide_height) + icon = create_thumb(file_path, thumbnail_path, size=size) tree_item = QtWidgets.QTreeWidgetItem([file_name]) tree_item.setData(0, QtCore.Qt.UserRole, item) tree_item.setIcon(0, icon) diff --git a/tests/openlp_core/lib/test_lib.py b/tests/openlp_core/lib/test_lib.py index db0453b15..1f2fc7f06 100644 --- a/tests/openlp_core/lib/test_lib.py +++ b/tests/openlp_core/lib/test_lib.py @@ -30,7 +30,9 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtGui from openlp.core.lib import DataType, build_icon, check_item_selected, create_separated_list, create_thumb, \ - get_text_file_string, image_to_byte, read_or_fail, read_int, resize_image, seek_or_fail, str_to_bool, validate_thumb + get_text_file_string, image_to_byte, read_or_fail, read_int, resize_image, seek_or_fail, str_to_bool, \ + validate_thumb +from openlp.core.common.registry import Registry from tests.utils.constants import RESOURCE_PATH @@ -275,7 +277,7 @@ def test_image_to_byte_base_64(): assert 'byte_array base64ified' == result, 'The result should be the return value of the mocked base64 method' -def test_create_thumb_with_size(registry): +def test_create_thumb_with_size(registry: Registry): """ Test the create_thumb() function with a given size. """ @@ -310,7 +312,7 @@ def test_create_thumb_with_size(registry): pass -def test_create_thumb_no_size(registry): +def test_create_thumb_no_size(registry: Registry): """ Test the create_thumb() function with no size specified. """ @@ -345,7 +347,7 @@ def test_create_thumb_no_size(registry): pass -def test_create_thumb_invalid_size(registry): +def test_create_thumb_invalid_size(registry: Registry): """ Test the create_thumb() function with invalid size specified. """ @@ -381,7 +383,7 @@ def test_create_thumb_invalid_size(registry): pass -def test_create_thumb_width_only(registry): +def test_create_thumb_width_only(registry: Registry): """ Test the create_thumb() function with a size of only width specified. """ @@ -417,7 +419,7 @@ def test_create_thumb_width_only(registry): pass -def test_create_thumb_height_only(registry): +def test_create_thumb_height_only(registry: Registry): """ Test the create_thumb() function with a size of only height specified. """ @@ -453,7 +455,7 @@ def test_create_thumb_height_only(registry): pass -def test_create_thumb_empty_img(registry): +def test_create_thumb_empty_img(registry: Registry): """ Test the create_thumb() function with a size of only height specified. """ @@ -504,7 +506,7 @@ def test_create_thumb_empty_img(registry): @patch('openlp.core.lib.QtGui.QImageReader') @patch('openlp.core.lib.build_icon') -def test_create_thumb_path_fails(mocked_build_icon, MockQImageReader, registry): +def test_create_thumb_path_fails(mocked_build_icon: MagicMock, MockQImageReader: MagicMock, registry: Registry): """ Test that build_icon() is run against the image_path when the thumbnail fails to be created """ @@ -539,7 +541,7 @@ def test_check_item_selected_true(): assert result is True, 'The result should be True' -def test_check_item_selected_false(registry): +def test_check_item_selected_false(registry: Registry): """ Test that the check_item_selected() function returns False when there are no selected indexes. """ @@ -610,7 +612,7 @@ def test_validate_thumb_file_exists_and_older(): assert result is False, 'The result should be False' -def test_resize_thumb(registry): +def test_resize_thumb(registry: Registry): """ Test the resize_thumb() function """ @@ -632,7 +634,7 @@ def test_resize_thumb(registry): assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' -def test_resize_thumb_ignoring_aspect_ratio(registry): +def test_resize_thumb_ignoring_aspect_ratio(registry: Registry): """ Test the resize_thumb() function ignoring aspect ratio """ @@ -654,7 +656,7 @@ def test_resize_thumb_ignoring_aspect_ratio(registry): assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' -def test_resize_thumb_width_aspect_ratio(registry): +def test_resize_thumb_width_aspect_ratio(registry: Registry): """ Test the resize_thumb() function using the image's width as the reference """ @@ -672,7 +674,7 @@ def test_resize_thumb_width_aspect_ratio(registry): assert wanted_width == result_size.width(), 'The image should have the requested width.' -def test_resize_thumb_same_aspect_ratio(registry): +def test_resize_thumb_same_aspect_ratio(registry: Registry): """ Test the resize_thumb() function when the image and the wanted aspect ratio are the same """ @@ -691,7 +693,7 @@ def test_resize_thumb_same_aspect_ratio(registry): @patch('openlp.core.lib.QtCore.QLocale.createSeparatedList') -def test_create_separated_list_qlocate(mocked_createSeparatedList): +def test_create_separated_list_qlocate(mocked_createSeparatedList: MagicMock): """ Test the create_separated_list function using the Qt provided method """ diff --git a/tests/openlp_plugins/images/test_mediaitem.py b/tests/openlp_plugins/images/test_mediaitem.py index 72f9f6ed8..64fc9fa21 100644 --- a/tests/openlp_plugins/images/test_mediaitem.py +++ b/tests/openlp_plugins/images/test_mediaitem.py @@ -22,17 +22,20 @@ This module contains tests for the lib submodule of the Images plugin. """ from pathlib import Path +from tempfile import TemporaryDirectory from unittest.mock import ANY, MagicMock, patch import pytest from PyQt5 import QtCore, QtWidgets -from openlp.core.common.registry import Registry from openlp.core.common.enum import ImageThemeMode +from openlp.core.common.registry import Registry from openlp.core.db.manager import DBManager +from openlp.core.lib import build_icon, create_thumb from openlp.core.lib.serviceitem import ItemCapabilities from openlp.core.widgets.views import TreeWidgetWithDnD from openlp.plugins.images.lib.mediaitem import ImageMediaItem +from tests.utils.constants import TEST_RESOURCES_PATH @pytest.fixture @@ -258,3 +261,64 @@ def test_generate_thumbnail_path_filename(media_item): # THEN: The path should be correct assert result == Path('.') / 'myimage.jpg' + + +@patch('openlp.plugins.images.lib.mediaitem.create_thumb') +def test_load_item_file_not_exist(mocked_create_thumb: MagicMock, media_item: ImageMediaItem): + """Test the load_item method when the file does not exist""" + # GIVEN: A media item and an Item to load + item = MagicMock(file_path=Path('myimage.jpg'), file_hash=None) + + # WHEN load_item() is called with the Item + result = media_item.load_item(item) + + # THEN: A QTreeWidgetItem with a "delete" icon should be returned + assert isinstance(result, QtWidgets.QTreeWidgetItem) + assert result.text(0) == 'myimage.jpg' + mocked_create_thumb.assert_not_called() + + +@patch('openlp.plugins.images.lib.mediaitem.validate_thumb') +@patch('openlp.plugins.images.lib.mediaitem.create_thumb', wraps=create_thumb) +@patch('openlp.plugins.images.lib.mediaitem.build_icon', wraps=build_icon) +def test_load_item_valid_thumbnail(mocked_build_icon: MagicMock, mocked_create_thumb: MagicMock, + mocked_validate_thumb: MagicMock, media_item: ImageMediaItem, registry: Registry): + """Test the load_item method with an existing thumbnail""" + # GIVEN: A media item and an Item to load + media_item.service_path = Path(TEST_RESOURCES_PATH) / 'images' + mocked_validate_thumb.return_value = True + image_path = Path(TEST_RESOURCES_PATH) / 'images' / 'tractor.jpg' + item = MagicMock(file_path=image_path, file_hash=None) + + # WHEN load_item() is called with the Item + result = media_item.load_item(item) + + # THEN: A QTreeWidgetItem with a "delete" icon should be returned + assert isinstance(result, QtWidgets.QTreeWidgetItem) + assert result.text(0) == 'tractor.jpg' + assert result.toolTip(0) == str(image_path) + mocked_create_thumb.assert_not_called() + mocked_build_icon.assert_called_once_with(image_path) + + +@patch('openlp.plugins.images.lib.mediaitem.validate_thumb') +@patch('openlp.plugins.images.lib.mediaitem.create_thumb', wraps=create_thumb) +def test_load_item_missing_thumbnail(mocked_create_thumb: MagicMock, mocked_validate_thumb: MagicMock, + media_item: ImageMediaItem, registry: Registry): + """Test the load_item method with no valid thumbnails""" + # GIVEN: A media item and an Item to load + with TemporaryDirectory() as tmpdir: + media_item.service_path = Path(tmpdir) + mocked_validate_thumb.return_value = False + image_path = Path(TEST_RESOURCES_PATH) / 'images' / 'tractor.jpg' + item = MagicMock(file_path=image_path, file_hash=None) + registry.get('settings').value.return_value = 400 + + # WHEN load_item() is called with the Item + result = media_item.load_item(item) + + # THEN: A QTreeWidgetItem with a "delete" icon should be returned + assert isinstance(result, QtWidgets.QTreeWidgetItem) + assert result.text(0) == 'tractor.jpg' + assert result.toolTip(0) == str(image_path) + mocked_create_thumb.assert_called_once_with(image_path, Path(tmpdir, 'tractor.jpg'), size=QtCore.QSize(-1, 400)) diff --git a/tests/resources/images/tractor.jpg b/tests/resources/images/tractor.jpg new file mode 100644 index 000000000..0135b793a Binary files /dev/null and b/tests/resources/images/tractor.jpg differ