diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 6343eee00..1de76f0a3 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -33,7 +33,7 @@ import ntpath from PyQt4 import QtGui -from openlp.core.common import RegistryProperties, Settings, translate, AppLocation +from openlp.core.common import RegistryProperties, Settings, translate, AppLocation, md5_hash from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags, create_thumb log = logging.getLogger(__name__) @@ -326,6 +326,12 @@ class ServiceItem(RegistryProperties): # If the item should have a display title but this frame doesn't have one, we make one up if self.is_capable(ItemCapabilities.HasDisplayTitle) and not display_title: display_title = translate('OpenLP.ServiceItem', '[slide %d]') % (len(self._raw_frames) + 1) + # Update image path to match servicemanager location if file was loaded from service + if image and not self.has_original_files and self.name == 'presentations': + file_location = os.path.join(path, file_name) + file_location_hash = md5_hash(file_location.encode('utf-8')) + image = os.path.join(AppLocation.get_section_data_path(self.name), 'thumbnails', + file_location_hash, ntpath.basename(image)) self._raw_frames.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title, 'notes': notes}) self._new_item() diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 3b6a4b2c7..6ac62c32a 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -1106,8 +1106,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): self.image_manager.stop_manager = True while self.image_manager.image_thread.isRunning(): time.sleep(0.1) - # Clean temporary files used by services - self.service_manager_contents.clean_up() if save_settings: if Settings().value('advanced/save current plugin'): Settings().setValue('advanced/current media plugin', self.media_tool_box.currentIndex()) @@ -1124,6 +1122,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): if self.live_controller.display: self.live_controller.display.close() self.live_controller.display = None + # Clean temporary files used by services + self.service_manager_contents.clean_up() if is_win(): # Needed for Windows to stop crashes on exit Registry().remove('application') diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index ea896fad7..974db7b06 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -24,6 +24,7 @@ The :mod:`openlp.core.utils` module provides the utility libraries for OpenLP. """ from datetime import datetime from distutils.version import LooseVersion +from http.client import HTTPException import logging import locale import os @@ -414,6 +415,11 @@ def get_web_page(url, header=None, update_openlp=False): page = None if retries > CONNECTION_RETRIES: raise + except socket.gaierror: + log.exception('Socket gaierror: {}'.format(url)) + page = None + if retries > CONNECTION_RETRIES: + raise except ConnectionRefusedError: log.exception('ConnectionRefused: {}'.format(url)) page = None @@ -425,6 +431,11 @@ def get_web_page(url, header=None, update_openlp=False): page = None if retries > CONNECTION_RETRIES: raise + except HTTPException: + log.exception('HTTPException error: {}'.format(url)) + page = None + if retries > CONNECTION_RETRIES: + raise except: # Don't know what's happening, so reraise the original raise diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index fbf18d98e..7cb13df3d 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -748,7 +748,10 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre """ if not reference_url: return None - page = get_web_page(reference_url, header, True) + try: + page = get_web_page(reference_url, header, True) + except: + page = None if not page: send_error_message('download') return None diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index d70dfc8a4..0f3574459 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -35,7 +35,7 @@ import logging import os import time -from openlp.core.common import is_win +from openlp.core.common import is_win, Registry if is_win(): from win32com.client import Dispatch @@ -231,21 +231,13 @@ class ImpressDocument(PresentationDocument): return False self.desktop = desktop properties = [] - if not is_win(): - # Recent versions of Impress on Windows won't start the presentation if it starts as minimized. It seems OK - # on Linux though. - properties.append(self.create_property('Minimized', True)) + properties.append(self.create_property('Hidden', True)) properties = tuple(properties) try: self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties) except: log.warning('Failed to load presentation %s' % url) return False - if is_win(): - # As we can't start minimized the Impress window gets in the way. - # Either window.setPosSize(0, 0, 200, 400, 12) or .setVisible(False) - window = self.document.getCurrentController().getFrame().getContainerWindow() - window.setVisible(False) self.presentation = self.document.getPresentation() self.presentation.Display = ScreenList().current['number'] + 1 self.control = None @@ -382,6 +374,8 @@ class ImpressDocument(PresentationDocument): """ log.debug('start presentation OpenOffice') if self.control is None or not self.control.isRunning(): + window = self.document.getCurrentController().getFrame().getContainerWindow() + window.setVisible(True) self.presentation.start() self.control = self.presentation.getController() # start() returns before the Component is ready. Try for 15 seconds. @@ -390,9 +384,13 @@ class ImpressDocument(PresentationDocument): time.sleep(0.1) sleep_count += 1 self.control = self.presentation.getController() + window.setVisible(False) else: self.control.activate() self.goto_slide(1) + # Make sure impress doesn't steal focus, unless we're on a single screen setup + if len(ScreenList().screen_list) > 1: + Registry().get('main_window').activateWindow() def get_slide_number(self): """ diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 63fd20ba5..b031da203 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -281,6 +281,7 @@ class PresentationMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.CanPreview) service_item.add_capability(ItemCapabilities.CanLoop) service_item.add_capability(ItemCapabilities.CanAppend) + service_item.name = 'images' # force a nonexistent theme service_item.theme = -1 for bitem in items: diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 69a23cc7f..48d5722e0 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -243,6 +243,10 @@ class Controller(object): Instruct the controller to stop and hide the presentation. """ log.debug('Live = %s, stop' % self.is_live) + # The document has not been loaded yet, so don't do anything. This can happen when going live with a + # presentation while blanked to desktop. + if not self.doc: + return # Save the current slide number to be able to return to this slide if the presentation is activated again. if self.doc.is_active(): self.doc.slidenumber = self.doc.get_slide_number() @@ -352,6 +356,7 @@ class MessageListener(object): self.controller = controller else: controller.add_handler(self.controllers[self.handler], file, hide_mode, message[3]) + self.timer.start() def slide(self, message): """ @@ -423,6 +428,7 @@ class MessageListener(object): is_live = message[1] if is_live: self.live_handler.shutdown() + self.timer.stop() else: self.preview_handler.shutdown() diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 257d9659e..c16c54205 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -89,11 +89,11 @@ class PdfController(PresentationController): # Analyse the output to see it the program is mudraw, ghostscript or neither for line in runlog.splitlines(): decoded_line = line.decode() - found_mudraw = re.search('usage: mudraw.*', decoded_line) + found_mudraw = re.search('usage: mudraw.*', decoded_line, re.IGNORECASE) if found_mudraw: program_type = 'mudraw' break - found_gs = re.search('GPL Ghostscript.*', decoded_line) + found_gs = re.search('GPL Ghostscript.*', decoded_line, re.IGNORECASE) if found_gs: program_type = 'gs' break @@ -222,8 +222,8 @@ class PdfDocument(PresentationDocument): continue # Calculate the ratio from pdf to screen if width > 0 and height > 0: - width_ratio = size.right() / width - height_ratio = size.bottom() / height + width_ratio = size.width() / width + height_ratio = size.height() / height # return the resolution that should be used. 72 is default. if width_ratio > height_ratio: return int(height_ratio * 72) @@ -254,7 +254,7 @@ class PdfDocument(PresentationDocument): if not os.path.isdir(self.get_temp_folder()): os.makedirs(self.get_temp_folder()) if self.controller.mudrawbin: - runlog = check_output([self.controller.mudrawbin, '-w', str(size.right()), '-h', str(size.bottom()), + runlog = check_output([self.controller.mudrawbin, '-w', str(size.width()), '-h', str(size.height()), '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path], startupinfo=self.startupinfo) elif self.controller.gsbin: diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 107ebe4cd..a2e4e1c68 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -32,7 +32,7 @@ import time from openlp.core.common import is_win, Settings if is_win(): - from win32com.client import DispatchWithEvents + from win32com.client import Dispatch import win32com import win32con import winreg @@ -93,22 +93,9 @@ class PowerpointController(PresentationController): """ Loads PowerPoint process. """ - class PowerPointEvents: - """ - Class to catch events from PowerPoint. - """ - def OnSlideShowNextClick(self, slideshow_window, effect): - """ - Occurs on the next click of the slide. - If the main OpenLP window is not in focus force update of the slidecontroller. - """ - if not Registry().get('main_window').isActiveWindow(): - log.debug('main window is not in focus - should update slidecontroller') - Registry().execute('slidecontroller_live_change', slideshow_window.View.CurrentShowPosition) - log.debug('start_process') if not self.process: - self.process = DispatchWithEvents('PowerPoint.Application', PowerPointEvents) + self.process = Dispatch('PowerPoint.Application') def kill(self): """ @@ -330,6 +317,7 @@ class PowerpointDocument(PresentationDocument): """ log.debug('start_presentation') # SlideShowWindow measures its size/position by points, not pixels + # https://technet.microsoft.com/en-us/library/dn528846.aspx try: dpi = win32ui.GetActiveWindow().GetDC().GetDeviceCaps(88) except win32ui.error: @@ -378,8 +366,9 @@ class PowerpointDocument(PresentationDocument): log.debug('compare size: %d and %d, %d and %d, %d and %d, %d and %d' % (size.y(), top, size.height(), (bottom - top), size.x(), left, size.width(), (right - left))) log.debug('window title: %s' % 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 os.path.basename(self.file_path) in window_title: + size.width() == (right - left) and filename_root in window_title: log.debug('Found a match and will save the handle') self.presentation_hwnd = hwnd # Stop powerpoint from flashing in the taskbar diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index a93ceb7b9..f15012c70 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -128,7 +128,8 @@ class PresentationTab(SettingsTab): 'Clicking on a selected slide in the slidecontroller advances to next effect.')) self.ppt_window_check_box.setText( translate('PresentationPlugin.PresentationTab', - 'Let PowerPoint control the size and position of the presentation window.')) + 'Let PowerPoint control the size and position of the presentation window ' + '(workaround for Windows 8 scaling issue).')) self.pdf_program_check_box.setText( translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:')) diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index 23126ab56..db8df267f 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -28,7 +28,7 @@ from unittest import TestCase from tests.functional import MagicMock, patch from tests.utils import assert_length, convert_file_service_item -from openlp.core.common import Registry +from openlp.core.common import Registry, md5_hash from openlp.core.lib import ItemCapabilities, ServiceItem, ServiceItemType VERSE = 'The Lord said to {r}Noah{/r}: \n'\ @@ -244,6 +244,33 @@ class TestServiceItem(TestCase): self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command') self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match') + @patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') + def add_from_command_for_a_presentation_thumb_test(self, mocked_get_section_data_path): + """ + Test the Service Item - adding a presentation, and updating the thumb path + """ + # GIVEN: A service item, a mocked AppLocation and presentation data + mocked_get_section_data_path.return_value = os.path.join('mocked', 'section', 'path') + service_item = ServiceItem(None) + service_item.has_original_files = False + service_item.name = 'presentations' + presentation_name = 'test.pptx' + thumb = os.path.join('tmp', 'test', 'thumb.png') + display_title = 'DisplayTitle' + notes = 'Note1\nNote2\n' + expected_thumb_path = os.path.join('mocked', 'section', 'path', 'thumbnails', + md5_hash(os.path.join(TEST_PATH, presentation_name).encode('utf-8')), + 'thumb.png') + frame = {'title': presentation_name, 'image': expected_thumb_path, 'path': TEST_PATH, + 'display_title': display_title, 'notes': notes} + + # WHEN: adding presentation to service_item + service_item.add_from_command(TEST_PATH, presentation_name, thumb, display_title, notes) + + # THEN: verify that it is setup as a Command and that the frame data matches + self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command') + self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match') + def service_item_load_optical_media_from_service_test(self): """ Test the Service Item - load an optical media item diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py index 903f15119..a7b93f1da 100644 --- a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py @@ -135,5 +135,5 @@ class TestPdfController(TestCase, TestMixin): self.assertEqual(760, image.height(), 'The height should be 760') self.assertEqual(537, image.width(), 'The width should be 537') else: - self.assertEqual(767, image.height(), 'The height should be 767') + self.assertEqual(768, image.height(), 'The height should be 768') self.assertEqual(543, image.width(), 'The width should be 543')