forked from openlp/openlp
Moved most of the presentation plugin over to pathlib
This commit is contained in:
parent
f0e7381f5c
commit
8ed5903ced
@ -29,9 +29,10 @@ import os
|
||||
import re
|
||||
import math
|
||||
|
||||
from PyQt5 import QtCore, QtGui, Qt, QtWidgets
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common import translate
|
||||
from openlp.core.common.path import Path
|
||||
|
||||
log = logging.getLogger(__name__ + '.__init__')
|
||||
|
||||
@ -125,10 +126,11 @@ 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 icon:
|
||||
The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file
|
||||
location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
|
||||
: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
|
||||
@ -136,6 +138,8 @@ def build_icon(icon):
|
||||
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:
|
||||
@ -221,10 +225,12 @@ def validate_thumb(file_path, thumb_path):
|
||||
:param thumb_path: The path to the thumb.
|
||||
:return: True, False if the image has changed since the thumb was created.
|
||||
"""
|
||||
if not os.path.exists(thumb_path):
|
||||
file_path = Path(file_path)
|
||||
thumb_path = Path(thumb_path)
|
||||
if not thumb_path.exists():
|
||||
return False
|
||||
image_date = os.stat(file_path).st_mtime
|
||||
thumb_date = os.stat(thumb_path).st_mtime
|
||||
image_date = file_path.stat().st_mtime
|
||||
thumb_date = thumb_path.stat().st_mtime
|
||||
return image_date <= thumb_date
|
||||
|
||||
|
||||
|
@ -359,10 +359,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||
:param files: The files to be loaded.
|
||||
:param target_group: The QTreeWidgetItem of the group that will be the parent of the added files
|
||||
"""
|
||||
names = []
|
||||
full_list = []
|
||||
for count in range(self.list_view.count()):
|
||||
names.append(self.list_view.item(count).text())
|
||||
full_list.append(self.list_view.item(count).data(QtCore.Qt.UserRole))
|
||||
duplicates_found = False
|
||||
files_added = False
|
||||
|
@ -95,3 +95,20 @@ def rmtree(*args, **kwargs):
|
||||
args, kwargs = replace_params(args, kwargs, ((0, 'path', path_to_str),))
|
||||
|
||||
return shutil.rmtree(*args, **kwargs)
|
||||
# TODO: Test and tidy
|
||||
def which(*args, **kwargs):
|
||||
"""
|
||||
Wraps :func:shutil.rmtree` so that we can accept Path objects.
|
||||
|
||||
:param openlp.core.common.path.Path path: Takes a Path object which is then converted to a str object
|
||||
:return: Passes the return from :func:`shutil.rmtree` back
|
||||
:rtype: None
|
||||
|
||||
See the following link for more information on the other parameters:
|
||||
https://docs.python.org/3/library/shutil.html#shutil.rmtree
|
||||
"""
|
||||
|
||||
file_name = shutil.which(*args, **kwargs)
|
||||
if file_name:
|
||||
return str_to_path(file_name)
|
||||
return None
|
||||
|
@ -32,11 +32,14 @@
|
||||
# http://nxsy.org/comparing-documents-with-openoffice-and-python
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
from openlp.core.common import is_win, Registry, delete_file
|
||||
from openlp.core.common.path import Path
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.common import Registry, delete_file, get_uno_command, get_uno_instance, is_win
|
||||
from openlp.core.lib import ScreenList
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
|
||||
TextType
|
||||
|
||||
if is_win():
|
||||
from win32com.client import Dispatch
|
||||
@ -55,14 +58,6 @@ else:
|
||||
except ImportError:
|
||||
uno_available = False
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.lib import ScreenList
|
||||
from openlp.core.common import get_uno_command, get_uno_instance
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
|
||||
TextType
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -203,12 +198,15 @@ class ImpressDocument(PresentationDocument):
|
||||
Class which holds information and controls a single presentation.
|
||||
"""
|
||||
|
||||
def __init__(self, controller, presentation):
|
||||
def __init__(self, controller, document_path):
|
||||
"""
|
||||
Constructor, store information about the file and initialise.
|
||||
|
||||
:param openlp.core.common.path.Path document_path: File path for the document to load
|
||||
:rtype: None
|
||||
"""
|
||||
log.debug('Init Presentation OpenOffice')
|
||||
super(ImpressDocument, self).__init__(controller, presentation)
|
||||
super().__init__(controller, document_path)
|
||||
self.document = None
|
||||
self.presentation = None
|
||||
self.control = None
|
||||
@ -225,10 +223,10 @@ class ImpressDocument(PresentationDocument):
|
||||
if desktop is None:
|
||||
self.controller.start_process()
|
||||
desktop = self.controller.get_com_desktop()
|
||||
url = 'file:///' + self.file_path.replace('\\', '/').replace(':', '|').replace(' ', '%20')
|
||||
url = self.file_path.as_uri()
|
||||
else:
|
||||
desktop = self.controller.get_uno_desktop()
|
||||
url = uno.systemPathToFileUrl(self.file_path)
|
||||
url = uno.systemPathToFileUrl(str(self.file_path))
|
||||
if desktop is None:
|
||||
return False
|
||||
self.desktop = desktop
|
||||
@ -254,11 +252,11 @@ class ImpressDocument(PresentationDocument):
|
||||
log.debug('create thumbnails OpenOffice')
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
temp_folder_path = self.get_temp_folder()
|
||||
if is_win():
|
||||
thumb_dir_url = 'file:///' + str(self.get_temp_folder()).replace('\\', '/') \
|
||||
.replace(':', '|').replace(' ', '%20')
|
||||
thumb_dir_url = temp_folder_path.as_uri()
|
||||
else:
|
||||
thumb_dir_url = uno.systemPathToFileUrl(str(self.get_temp_folder()))
|
||||
thumb_dir_url = uno.systemPathToFileUrl(str(temp_folder_path))
|
||||
properties = []
|
||||
properties.append(self.create_property('FilterName', 'impress_png_Export'))
|
||||
properties = tuple(properties)
|
||||
@ -266,17 +264,16 @@ class ImpressDocument(PresentationDocument):
|
||||
pages = doc.getDrawPages()
|
||||
if not pages:
|
||||
return
|
||||
temp_folder_path = self.get_temp_folder()
|
||||
if not temp_folder_path.isdir():
|
||||
temp_folder_path.mkdir()
|
||||
if not temp_folder_path.is_dir():
|
||||
temp_folder_path.mkdir(parents=True)
|
||||
for index in range(pages.getCount()):
|
||||
page = pages.getByIndex(index)
|
||||
doc.getCurrentController().setCurrentPage(page)
|
||||
url_path = '{path}/{name}.png'.format(path=thumb_dir_url, name=str(index + 1))
|
||||
path = temp_folder_path / '{number).png'.format(number=index + 1)
|
||||
url_path = '{path}/{name:d}.png'.format(path=thumb_dir_url, name=index + 1)
|
||||
path = temp_folder_path / '{number:d}.png'.format(number=index + 1)
|
||||
try:
|
||||
doc.storeToURL(url_path, properties)
|
||||
self.convert_thumbnail(str(path), index + 1)
|
||||
self.convert_thumbnail(path, index + 1)
|
||||
delete_file(path)
|
||||
except ErrorCodeIOException as exception:
|
||||
log.exception('ERROR! ErrorCodeIOException {error:d}'.format(error=exception.ErrCode))
|
||||
|
@ -19,15 +19,13 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
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.common.path import Path, path_to_str, str_to_path
|
||||
from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext,\
|
||||
build_icon, check_item_selected, create_thumb, validate_thumb
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
|
||||
@ -128,7 +126,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
"""
|
||||
self.list_view.setIconSize(QtCore.QSize(88, 50))
|
||||
file_paths = Settings().value(self.settings_section + '/presentations files')
|
||||
self.load_list([path_to_str(file) for file in file_paths], initial_load=True)
|
||||
self.load_list([path_to_str(path) for path in file_paths], initial_load=True)
|
||||
self.populate_display_types()
|
||||
|
||||
def populate_display_types(self):
|
||||
@ -152,54 +150,57 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
else:
|
||||
self.presentation_widget.hide()
|
||||
|
||||
def load_list(self, files, target_group=None, initial_load=False):
|
||||
def load_list(self, file_paths, target_group=None, initial_load=False):
|
||||
"""
|
||||
Add presentations into the media manager. This is called both on initial load of the plugin to populate with
|
||||
existing files, and when the user adds new files via the media manager.
|
||||
|
||||
:param list[openlp.core.common.path.Path] file_paths: List of file paths to add to the media manager.
|
||||
"""
|
||||
current_list = self.get_file_list()
|
||||
titles = [file_path.name for file_path in current_list]
|
||||
file_paths = [str_to_path(filename) for filename in file_paths]
|
||||
current_paths = self.get_file_list()
|
||||
titles = [file_path.name for file_path in current_paths]
|
||||
self.application.set_busy_cursor()
|
||||
if not initial_load:
|
||||
self.main_window.display_progress_bar(len(files))
|
||||
self.main_window.display_progress_bar(len(file_paths))
|
||||
# Sort the presentations by its filename considering language specific characters.
|
||||
files.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
|
||||
for file in files:
|
||||
file_paths.sort(key=lambda file_path: get_locale_key(file_path.name))
|
||||
for file_path in file_paths:
|
||||
if not initial_load:
|
||||
self.main_window.increment_progress_bar()
|
||||
if current_list.count(file) > 0:
|
||||
if current_paths.count(file_path) > 0:
|
||||
continue
|
||||
filename = os.path.split(file)[1]
|
||||
if not os.path.exists(file):
|
||||
item_name = QtWidgets.QListWidgetItem(filename)
|
||||
file_name = file_path.name
|
||||
if not file_path.exists():
|
||||
item_name = QtWidgets.QListWidgetItem(file_name)
|
||||
item_name.setIcon(build_icon(ERROR_IMAGE))
|
||||
item_name.setData(QtCore.Qt.UserRole, file)
|
||||
item_name.setToolTip(file)
|
||||
item_name.setData(QtCore.Qt.UserRole, path_to_str(file_path))
|
||||
item_name.setToolTip(str(file_path))
|
||||
self.list_view.addItem(item_name)
|
||||
else:
|
||||
if titles.count(filename) > 0:
|
||||
if titles.count(file_name) > 0:
|
||||
if not initial_load:
|
||||
critical_error_message_box(translate('PresentationPlugin.MediaItem', 'File Exists'),
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'A presentation with that filename already exists.'))
|
||||
continue
|
||||
controller_name = self.find_controller_by_type(filename)
|
||||
controller_name = self.find_controller_by_type(file_path)
|
||||
if controller_name:
|
||||
controller = self.controllers[controller_name]
|
||||
doc = controller.add_document(file)
|
||||
thumb = str(doc.get_thumbnail_folder() / 'icon.png')
|
||||
preview = doc.get_thumbnail_path(1, True)
|
||||
if not preview and not initial_load:
|
||||
doc = controller.add_document(file_path)
|
||||
thumbnail_path = doc.get_thumbnail_folder() / 'icon.png'
|
||||
preview_path = doc.get_thumbnail_path(1, True)
|
||||
if not preview_path and not initial_load:
|
||||
doc.load_presentation()
|
||||
preview = doc.get_thumbnail_path(1, True)
|
||||
preview_path = doc.get_thumbnail_path(1, True)
|
||||
doc.close_presentation()
|
||||
if not (preview and os.path.exists(preview)):
|
||||
if not (preview_path and preview_path.exists()):
|
||||
icon = build_icon(':/general/general_delete.png')
|
||||
else:
|
||||
if validate_thumb(preview, thumb):
|
||||
icon = build_icon(thumb)
|
||||
if validate_thumb(preview_path, thumbnail_path):
|
||||
icon = build_icon(thumbnail_path)
|
||||
else:
|
||||
icon = create_thumb(preview, thumb)
|
||||
icon = create_thumb(str(preview_path), str(thumbnail_path))
|
||||
else:
|
||||
if initial_load:
|
||||
icon = build_icon(':/general/general_delete.png')
|
||||
@ -208,10 +209,10 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'This type of presentation is not supported.'))
|
||||
continue
|
||||
item_name = QtWidgets.QListWidgetItem(filename)
|
||||
item_name.setData(QtCore.Qt.UserRole, file)
|
||||
item_name = QtWidgets.QListWidgetItem(file_name)
|
||||
item_name.setData(QtCore.Qt.UserRole, path_to_str(file_path))
|
||||
item_name.setIcon(icon)
|
||||
item_name.setToolTip(file)
|
||||
item_name.setToolTip(str(file_path))
|
||||
self.list_view.addItem(item_name)
|
||||
if not initial_load:
|
||||
self.main_window.finished_progress_bar()
|
||||
@ -228,8 +229,8 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
self.application.set_busy_cursor()
|
||||
self.main_window.display_progress_bar(len(row_list))
|
||||
for item in items:
|
||||
filepath = str(item.data(QtCore.Qt.UserRole))
|
||||
self.clean_up_thumbnails(filepath)
|
||||
file_path = str_to_path(item.data(QtCore.Qt.UserRole))
|
||||
self.clean_up_thumbnails(file_path)
|
||||
self.main_window.increment_progress_bar()
|
||||
self.main_window.finished_progress_bar()
|
||||
for row in row_list:
|
||||
@ -237,30 +238,29 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
Settings().setValue(self.settings_section + '/presentations files', self.get_file_list())
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def clean_up_thumbnails(self, filepath, clean_for_update=False):
|
||||
def clean_up_thumbnails(self, file_path, clean_for_update=False):
|
||||
"""
|
||||
Clean up the files created such as thumbnails
|
||||
|
||||
:param filepath: File path of the presention to clean up after
|
||||
:param clean_for_update: Only clean thumbnails if update is needed
|
||||
:return: None
|
||||
:param openlp.core.common.path.Path file_path: File path of the presention to clean up after
|
||||
:param bool clean_for_update: Only clean thumbnails if update is needed
|
||||
:rtype: None
|
||||
"""
|
||||
for cidx in self.controllers:
|
||||
root, file_ext = os.path.splitext(filepath)
|
||||
file_ext = file_ext[1:]
|
||||
file_ext = file_path.suffix[1:]
|
||||
if file_ext in self.controllers[cidx].supports or file_ext in self.controllers[cidx].also_supports:
|
||||
doc = self.controllers[cidx].add_document(filepath)
|
||||
doc = self.controllers[cidx].add_document(file_path)
|
||||
if clean_for_update:
|
||||
thumb_path = doc.get_thumbnail_path(1, True)
|
||||
if not thumb_path or not os.path.exists(filepath) or os.path.getmtime(
|
||||
thumb_path) < os.path.getmtime(filepath):
|
||||
if not thumb_path or not file_path.exists() or \
|
||||
thumb_path.stat().st_mtime < file_path.stat().st_mtime:
|
||||
doc.presentation_deleted()
|
||||
else:
|
||||
doc.presentation_deleted()
|
||||
doc.close_presentation()
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
||||
context=ServiceItemContext.Service, presentation_file=None):
|
||||
context=ServiceItemContext.Service, file_path=None):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
|
||||
@ -276,10 +276,9 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
items = self.list_view.selectedItems()
|
||||
if len(items) > 1:
|
||||
return False
|
||||
filename = presentation_file
|
||||
if filename is None:
|
||||
filename = items[0].data(QtCore.Qt.UserRole)
|
||||
file_type = os.path.splitext(filename.lower())[1][1:]
|
||||
if file_path is None:
|
||||
file_path = str_to_path(items[0].data(QtCore.Qt.UserRole))
|
||||
file_type = file_path.suffix.lower()[1:]
|
||||
if not self.display_type_combo_box.currentText():
|
||||
return False
|
||||
service_item.add_capability(ItemCapabilities.CanEditTitle)
|
||||
@ -292,29 +291,28 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
# force a nonexistent theme
|
||||
service_item.theme = -1
|
||||
for bitem in items:
|
||||
filename = presentation_file
|
||||
if filename is None:
|
||||
filename = bitem.data(QtCore.Qt.UserRole)
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.title = name
|
||||
if os.path.exists(filename):
|
||||
processor = self.find_controller_by_type(filename)
|
||||
if file_path is None:
|
||||
file_path = str_to_path(bitem.data(QtCore.Qt.UserRole))
|
||||
path, file_name = file_path.parent, file_path.name
|
||||
service_item.title = file_name
|
||||
if file_path.exists():
|
||||
processor = self.find_controller_by_type(file_path)
|
||||
if not processor:
|
||||
return False
|
||||
controller = self.controllers[processor]
|
||||
service_item.processor = None
|
||||
doc = controller.add_document(filename)
|
||||
doc = controller.add_document(file_path)
|
||||
if doc.get_thumbnail_path(1, True) is None or \
|
||||
not (doc.get_temp_folder() / 'mainslide001.png').is_file():
|
||||
not (doc.get_temp_folder() / 'mainslide001.png').is_file():
|
||||
doc.load_presentation()
|
||||
i = 1
|
||||
image = str(doc.get_temp_folder() / 'mainslide{number:0>3d}.png'.format(number=i))
|
||||
thumbnail = str(doc.get_thumbnail_folder() / 'slide{number:d}.png'.format(number=i))
|
||||
while os.path.isfile(image):
|
||||
service_item.add_from_image(image, name, thumbnail=thumbnail)
|
||||
image_path = doc.get_temp_folder() / 'mainslide{number:0>3d}.png'.format(number=i)
|
||||
thumbnail_path = doc.get_thumbnail_folder() / 'slide{number:d}.png'.format(number=i)
|
||||
while image_path.is_file():
|
||||
service_item.add_from_image(str(image_path), file_name, thumbnail=str(thumbnail_path))
|
||||
i += 1
|
||||
image = str(doc.get_temp_folder() / 'mainslide{number:0>3d}.png'.format(number=i))
|
||||
thumbnail = str(doc.get_thumbnail_folder() / 'slide{number:d}.png'.format(number=i))
|
||||
image_path = doc.get_temp_folder() / 'mainslide{number:0>3d}.png'.format(number=i)
|
||||
thumbnail_path = doc.get_thumbnail_folder() / 'slide{number:d}.png'.format(number=i)
|
||||
service_item.add_capability(ItemCapabilities.HasThumbnails)
|
||||
doc.close_presentation()
|
||||
return True
|
||||
@ -324,34 +322,34 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
critical_error_message_box(translate('PresentationPlugin.MediaItem', 'Missing Presentation'),
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'The presentation {name} no longer exists.'
|
||||
).format(name=filename))
|
||||
).format(name=file_path))
|
||||
return False
|
||||
else:
|
||||
service_item.processor = self.display_type_combo_box.currentText()
|
||||
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
|
||||
for bitem in items:
|
||||
filename = bitem.data(QtCore.Qt.UserRole)
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.title = name
|
||||
if os.path.exists(filename):
|
||||
file_path = str_to_path(bitem.data(QtCore.Qt.UserRole))
|
||||
path, file_name = file_path.parent, file_path.name
|
||||
service_item.title = file_name
|
||||
if file_path.exists:
|
||||
if self.display_type_combo_box.itemData(self.display_type_combo_box.currentIndex()) == 'automatic':
|
||||
service_item.processor = self.find_controller_by_type(filename)
|
||||
service_item.processor = self.find_controller_by_type(file_path)
|
||||
if not service_item.processor:
|
||||
return False
|
||||
controller = self.controllers[service_item.processor]
|
||||
doc = controller.add_document(filename)
|
||||
doc = controller.add_document(file_path)
|
||||
if doc.get_thumbnail_path(1, True) is None:
|
||||
doc.load_presentation()
|
||||
i = 1
|
||||
img = doc.get_thumbnail_path(i, True)
|
||||
if img:
|
||||
thumbnail_path = doc.get_thumbnail_path(i, True)
|
||||
if thumbnail_path:
|
||||
# Get titles and notes
|
||||
titles, notes = doc.get_titles_and_notes()
|
||||
service_item.add_capability(ItemCapabilities.HasDisplayTitle)
|
||||
if notes.count('') != len(notes):
|
||||
service_item.add_capability(ItemCapabilities.HasNotes)
|
||||
service_item.add_capability(ItemCapabilities.HasThumbnails)
|
||||
while img:
|
||||
while thumbnail_path:
|
||||
# Use title and note if available
|
||||
title = ''
|
||||
if titles and len(titles) >= i:
|
||||
@ -359,9 +357,9 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
note = ''
|
||||
if notes and len(notes) >= i:
|
||||
note = notes[i - 1]
|
||||
service_item.add_from_command(path, name, img, title, note)
|
||||
service_item.add_from_command(str(path), file_name, str(thumbnail_path), title, note)
|
||||
i += 1
|
||||
img = doc.get_thumbnail_path(i, True)
|
||||
thumbnail_path = doc.get_thumbnail_path(i, True)
|
||||
doc.close_presentation()
|
||||
return True
|
||||
else:
|
||||
@ -371,7 +369,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
'Missing Presentation'),
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'The presentation {name} is incomplete, '
|
||||
'please reload.').format(name=filename))
|
||||
'please reload.').format(name=file_path))
|
||||
return False
|
||||
else:
|
||||
# File is no longer present
|
||||
@ -379,18 +377,20 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
critical_error_message_box(translate('PresentationPlugin.MediaItem', 'Missing Presentation'),
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'The presentation {name} no longer exists.'
|
||||
).format(name=filename))
|
||||
).format(name=file_path))
|
||||
return False
|
||||
|
||||
def find_controller_by_type(self, filename):
|
||||
def find_controller_by_type(self, file_path):
|
||||
"""
|
||||
Determine the default application controller to use for the selected file type. This is used if "Automatic" is
|
||||
set as the preferred controller. Find the first (alphabetic) enabled controller which "supports" the extension.
|
||||
If none found, then look for a controller which "also supports" it instead.
|
||||
|
||||
:param filename: The file name
|
||||
:param openlp.core.common.path.Path file_path: The file path
|
||||
:return: The default application controller for this file type, or None if not supported
|
||||
:rtype: PresentationController
|
||||
"""
|
||||
file_type = os.path.splitext(filename)[1][1:]
|
||||
file_type = file_path.suffix[1:]
|
||||
if not file_type:
|
||||
return None
|
||||
for controller in self.controllers:
|
||||
|
@ -19,16 +19,15 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import copy
|
||||
import os
|
||||
import logging
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.common import Registry, Settings
|
||||
from openlp.core.ui import HideMode
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.core.lib import ServiceItemContext
|
||||
from openlp.core.ui import HideMode
|
||||
from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -325,21 +324,25 @@ class MessageListener(object):
|
||||
is_live = message[1]
|
||||
item = message[0]
|
||||
hide_mode = message[2]
|
||||
file = item.get_frame_path()
|
||||
file_path = Path(item.get_frame_path())
|
||||
self.handler = item.processor
|
||||
# When starting presentation from the servicemanager we convert
|
||||
# PDF/XPS/OXPS-serviceitems into image-serviceitems. When started from the mediamanager
|
||||
# the conversion has already been done at this point.
|
||||
file_type = os.path.splitext(file.lower())[1][1:]
|
||||
file_type = file_path.suffix.lower()[1:]
|
||||
if file_type in PDF_CONTROLLER_FILETYPES:
|
||||
log.debug('Converting from pdf/xps/oxps to images for serviceitem with file {name}'.format(name=file))
|
||||
log.debug('Converting from pdf/xps/oxps to images for serviceitem with file {name}'.format(name=file_path))
|
||||
# Create a copy of the original item, and then clear the original item so it can be filled with images
|
||||
item_cpy = copy.copy(item)
|
||||
item.__init__(None)
|
||||
if is_live:
|
||||
self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Live, file)
|
||||
# TODO: To Path object
|
||||
self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Live,
|
||||
str(file_path))
|
||||
else:
|
||||
self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Preview, file)
|
||||
# TODO: To Path object
|
||||
self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Preview,
|
||||
str(file_path))
|
||||
# Some of the original serviceitem attributes is needed in the new serviceitem
|
||||
item.footer = item_cpy.footer
|
||||
item.from_service = item_cpy.from_service
|
||||
@ -352,13 +355,13 @@ class MessageListener(object):
|
||||
self.handler = None
|
||||
else:
|
||||
if self.handler == self.media_item.automatic:
|
||||
self.handler = self.media_item.find_controller_by_type(file)
|
||||
self.handler = self.media_item.find_controller_by_type(file_path)
|
||||
if not self.handler:
|
||||
return
|
||||
else:
|
||||
# the saved handler is not present so need to use one based on file suffix.
|
||||
# the saved handler is not present so need to use one based on file_path suffix.
|
||||
if not self.controllers[self.handler].available:
|
||||
self.handler = self.media_item.find_controller_by_type(file)
|
||||
self.handler = self.media_item.find_controller_by_type(file_path)
|
||||
if not self.handler:
|
||||
return
|
||||
if is_live:
|
||||
@ -370,7 +373,7 @@ class MessageListener(object):
|
||||
if self.handler is None:
|
||||
self.controller = controller
|
||||
else:
|
||||
controller.add_handler(self.controllers[self.handler], file, hide_mode, message[3])
|
||||
controller.add_handler(self.controllers[self.handler], file_path, hide_mode, message[3])
|
||||
self.timer.start()
|
||||
|
||||
def slide(self, message):
|
||||
|
@ -23,13 +23,13 @@
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
from shutil import which
|
||||
from subprocess import check_output, CalledProcessError
|
||||
|
||||
from openlp.core.common import AppLocation, check_binary_exists
|
||||
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.shutil import which
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
if is_win():
|
||||
@ -66,11 +66,12 @@ class PdfController(PresentationController):
|
||||
Function that checks whether a binary is either ghostscript or mudraw or neither.
|
||||
Is also used from presentationtab.py
|
||||
|
||||
:param 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: Type of the binary, 'gs' if ghostscript, 'mudraw' if mudraw, None if invalid.
|
||||
:rtype: str | None
|
||||
"""
|
||||
program_type = None
|
||||
runlog = check_binary_exists(Path(program_path))
|
||||
runlog = check_binary_exists(program_path)
|
||||
# Analyse the output to see it the program is mudraw, ghostscript or neither
|
||||
for line in runlog.splitlines():
|
||||
decoded_line = line.decode()
|
||||
@ -107,30 +108,29 @@ class PdfController(PresentationController):
|
||||
:return: True if program to open PDF-files was found, otherwise False.
|
||||
"""
|
||||
log.debug('check_installed Pdf')
|
||||
self.mudrawbin = ''
|
||||
self.mutoolbin = ''
|
||||
self.gsbin = ''
|
||||
self.mudrawbin = None
|
||||
self.mutoolbin = None
|
||||
self.gsbin = None
|
||||
self.also_supports = []
|
||||
# Use the user defined program if given
|
||||
if Settings().value('presentations/enable_pdf_program'):
|
||||
pdf_program = path_to_str(Settings().value('presentations/pdf_program'))
|
||||
program_type = self.process_check_binary(pdf_program)
|
||||
program_path = Settings().value('presentations/pdf_program')
|
||||
program_type = self.process_check_binary(program_path)
|
||||
if program_type == 'gs':
|
||||
self.gsbin = pdf_program
|
||||
self.gsbin = program_path
|
||||
elif program_type == 'mudraw':
|
||||
self.mudrawbin = pdf_program
|
||||
self.mudrawbin = program_path
|
||||
elif program_type == 'mutool':
|
||||
self.mutoolbin = pdf_program
|
||||
self.mutoolbin = program_path
|
||||
else:
|
||||
# Fallback to autodetection
|
||||
application_path = str(AppLocation.get_directory(AppLocation.AppDir))
|
||||
application_path = AppLocation.get_directory(AppLocation.AppDir)
|
||||
if is_win():
|
||||
# for windows we only accept mudraw.exe or mutool.exe in the base folder
|
||||
application_path = str(AppLocation.get_directory(AppLocation.AppDir))
|
||||
if os.path.isfile(os.path.join(application_path, 'mudraw.exe')):
|
||||
self.mudrawbin = os.path.join(application_path, 'mudraw.exe')
|
||||
elif os.path.isfile(os.path.join(application_path, 'mutool.exe')):
|
||||
self.mutoolbin = os.path.join(application_path, 'mutool.exe')
|
||||
if (application_path / 'mudraw.exe').is_file():
|
||||
self.mudrawbin = application_path / 'mudraw.exe'
|
||||
elif (application_path / 'mutool.exe').is_file():
|
||||
self.mutoolbin = application_path / 'mutool.exe'
|
||||
else:
|
||||
DEVNULL = open(os.devnull, 'wb')
|
||||
# First try to find mudraw
|
||||
@ -143,11 +143,11 @@ class PdfController(PresentationController):
|
||||
self.gsbin = which('gs')
|
||||
# Last option: check if mudraw or mutool is placed in OpenLP base folder
|
||||
if not self.mudrawbin and not self.mutoolbin and not self.gsbin:
|
||||
application_path = str(AppLocation.get_directory(AppLocation.AppDir))
|
||||
if os.path.isfile(os.path.join(application_path, 'mudraw')):
|
||||
self.mudrawbin = os.path.join(application_path, 'mudraw')
|
||||
elif os.path.isfile(os.path.join(application_path, 'mutool')):
|
||||
self.mutoolbin = os.path.join(application_path, 'mutool')
|
||||
application_path = AppLocation.get_directory(AppLocation.AppDir)
|
||||
if (application_path / 'mudraw').is_file():
|
||||
self.mudrawbin = application_path / 'mudraw'
|
||||
elif (application_path / 'mutool').is_file():
|
||||
self.mutoolbin = application_path / 'mutool'
|
||||
if self.mudrawbin or self.mutoolbin:
|
||||
self.also_supports = ['xps', 'oxps']
|
||||
return True
|
||||
@ -172,12 +172,15 @@ class PdfDocument(PresentationDocument):
|
||||
image-serviceitem on the fly and present as such. Therefore some of the 'playback'
|
||||
functions is not implemented.
|
||||
"""
|
||||
def __init__(self, controller, presentation):
|
||||
def __init__(self, controller, document_path):
|
||||
"""
|
||||
Constructor, store information about the file and initialise.
|
||||
|
||||
:param openlp.core.common.path.Path document_path: Path to the document to load
|
||||
:rtype: None
|
||||
"""
|
||||
log.debug('Init Presentation Pdf')
|
||||
PresentationDocument.__init__(self, controller, presentation)
|
||||
super().__init__(controller, document_path)
|
||||
self.presentation = None
|
||||
self.blanked = False
|
||||
self.hidden = False
|
||||
@ -200,13 +203,13 @@ class PdfDocument(PresentationDocument):
|
||||
:return: The resolution dpi to be used.
|
||||
"""
|
||||
# Use a postscript script to get size of the pdf. It is assumed that all pages have same size
|
||||
gs_resolution_script = str(AppLocation.get_directory(
|
||||
AppLocation.PluginsDir)) + '/presentations/lib/ghostscript_get_resolution.ps'
|
||||
gs_resolution_script = AppLocation.get_directory(
|
||||
AppLocation.PluginsDir) / 'presentations' / 'lib' / 'ghostscript_get_resolution.ps'
|
||||
# Run the script on the pdf to get the size
|
||||
runlog = []
|
||||
try:
|
||||
runlog = check_output([self.controller.gsbin, '-dNOPAUSE', '-dNODISPLAY', '-dBATCH',
|
||||
'-sFile=' + self.file_path, gs_resolution_script],
|
||||
runlog = check_output([str(self.controller.gsbin), '-dNOPAUSE', '-dNODISPLAY', '-dBATCH',
|
||||
'-sFile={file_path}'.format(file_path=self.file_path), str(gs_resolution_script)],
|
||||
startupinfo=self.startupinfo)
|
||||
except CalledProcessError as e:
|
||||
log.debug(' '.join(e.cmd))
|
||||
@ -246,7 +249,7 @@ class PdfDocument(PresentationDocument):
|
||||
created_files = sorted(temp_dir_path.glob('*'))
|
||||
for image_path in created_files:
|
||||
if image_path.is_file():
|
||||
self.image_files.append(str(image_path))
|
||||
self.image_files.append(image_path)
|
||||
self.num_pages = len(self.image_files)
|
||||
return True
|
||||
size = ScreenList().current['size']
|
||||
@ -258,27 +261,27 @@ class PdfDocument(PresentationDocument):
|
||||
# The %03d in the file name is handled by each binary
|
||||
if self.controller.mudrawbin:
|
||||
log.debug('loading presentation using mudraw')
|
||||
runlog = check_output([self.controller.mudrawbin, '-w', str(size.width()), '-h', str(size.height()),
|
||||
'-o', str(temp_dir_path / 'mainslide%03d.png'), self.file_path],
|
||||
runlog = check_output([str(self.controller.mudrawbin), '-w', str(size.width()), '-h', str(size.height()),
|
||||
'-o', str(temp_dir_path / 'mainslide%03d.png'), str(self.file_path)],
|
||||
startupinfo=self.startupinfo)
|
||||
elif self.controller.mutoolbin:
|
||||
log.debug('loading presentation using mutool')
|
||||
runlog = check_output([self.controller.mutoolbin, 'draw', '-w', str(size.width()), '-h',
|
||||
str(size.height()),
|
||||
'-o', str(temp_dir_path / 'mainslide%03d.png'), self.file_path],
|
||||
runlog = check_output([str(self.controller.mutoolbin), 'draw', '-w', str(size.width()),
|
||||
'-h', str(size.height()), '-o', str(temp_dir_path / 'mainslide%03d.png'),
|
||||
str(self.file_path)],
|
||||
startupinfo=self.startupinfo)
|
||||
elif self.controller.gsbin:
|
||||
log.debug('loading presentation using gs')
|
||||
resolution = self.gs_get_resolution(size)
|
||||
runlog = check_output([self.controller.gsbin, '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m',
|
||||
'-r' + str(resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4',
|
||||
'-sOutputFile=' + str(temp_dir_path / 'mainslide%03d.png'),
|
||||
self.file_path], startupinfo=self.startupinfo)
|
||||
runlog = check_output([str(self.controller.gsbin), '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m',
|
||||
'-r{res}'.format(res=resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4',
|
||||
'-sOutputFile={output}'.format(output=temp_dir_path / 'mainslide%03d.png'),
|
||||
str(self.file_path)], startupinfo=self.startupinfo)
|
||||
created_files = sorted(temp_dir_path.glob('*'))
|
||||
for image_path in created_files:
|
||||
if image_path.is_file():
|
||||
self.image_files.append(str(image_path))
|
||||
except Exception:
|
||||
self.image_files.append(image_path)
|
||||
except Exception as e:
|
||||
log.exception(runlog)
|
||||
return False
|
||||
self.num_pages = len(self.image_files)
|
||||
|
@ -120,15 +120,16 @@ class PowerpointDocument(PresentationDocument):
|
||||
Class which holds information and controls a single presentation.
|
||||
"""
|
||||
|
||||
def __init__(self, controller, presentation):
|
||||
def __init__(self, controller, document_path):
|
||||
"""
|
||||
Constructor, store information about the file and initialise.
|
||||
|
||||
:param controller:
|
||||
:param presentation:
|
||||
:param openlp.core.common.path.Path document_path: Path to the document to load
|
||||
:rtype: None
|
||||
"""
|
||||
log.debug('Init Presentation Powerpoint')
|
||||
super(PowerpointDocument, self).__init__(controller, presentation)
|
||||
super().__init__(controller, document_path)
|
||||
self.presentation = None
|
||||
self.index_map = {}
|
||||
self.slide_count = 0
|
||||
@ -145,7 +146,7 @@ class PowerpointDocument(PresentationDocument):
|
||||
try:
|
||||
if not self.controller.process:
|
||||
self.controller.start_process()
|
||||
self.controller.process.Presentations.Open(os.path.normpath(self.file_path), False, False, False)
|
||||
self.controller.process.Presentations.Open(str(self.file_path), False, False, False)
|
||||
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
|
||||
self.create_thumbnails()
|
||||
self.create_titles_and_notes()
|
||||
@ -363,9 +364,8 @@ class PowerpointDocument(PresentationDocument):
|
||||
width=size.width(),
|
||||
horizontal=(right - left)))
|
||||
log.debug('window title: {title}'.format(title=window_title))
|
||||
filename_root, filename_ext = os.path.splitext(os.path.basename(self.file_path))
|
||||
if size.y() == top and size.height() == (bottom - top) and size.x() == left and \
|
||||
size.width() == (right - left) and filename_root in window_title:
|
||||
size.width() == (right - left) and self.file_path.stem in window_title:
|
||||
log.debug('Found a match and will save the handle')
|
||||
self.presentation_hwnd = hwnd
|
||||
# Stop powerpoint from flashing in the taskbar
|
||||
|
@ -85,9 +85,9 @@ class PptviewController(PresentationController):
|
||||
if self.process:
|
||||
return
|
||||
log.debug('start PPTView')
|
||||
dll_path = os.path.join(str(AppLocation.get_directory(AppLocation.AppDir)),
|
||||
'plugins', 'presentations', 'lib', 'pptviewlib', 'pptviewlib.dll')
|
||||
self.process = cdll.LoadLibrary(dll_path)
|
||||
dll_path = AppLocation.get_directory(AppLocation.AppDir) \
|
||||
/ 'plugins' / 'presentations' / 'lib' / 'pptviewlib' / 'pptviewlib.dll'
|
||||
self.process = cdll.LoadLibrary(str(dll_path))
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
self.process.SetDebug(1)
|
||||
|
||||
@ -104,12 +104,15 @@ class PptviewDocument(PresentationDocument):
|
||||
"""
|
||||
Class which holds information and controls a single presentation.
|
||||
"""
|
||||
def __init__(self, controller, presentation):
|
||||
def __init__(self, controller, document_path):
|
||||
"""
|
||||
Constructor, store information about the file and initialise.
|
||||
|
||||
:param openlp.core.common.path.Path document_path: File path to the document to load
|
||||
:rtype: None
|
||||
"""
|
||||
log.debug('Init Presentation PowerPoint')
|
||||
super(PptviewDocument, self).__init__(controller, presentation)
|
||||
super().__init__(controller, document_path)
|
||||
self.presentation = None
|
||||
self.ppt_id = None
|
||||
self.blanked = False
|
||||
@ -121,17 +124,16 @@ class PptviewDocument(PresentationDocument):
|
||||
the background PptView task started earlier.
|
||||
"""
|
||||
log.debug('LoadPresentation')
|
||||
temp_dir_path = self.get_temp_folder()
|
||||
temp_path = self.get_temp_folder()
|
||||
size = ScreenList().current['size']
|
||||
rect = RECT(size.x(), size.y(), size.right(), size.bottom())
|
||||
self.file_path = os.path.normpath(self.file_path)
|
||||
preview_path = temp_dir_path / 'slide'
|
||||
preview_path = temp_path / 'slide'
|
||||
# Ensure that the paths are null terminated
|
||||
byte_file_path = self.file_path.encode('utf-16-le') + b'\0'
|
||||
preview_file_name = str(preview_path).encode('utf-16-le') + b'\0'
|
||||
if not temp_dir_path:
|
||||
temp_dir_path.mkdir(parents=True)
|
||||
self.ppt_id = self.controller.process.OpenPPT(byte_file_path, None, rect, preview_file_name)
|
||||
file_path_utf16 = str(self.file_path).encode('utf-16-le') + b'\0'
|
||||
preview_path_utf16 = str(preview_path).encode('utf-16-le') + b'\0'
|
||||
if not temp_path.is_dir():
|
||||
temp_path.mkdir(parents=True)
|
||||
self.ppt_id = self.controller.process.OpenPPT(file_path_utf16, None, rect, preview_path_utf16)
|
||||
if self.ppt_id >= 0:
|
||||
self.create_thumbnails()
|
||||
self.stop_presentation()
|
||||
@ -148,7 +150,7 @@ class PptviewDocument(PresentationDocument):
|
||||
return
|
||||
log.debug('create_thumbnails proceeding')
|
||||
for idx in range(self.get_slide_count()):
|
||||
path = '{folder}\\slide{index}.bmp'.format(folder=self.get_temp_folder(), index=str(idx + 1))
|
||||
path = self.get_temp_folder() / 'slide{index:d}.bmp'.format(index=idx + 1)
|
||||
self.convert_thumbnail(path, idx + 1)
|
||||
|
||||
def create_titles_and_notes(self):
|
||||
@ -161,13 +163,12 @@ class PptviewDocument(PresentationDocument):
|
||||
"""
|
||||
titles = None
|
||||
notes = None
|
||||
filename = os.path.normpath(self.file_path)
|
||||
# let's make sure we have a valid zipped presentation
|
||||
if os.path.exists(filename) and zipfile.is_zipfile(filename):
|
||||
if self.file_path.exists() and zipfile.is_zipfile(str(self.file_path)):
|
||||
namespaces = {"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
|
||||
"a": "http://schemas.openxmlformats.org/drawingml/2006/main"}
|
||||
# open the file
|
||||
with zipfile.ZipFile(filename) as zip_file:
|
||||
with zipfile.ZipFile(str(self.file_path)) as zip_file:
|
||||
# find the presentation.xml to get the slide count
|
||||
with zip_file.open('ppt/presentation.xml') as pres:
|
||||
tree = ElementTree.parse(pres)
|
||||
|
@ -19,10 +19,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
@ -87,19 +84,26 @@ class PresentationDocument(object):
|
||||
Returns a path to an image containing a preview for the requested slide
|
||||
|
||||
"""
|
||||
def __init__(self, controller, name):
|
||||
def __init__(self, controller, document_path):
|
||||
"""
|
||||
Constructor for the PresentationController class
|
||||
|
||||
:param controller:
|
||||
:param openlp.core.common.path.Path document_path: Path to the document to load.
|
||||
:rtype: None
|
||||
"""
|
||||
self.controller = controller
|
||||
self._setup(name)
|
||||
self._setup(document_path)
|
||||
|
||||
def _setup(self, name):
|
||||
def _setup(self, document_path):
|
||||
"""
|
||||
Run some initial setup. This method is separate from __init__ in order to mock it out in tests.
|
||||
|
||||
:param openlp.core.common.path.Path document_path: Path to the document to load.
|
||||
:rtype: None
|
||||
"""
|
||||
self.slide_number = 0
|
||||
self.file_path = name
|
||||
self.file_path = document_path
|
||||
check_directory_exists(self.get_thumbnail_folder())
|
||||
|
||||
def load_presentation(self):
|
||||
@ -126,12 +130,6 @@ class PresentationDocument(object):
|
||||
except OSError:
|
||||
log.exception('Failed to delete presentation controller files')
|
||||
|
||||
def get_file_name(self):
|
||||
"""
|
||||
Return just the filename of the presentation, without the directory
|
||||
"""
|
||||
return os.path.split(self.file_path)[1]
|
||||
|
||||
def get_thumbnail_folder(self):
|
||||
"""
|
||||
The location where thumbnail images will be stored
|
||||
@ -141,9 +139,9 @@ class PresentationDocument(object):
|
||||
"""
|
||||
# TODO: If statement can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
|
||||
if Settings().value('presentations/thumbnail_scheme') == 'md5':
|
||||
folder = md5_hash(self.file_path.encode('utf-8'))
|
||||
folder = md5_hash(bytes(self.file_path))
|
||||
else:
|
||||
folder = self.get_file_name()
|
||||
folder = self.file_path.name
|
||||
return Path(self.controller.thumbnail_folder, folder)
|
||||
|
||||
def get_temp_folder(self):
|
||||
@ -155,19 +153,22 @@ class PresentationDocument(object):
|
||||
"""
|
||||
# TODO: If statement can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
|
||||
if Settings().value('presentations/thumbnail_scheme') == 'md5':
|
||||
folder = md5_hash(self.file_path.encode('utf-8'))
|
||||
folder = md5_hash(bytes(self.file_path))
|
||||
else:
|
||||
folder = self.get_file_name()
|
||||
folder = self.file_path.name
|
||||
return Path(self.controller.temp_folder, folder)
|
||||
|
||||
def check_thumbnails(self):
|
||||
"""
|
||||
Returns ``True`` if the thumbnail images exist and are more recent than the powerpoint file.
|
||||
Check that the last thumbnail image exists and is valid and are more recent than the powerpoint file.
|
||||
|
||||
:return: If the thumbnail is valid
|
||||
:rtype: bool
|
||||
"""
|
||||
last_image = self.get_thumbnail_path(self.get_slide_count(), True)
|
||||
if not (last_image and os.path.isfile(last_image)):
|
||||
last_image_path = self.get_thumbnail_path(self.get_slide_count(), True)
|
||||
if not (last_image_path and last_image_path.is_file()):
|
||||
return False
|
||||
return validate_thumb(self.file_path, last_image)
|
||||
return validate_thumb(self.file_path, last_image_path)
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
@ -250,24 +251,28 @@ class PresentationDocument(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def convert_thumbnail(self, file, idx):
|
||||
def convert_thumbnail(self, image_path, index):
|
||||
"""
|
||||
Convert the slide image the application made to a scaled 360px height .png image.
|
||||
|
||||
:param openlp.core.common.path.Path image_path: Path to the image to create a thumb nail of
|
||||
:param int index: The index of the slide to create the thumbnail for.
|
||||
:rtype: None
|
||||
"""
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
if os.path.isfile(file):
|
||||
thumb_path = self.get_thumbnail_path(idx, False)
|
||||
create_thumb(file, thumb_path, False, QtCore.QSize(-1, 360))
|
||||
if image_path.is_file():
|
||||
thumb_path = self.get_thumbnail_path(index, False)
|
||||
create_thumb(str(image_path), str(thumb_path), False, QtCore.QSize(-1, 360))
|
||||
|
||||
def get_thumbnail_path(self, slide_no, check_exists=True):
|
||||
def get_thumbnail_path(self, slide_no, check_exists=False):
|
||||
"""
|
||||
Returns an image path containing a preview for the requested slide
|
||||
|
||||
:param int slide_no: The slide an image is required for, starting at 1
|
||||
:param bool check_exists: Check if the generated path exists
|
||||
:return: The path, or None if the :param:`check_exists` is True and the file does not exist
|
||||
:rtype: openlp.core.common.path.Path, None
|
||||
:rtype: openlp.core.common.path.Path | None
|
||||
"""
|
||||
path = self.get_thumbnail_folder() / (self.controller.thumbnail_prefix + str(slide_no) + '.png')
|
||||
if path.is_file() or not check_exists:
|
||||
@ -313,43 +318,38 @@ class PresentationDocument(object):
|
||||
Reads the titles from the titles file and
|
||||
the notes files and returns the content in two lists
|
||||
"""
|
||||
titles = []
|
||||
notes = []
|
||||
titles_file = str(self.get_thumbnail_folder() / 'titles.txt')
|
||||
if os.path.exists(titles_file):
|
||||
try:
|
||||
with open(titles_file, encoding='utf-8') as fi:
|
||||
titles = fi.read().splitlines()
|
||||
except:
|
||||
log.exception('Failed to open/read existing titles file')
|
||||
titles = []
|
||||
titles_path = self.get_thumbnail_folder() / 'titles.txt'
|
||||
try:
|
||||
titles = titles_path.read_text().splitlines()
|
||||
except:
|
||||
log.exception('Failed to open/read existing titles file')
|
||||
titles = []
|
||||
for slide_no, title in enumerate(titles, 1):
|
||||
notes_file = str(self.get_thumbnail_folder() / 'slideNotes{number:d}.txt'.format(number=slide_no))
|
||||
note = ''
|
||||
if os.path.exists(notes_file):
|
||||
try:
|
||||
with open(notes_file, encoding='utf-8') as fn:
|
||||
note = fn.read()
|
||||
except:
|
||||
log.exception('Failed to open/read notes file')
|
||||
note = ''
|
||||
notes_path = self.get_thumbnail_folder() / 'slideNotes{number:d}.txt'.format(number=slide_no)
|
||||
try:
|
||||
note = notes_path.read_text()
|
||||
except:
|
||||
log.exception('Failed to open/read notes file')
|
||||
note = ''
|
||||
notes.append(note)
|
||||
return titles, notes
|
||||
|
||||
def save_titles_and_notes(self, titles, notes):
|
||||
"""
|
||||
Performs the actual persisting of titles to the titles.txt
|
||||
and notes to the slideNote%.txt
|
||||
Performs the actual persisting of titles to the titles.txt and notes to the slideNote%.txt
|
||||
|
||||
:param list[str] titles: The titles to save
|
||||
:param list[str] notes: The notes to save
|
||||
:rtype: None
|
||||
"""
|
||||
if titles:
|
||||
titles_path = self.get_thumbnail_folder() / 'titles.txt'
|
||||
with titles_path.open(mode='wt', encoding='utf-8') as fo:
|
||||
fo.writelines(titles)
|
||||
titles_path.write_text('\n'.join(titles))
|
||||
if notes:
|
||||
for slide_no, note in enumerate(notes, 1):
|
||||
notes_path = self.get_thumbnail_folder() / 'slideNotes{number:d}.txt'.format(number=slide_no)
|
||||
with notes_path.open(mode='wt', encoding='utf-8') as fn:
|
||||
fn.write(note)
|
||||
notes_path.write_text(note)
|
||||
|
||||
|
||||
class PresentationController(object):
|
||||
@ -426,12 +426,11 @@ class PresentationController(object):
|
||||
self.document_class = document_class
|
||||
self.settings_section = self.plugin.settings_section
|
||||
self.available = None
|
||||
self.temp_folder = os.path.join(str(AppLocation.get_section_data_path(self.settings_section)), name)
|
||||
self.thumbnail_folder = os.path.join(
|
||||
str(AppLocation.get_section_data_path(self.settings_section)), 'thumbnails')
|
||||
self.temp_folder = AppLocation.get_section_data_path(self.settings_section) / name
|
||||
self.thumbnail_folder = AppLocation.get_section_data_path(self.settings_section) / 'thumbnails'
|
||||
self.thumbnail_prefix = 'slide'
|
||||
check_directory_exists(Path(self.thumbnail_folder))
|
||||
check_directory_exists(Path(self.temp_folder))
|
||||
check_directory_exists(self.thumbnail_folder)
|
||||
check_directory_exists(self.temp_folder)
|
||||
|
||||
def enabled(self):
|
||||
"""
|
||||
@ -466,11 +465,15 @@ class PresentationController(object):
|
||||
log.debug('Kill')
|
||||
self.close_presentation()
|
||||
|
||||
def add_document(self, name):
|
||||
def add_document(self, document_path):
|
||||
"""
|
||||
Called when a new presentation document is opened.
|
||||
|
||||
:param openlp.core.common.path.Path document_path: Path to the document to load
|
||||
:return: The document
|
||||
:rtype: PresentationDocument
|
||||
"""
|
||||
document = self.document_class(self, name)
|
||||
document = self.document_class(self, document_path)
|
||||
self.docs.append(document)
|
||||
return document
|
||||
|
||||
|
@ -38,7 +38,6 @@ class PresentationTab(SettingsTab):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
self.parent = parent
|
||||
self.controllers = controllers
|
||||
super(PresentationTab, self).__init__(parent, title, visible_title, icon_path)
|
||||
self.activated = False
|
||||
@ -194,7 +193,7 @@ class PresentationTab(SettingsTab):
|
||||
pdf_program_path = self.program_path_edit.path
|
||||
enable_pdf_program = self.pdf_program_check_box.checkState()
|
||||
# If the given program is blank disable using the program
|
||||
if not pdf_program_path:
|
||||
if pdf_program_path is None:
|
||||
enable_pdf_program = 0
|
||||
if pdf_program_path != Settings().value(self.settings_section + '/pdf_program'):
|
||||
Settings().setValue(self.settings_section + '/pdf_program', pdf_program_path)
|
||||
@ -220,9 +219,11 @@ class PresentationTab(SettingsTab):
|
||||
|
||||
def on_program_path_edit_path_changed(self, new_path):
|
||||
"""
|
||||
Select the mudraw or ghostscript binary that should be used.
|
||||
Handle the `pathEditChanged` signal from program_path_edit
|
||||
|
||||
:param openlp.core.common.path.Path new_path: File path to the new program
|
||||
:rtype: None
|
||||
"""
|
||||
new_path = path_to_str(new_path)
|
||||
if new_path:
|
||||
if not PdfController.process_check_binary(new_path):
|
||||
critical_error_message_box(UiStrings().Error,
|
||||
|
@ -244,20 +244,3 @@ class TestPresentationDocument(TestCase):
|
||||
|
||||
# THEN: load_presentation should return false
|
||||
self.assertFalse(result, "PresentationDocument.load_presentation should return false.")
|
||||
|
||||
def test_get_file_name(self):
|
||||
"""
|
||||
Test the PresentationDocument.get_file_name method.
|
||||
"""
|
||||
|
||||
# GIVEN: A mocked os.path.split which returns a list, an instance of PresentationDocument and
|
||||
# arbitary file_path.
|
||||
self.mock_os.path.split.return_value = ['directory', 'file.ext']
|
||||
instance = PresentationDocument(self.mock_controller, 'Name')
|
||||
instance.file_path = 'filepath'
|
||||
|
||||
# WHEN: Calling get_file_name
|
||||
result = instance.get_file_name()
|
||||
|
||||
# THEN: get_file_name should return 'file.ext'
|
||||
self.assertEqual(result, 'file.ext')
|
||||
|
Loading…
Reference in New Issue
Block a user