openlp/openlp/core/lib/__init__.py

399 lines
14 KiB
Python

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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, either version 3 of the License, or #
# (at your option) any later version. #
# #
# 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, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
The :mod:`lib` module contains most of the components and libraries that make
OpenLP work.
"""
import logging
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common.i18n import translate
from openlp.core.common.path import Path
log = logging.getLogger(__name__ + '.__init__')
class ServiceItemContext(object):
"""
The context in which a Service Item is being generated
"""
Preview = 0
Live = 1
Service = 2
class ImageSource(object):
"""
This enumeration class represents different image sources. An image sources states where an image is used. This
enumeration class is need in the context of the :class:~openlp.core.lib.imagemanager`.
``ImagePlugin``
This states that an image is being used by the image plugin.
``Theme``
This says, that the image is used by a theme.
``CommandPlugins``
This states that an image is being used by a command plugin.
"""
ImagePlugin = 1
Theme = 2
CommandPlugins = 3
class MediaType(object):
"""
An enumeration class for types of media.
"""
Audio = 1
Video = 2
class ServiceItemAction(object):
"""
Provides an enumeration for the required action moving between service items by left/right arrow keys
"""
Previous = 1
PreviousLastSlide = 2
Next = 3
class ItemCapabilities(object):
"""
Provides an enumeration of a service item's capabilities
``CanPreview``
The capability to allow the ServiceManager to add to the preview tab when making the previous item live.
``CanEdit``
The capability to allow the ServiceManager to allow the item to be edited
``CanMaintain``
The capability to allow the ServiceManager to allow the item to be reordered.
``RequiresMedia``
Determines is the service_item needs a Media Player
``CanLoop``
The capability to allow the SlideController to allow the loop processing.
``CanAppend``
The capability to allow the ServiceManager to add leaves to the
item
``NoLineBreaks``
The capability to remove lines breaks in the renderer
``OnLoadUpdate``
The capability to update MediaManager when a service Item is loaded.
``AddIfNewItem``
Not Used
``ProvidesOwnDisplay``
The capability to tell the SlideController the service Item has a different display.
``HasDetailedTitleDisplay``
Being Removed and decommissioned.
``HasVariableStartTime``
The capability to tell the ServiceManager that a change to start time is possible.
``CanSoftBreak``
The capability to tell the renderer that Soft Break is allowed
``CanWordSplit``
The capability to tell the renderer that it can split words is
allowed
``HasBackgroundAudio``
That a audio file is present with the text.
``CanAutoStartForLive``
The capability to ignore the do not play if display blank flag.
``CanEditTitle``
The capability to edit the title of the item
``IsOptical``
Determines is the service_item is based on an optical device
``HasDisplayTitle``
The item contains 'displaytitle' on every frame which should be
preferred over 'title' when displaying the item
``HasNotes``
The item contains 'notes'
``HasThumbnails``
The item has related thumbnails available
``HasMetaData``
The item has Meta Data about item
"""
CanPreview = 1
CanEdit = 2
CanMaintain = 3
RequiresMedia = 4
CanLoop = 5
CanAppend = 6
NoLineBreaks = 7
OnLoadUpdate = 8
AddIfNewItem = 9
ProvidesOwnDisplay = 10
# HasDetailedTitleDisplay = 11
HasVariableStartTime = 12
CanSoftBreak = 13
CanWordSplit = 14
HasBackgroundAudio = 15
CanAutoStartForLive = 16
CanEditTitle = 17
IsOptical = 18
HasDisplayTitle = 19
HasNotes = 20
HasThumbnails = 21
HasMetaData = 22
def get_text_file_string(text_file_path):
"""
Open a file and return its content as a string. If the supplied file path is not a file then the function
returns False. If there is an error loading the file or the content can't be decoded then the function will return
None.
: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
decoding the file.
:rtype: str | False | None
"""
if not text_file_path.is_file():
return False
content = None
try:
with text_file_path.open('r', encoding='utf-8') as file_handle:
if file_handle.read(3) != '\xEF\xBB\xBF':
# no BOM was found
file_handle.seek(0)
content = file_handle.read()
except (OSError, UnicodeError):
log.exception('Failed to open text file {text}'.format(text=text_file_path))
return content
def str_to_bool(string_value):
"""
Convert a string version of a boolean into a real boolean.
:param string_value: The string value to examine and convert to a boolean type.
:return: The correct boolean value
"""
if isinstance(string_value, bool):
return string_value
return str(string_value).strip().lower() in ('true', 'yes', 'y')
def build_icon(icon):
"""
Build a QIcon instance from an existing QIcon, a resource location, or a physical file location. If the icon is a
QIcon instance, that icon is simply returned. If not, it builds a QIcon instance from the resource or file name.
:param QtGui.QIcon | Path | QtGui.QIcon | str icon:
The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file path
location like ``Path(/path/to/file.png)``. However, the **recommended** way is to specify a resource string.
:return: The build icon.
:rtype: QtGui.QIcon
"""
if isinstance(icon, QtGui.QIcon):
return icon
pix_map = None
button_icon = QtGui.QIcon()
if isinstance(icon, str):
pix_map = QtGui.QPixmap(icon)
elif isinstance(icon, Path):
pix_map = QtGui.QPixmap(str(icon))
elif isinstance(icon, QtGui.QImage):
pix_map = QtGui.QPixmap.fromImage(icon)
if pix_map:
button_icon.addPixmap(pix_map, QtGui.QIcon.Normal, QtGui.QIcon.Off)
return button_icon
def image_to_byte(image, base_64=True):
"""
Resize an image to fit on the current screen for the web and returns it as a byte stream.
:param image: The image to be converted.
:param base_64: If True returns the image as Base64 bytes, otherwise the image is returned as a byte array.
To preserve original intention, this defaults to True
"""
log.debug('image_to_byte - start')
byte_array = QtCore.QByteArray()
# use buffer to store pixmap into byteArray
buffer = QtCore.QBuffer(byte_array)
buffer.open(QtCore.QIODevice.WriteOnly)
image.save(buffer, "PNG")
log.debug('image_to_byte - end')
if not base_64:
return byte_array
# convert to base64 encoding so does not get missed!
return bytes(byte_array.toBase64()).decode('utf-8')
def create_thumb(image_path, thumb_path, return_icon=True, size=None):
"""
Create a thumbnail from the given image path and depending on ``return_icon`` it returns an icon from this thumb.
:param openlp.core.common.path.Path image_path: The image file to create the icon from.
:param openlp.core.common.path.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.
:return: The final icon.
"""
reader = QtGui.QImageReader(str(image_path))
if size is None:
# No size given; use default height of 88
if reader.size().isEmpty():
ratio = 1
else:
ratio = reader.size().width() / reader.size().height()
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
elif size.isValid():
# Complete size given
reader.setScaledSize(size)
else:
# Invalid size given
if reader.size().isEmpty():
ratio = 1
else:
ratio = reader.size().width() / reader.size().height()
if size.width() >= 0:
# Valid width; scale height
reader.setScaledSize(QtCore.QSize(size.width(), int(size.width() / ratio)))
elif size.height() >= 0:
# Valid height; scale width
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))
thumb = reader.read()
thumb.save(str(thumb_path), thumb_path.suffix[1:].lower())
if not return_icon:
return
if thumb_path.exists():
return build_icon(thumb_path)
# Fallback for files with animation support.
return build_icon(image_path)
def validate_thumb(file_path, thumb_path):
"""
Validates whether an file's thumb still exists and if is up to date. **Note**, you must **not** call this function,
before checking the existence of the file.
:param openlp.core.common.path.Path file_path: The path to the file. The file **must** exist!
:param openlp.core.common.path.Path thumb_path: The path to the thumb.
:return: Has the image changed since the thumb was created?
:rtype: bool
"""
if not thumb_path.exists():
return False
image_date = file_path.stat().st_mtime
thumb_date = thumb_path.stat().st_mtime
return image_date <= thumb_date
def resize_image(image_path, width, height, background='#000000', ignore_aspect_ratio=False):
"""
Resize an image to fit on the current screen.
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
:param image_path: The path to the image to resize.
:param width: The new image width.
:param height: The new image height.
:param background: The background colour. Defaults to black.
"""
log.debug('resize_image - start')
reader = QtGui.QImageReader(image_path)
# The image's ratio.
image_ratio = reader.size().width() / reader.size().height()
resize_ratio = width / height
# Figure out the size we want to resize the image to (keep aspect ratio).
if image_ratio == resize_ratio or ignore_aspect_ratio:
size = QtCore.QSize(width, height)
elif image_ratio < resize_ratio:
# Use the image's height as reference for the new size.
size = QtCore.QSize(image_ratio * height, height)
else:
# Use the image's width as reference for the new size.
size = QtCore.QSize(width, 1 / (image_ratio / width))
reader.setScaledSize(size)
preview = reader.read()
if image_ratio == resize_ratio:
# We neither need to centre the image nor add "bars" to the image.
return preview
real_width = preview.width()
real_height = preview.height()
# and move it to the centre of the preview space
new_image = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32_Premultiplied)
painter = QtGui.QPainter(new_image)
painter.fillRect(new_image.rect(), QtGui.QColor(background))
painter.drawImage((width - real_width) // 2, (height - real_height) // 2, preview)
return new_image
def check_item_selected(list_widget, message):
"""
Check if a list item is selected so an action may be performed on it
:param list_widget: The list to check for selected items
:param message: The message to give the user if no item is selected
"""
if not list_widget.selectedIndexes():
QtWidgets.QMessageBox.information(list_widget.parent(),
translate('OpenLP.MediaManagerItem', 'No Items Selected'), message)
return False
return True
def create_separated_list(string_list):
"""
Returns a string that represents a join of a list of strings with a localized separator.
Localized separation will be done via the translate() function by the translators.
:param string_list: List of unicode strings
:return: Formatted string
"""
list_length = len(string_list)
if list_length == 1:
list_to_string = string_list[0]
elif list_length == 2:
list_to_string = translate('OpenLP.core.lib', '{one} and {two}').format(one=string_list[0], two=string_list[1])
elif list_length > 2:
list_to_string = translate('OpenLP.core.lib', '{first} and {last}').format(first=', '.join(string_list[:-1]),
last=string_list[-1])
else:
list_to_string = ''
return list_to_string