From e9c0057b96c1ebe783bcb120db9504e7ad0449da Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Thu, 26 Mar 2015 15:17:14 +0100 Subject: [PATCH 1/5] Fix a traceback by changing filepath to file_path. Fixes bug 1389149. Fixes: https://launchpad.net/bugs/1389149 --- openlp/plugins/presentations/lib/messagelistener.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 778150039..c420d5f3a 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -93,7 +93,7 @@ class Controller(object): return True if not self.doc.is_loaded(): if not self.doc.load_presentation(): - log.warning('Failed to activate %s' % self.doc.filepath) + log.warning('Failed to activate %s' % self.doc.file_path) return False if self.is_live: self.doc.start_presentation() @@ -104,7 +104,7 @@ class Controller(object): if self.doc.is_active(): return True else: - log.warning('Failed to activate %s' % self.doc.filepath) + log.warning('Failed to activate %s' % self.doc.file_path) return False def slide(self, slide): From 0e6cff913a8d46165e7d4517383e89f78c4c982a Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Thu, 26 Mar 2015 15:22:23 +0100 Subject: [PATCH 2/5] Take focus back if Powerpoint steals it - fixes bug 1423913. Optionally advance a Powerpoint slides animation when clicked in the slidecontroller - fixes bug 1194847. Made OpenLP respect hidden slides. Improved logging in case of errors. Fixes: https://launchpad.net/bugs/1194847, https://launchpad.net/bugs/1423913 --- .../presentations/lib/powerpointcontroller.py | 175 ++++++++++++------ .../presentations/lib/presentationtab.py | 24 +++ .../presentations/presentationplugin.py | 3 +- 3 files changed, 149 insertions(+), 53 deletions(-) diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 550411760..9f192227d 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -22,11 +22,14 @@ """ This module is for controlling powerpoint. PPT API documentation: `http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_ +2010: https://msdn.microsoft.com/en-us/library/office/ff743835%28v=office.14%29.aspx +2013: https://msdn.microsoft.com/en-us/library/office/ff743835.aspx """ import os import logging +import time -from openlp.core.common import is_win +from openlp.core.common import is_win, Settings if is_win(): from win32com.client import Dispatch @@ -82,6 +85,7 @@ class PowerpointController(PresentationController): if not self.process: self.process = Dispatch('PowerPoint.Application') self.process.Visible = True + # ppWindowMinimized = 2 self.process.WindowState = 2 def kill(self): @@ -97,8 +101,10 @@ class PowerpointController(PresentationController): if self.process.Presentations.Count > 0: return self.process.Quit() - except (AttributeError, pywintypes.com_error): - pass + except (AttributeError, pywintypes.com_error) as e: + log.exception('Exception caught while killing powerpoint process') + log.exception(e) + trace_error_handler(log) self.process = None @@ -117,6 +123,8 @@ class PowerpointDocument(PresentationDocument): log.debug('Init Presentation Powerpoint') super(PowerpointDocument, self).__init__(controller, presentation) self.presentation = None + self.index_map = {} + self.slide_count = 0 def load_presentation(self): """ @@ -131,16 +139,21 @@ class PowerpointDocument(PresentationDocument): self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count) self.create_thumbnails() self.create_titles_and_notes() - # Powerpoint 2013 pops up when loading a file, so we minimize it again - if self.presentation.Application.Version == u'15.0': + # Powerpoint 2010 and 2013 pops up when loading a file, so we minimize it again + if float(self.presentation.Application.Version) >= 14.0: try: + # ppWindowMinimized = 2 self.presentation.Application.WindowState = 2 - except: - log.error('Failed to minimize main powerpoint window') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Failed to minimize main powerpoint window') + log.exception(e) trace_error_handler(log) + # Make sure powerpoint doesn't steal focus + Registry().get('main_window').activateWindow() return True - except pywintypes.com_error: - log.error('PPT open failed') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Exception caught while loading Powerpoint presentation') + log.exception(e) trace_error_handler(log) return False @@ -158,9 +171,14 @@ class PowerpointDocument(PresentationDocument): log.debug('create_thumbnails') if self.check_thumbnails(): return + key = 1 for num in range(self.presentation.Slides.Count): - self.presentation.Slides(num + 1).Export( - os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240) + if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden: + self.index_map[key] = num + 1 + self.presentation.Slides(num + 1).Export( + os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (key)), 'png', 320, 240) + key += 1 + self.slide_count = key - 1 def close_presentation(self): """ @@ -171,10 +189,14 @@ class PowerpointDocument(PresentationDocument): if self.presentation: try: self.presentation.Close() - except pywintypes.com_error: - pass + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while closing powerpoint presentation') + log.exception(e) + trace_error_handler(log) self.presentation = None self.controller.remove_doc(self) + # Make sure powerpoint doesn't steal focus + Registry().get('main_window').activateWindow() def is_loaded(self): """ @@ -188,7 +210,10 @@ class PowerpointDocument(PresentationDocument): return False if self.controller.process.Presentations.Count == 0: return False - except (AttributeError, pywintypes.com_error): + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in is_loaded') + log.exception(e) + trace_error_handler(log) return False return True @@ -204,7 +229,10 @@ class PowerpointDocument(PresentationDocument): return False if self.presentation.SlideShowWindow.View is None: return False - except (AttributeError, pywintypes.com_error): + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in is_active') + log.exception(e) + trace_error_handler(log) return False return True @@ -215,19 +243,21 @@ class PowerpointDocument(PresentationDocument): log.debug('unblank_screen') try: self.presentation.SlideShowSettings.Run() + # ppSlideShowRunning = 1 self.presentation.SlideShowWindow.View.State = 1 self.presentation.SlideShowWindow.Activate() - if self.presentation.Application.Version == '14.0': - # Unblanking is broken in PowerPoint 2010, need to redisplay - slide = self.presentation.SlideShowWindow.View.CurrentShowPosition - click = self.presentation.SlideShowWindow.View.GetClickIndex() - self.presentation.SlideShowWindow.View.GotoSlide(slide) - if click: - self.presentation.SlideShowWindow.View.GotoClick(click) - except pywintypes.com_error: - log.error('COM error while in unblank_screen') + # Unblanking is broken in PowerPoint 2010 and 2013, need to redisplay + if float(self.presentation.Application.Version) >= 14.0: + self.presentation.SlideShowWindow.View.GotoSlide(self.blank_slide, False) + if self.blank_click: + self.presentation.SlideShowWindow.View.GotoClick(self.blank_click) + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in unblank_screen') + log.exception(e) trace_error_handler(log) self.show_error_msg() + # Make sure powerpoint doesn't steal focus + Registry().get('main_window').activateWindow() def blank_screen(self): """ @@ -235,9 +265,15 @@ class PowerpointDocument(PresentationDocument): """ log.debug('blank_screen') try: + # Unblanking is broken in PowerPoint 2010 and 2013, need to save info for later + if float(self.presentation.Application.Version) >= 14.0: + self.blank_slide = self.get_slide_number() + self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex() + # ppSlideShowBlackScreen = 3 self.presentation.SlideShowWindow.View.State = 3 - except pywintypes.com_error: - log.error('COM error while in blank_screen') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in blank_screen') + log.exception(e) trace_error_handler(log) self.show_error_msg() @@ -248,9 +284,11 @@ class PowerpointDocument(PresentationDocument): log.debug('is_blank') if self.is_active(): try: + # ppSlideShowBlackScreen = 3 return self.presentation.SlideShowWindow.View.State == 3 - except pywintypes.com_error: - log.error('COM error while in is_blank') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in is_blank') + log.exception(e) trace_error_handler(log) self.show_error_msg() else: @@ -263,8 +301,9 @@ class PowerpointDocument(PresentationDocument): log.debug('stop_presentation') try: self.presentation.SlideShowWindow.View.Exit() - except pywintypes.com_error: - log.error('COM error while in stop_presentation') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in stop_presentation') + log.exception(e) trace_error_handler(log) self.show_error_msg() @@ -292,15 +331,19 @@ class PowerpointDocument(PresentationDocument): ppt_window.Left = size.x() * 72 / dpi ppt_window.Width = size.width() * 72 / dpi except AttributeError as e: - log.error('AttributeError while in start_presentation') - log.error(e) - # Powerpoint 2013 pops up when starting a file, so we minimize it again - if self.presentation.Application.Version == u'15.0': + log.exception('AttributeError while in start_presentation') + log.exception(e) + # Powerpoint 2010 and 2013 pops up when starting a file, so we minimize it again + if float(self.presentation.Application.Version) >= 14.0: try: + # ppWindowMinimized = 2 self.presentation.Application.WindowState = 2 - except: - log.error('Failed to minimize main powerpoint window') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Failed to minimize main powerpoint window') + log.exception(e) trace_error_handler(log) + # Make sure powerpoint doesn't steal focus + Registry().get('main_window').activateWindow() def get_slide_number(self): """ @@ -309,9 +352,19 @@ class PowerpointDocument(PresentationDocument): log.debug('get_slide_number') ret = 0 try: - ret = self.presentation.SlideShowWindow.View.CurrentShowPosition - except pywintypes.com_error: - log.error('COM error while in get_slide_number') + # We need 2 approaches to getting the current slide number, because + # SlideShowWindow.View.Slide.SlideIndex wont work on the end-slide where Slide isn't available, and + # SlideShowWindow.View.CurrentShowPosition returns 0 when called when a transistion is executing (in 2013) + # So we use SlideShowWindow.View.Slide.SlideIndex unless the state is done (ppSlideShowDone = 5) + if self.presentation.SlideShowWindow.View.State != 5: + ret = self.presentation.SlideShowWindow.View.Slide.SlideNumber + # Do reverse lookup in the index_map to find the slide number to return + ret = next((key for key, slidenum in self.index_map.items() if slidenum == ret), None) + else: + ret = self.presentation.SlideShowWindow.View.CurrentShowPosition + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in get_slide_number') + log.exception(e) trace_error_handler(log) self.show_error_msg() return ret @@ -321,11 +374,13 @@ class PowerpointDocument(PresentationDocument): Returns total number of slides. """ log.debug('get_slide_count') + return self.slide_count ret = 0 try: ret = self.presentation.Slides.Count - except pywintypes.com_error: - log.error('COM error while in get_slide_count') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in get_slide_count') + log.exception(e) trace_error_handler(log) self.show_error_msg() return ret @@ -338,9 +393,17 @@ class PowerpointDocument(PresentationDocument): """ log.debug('goto_slide') try: - self.presentation.SlideShowWindow.View.GotoSlide(slide_no) - except pywintypes.com_error: - log.error('COM error while in goto_slide') + if Settings().value('presentations/powerpoint slide click advance') and self.get_slide_number() == self.index_map[slide_no]: + click_index = self.presentation.SlideShowWindow.View.GetClickIndex() + click_count = self.presentation.SlideShowWindow.View.GetClickCount() + log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d' % (click_index, click_count)) + if click_index < click_count: + self.next_step() + else: + self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[slide_no]) + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in goto_slide') + log.exception(e) trace_error_handler(log) self.show_error_msg() @@ -351,12 +414,14 @@ class PowerpointDocument(PresentationDocument): log.debug('next_step') try: self.presentation.SlideShowWindow.View.Next() - except pywintypes.com_error: - log.error('COM error while in next_step') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in next_step') + log.exception(e) trace_error_handler(log) self.show_error_msg() return if self.get_slide_number() > self.get_slide_count(): + log.debug('past end, stepping back to previous') self.previous_step() def previous_step(self): @@ -366,8 +431,9 @@ class PowerpointDocument(PresentationDocument): log.debug('previous_step') try: self.presentation.SlideShowWindow.View.Previous() - except pywintypes.com_error: - log.error('COM error while in previous_step') + except (AttributeError, pywintypes.com_error) as e: + log.exception('Caught exception while in previous_step') + log.exception(e) trace_error_handler(log) self.show_error_msg() @@ -377,7 +443,7 @@ class PowerpointDocument(PresentationDocument): :param slide_no: The slide the text is required for, starting at 1 """ - return _get_text_from_shapes(self.presentation.Slides(slide_no).Shapes) + return _get_text_from_shapes(self.presentation.Slides(self.index_map[slide_no]).Shapes) def get_slide_notes(self, slide_no): """ @@ -385,7 +451,7 @@ class PowerpointDocument(PresentationDocument): :param slide_no: The slide the text is required for, starting at 1 """ - return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes) + return _get_text_from_shapes(self.presentation.Slides(self.index_map[slide_no]).NotesPage.Shapes) def create_titles_and_notes(self): """ @@ -396,7 +462,8 @@ class PowerpointDocument(PresentationDocument): """ titles = [] notes = [] - for slide in self.presentation.Slides: + for num in range(self.get_slide_count()): + slide = self.presentation.Slides(self.index_map[num + 1]) try: text = slide.Shapes.Title.TextFrame.TextRange.Text except Exception as e: @@ -413,7 +480,11 @@ class PowerpointDocument(PresentationDocument): """ Stop presentation and display an error message. """ - self.stop_presentation() + try: + self.presentation.SlideShowWindow.View.Exit() + except (AttributeError, pywintypes.com_error) as e: + log.exception('Failed to exit Powerpoint presentation after error') + log.exception(e) critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument', 'An error occurred in the Powerpoint integration ' 'and the presentation will be stopped. ' diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index d237cf2df..dbeeb56ba 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -68,6 +68,15 @@ class PresentationTab(SettingsTab): self.override_app_check_box.setObjectName('override_app_check_box') self.advanced_layout.addWidget(self.override_app_check_box) self.left_layout.addWidget(self.advanced_group_box) + # PowerPoint + self.powerpoint_group_box = QtGui.QGroupBox(self.left_column) + self.powerpoint_group_box.setObjectName('powerpoint_group_box') + self.powerpoint_layout = QtGui.QVBoxLayout(self.powerpoint_group_box) + self.powerpoint_layout.setObjectName('powerpoint_layout') + self.ppt_slide_click_check_box = QtGui.QCheckBox(self.powerpoint_group_box) + self.powerpoint_group_box.setObjectName('ppt_slide_click_check_box') + self.powerpoint_layout.addWidget(self.ppt_slide_click_check_box) + self.left_layout.addWidget(self.powerpoint_group_box) # Pdf options self.pdf_group_box = QtGui.QGroupBox(self.left_column) self.pdf_group_box.setObjectName('pdf_group_box') @@ -108,8 +117,12 @@ class PresentationTab(SettingsTab): self.set_controller_text(checkbox, controller) self.advanced_group_box.setTitle(UiStrings().Advanced) self.pdf_group_box.setTitle(translate('PresentationPlugin.PresentationTab', 'PDF options')) + self.powerpoint_group_box.setTitle(translate('PresentationPlugin.PresentationTab', 'PowerPoint options')) self.override_app_check_box.setText( translate('PresentationPlugin.PresentationTab', 'Allow presentation application to be overridden')) + self.ppt_slide_click_check_box.setText( + translate('PresentationPlugin.PresentationTab', + 'Clicking on a selected slide in the slidecontroller advances to next effect.')) self.pdf_program_check_box.setText( translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:')) @@ -123,11 +136,17 @@ class PresentationTab(SettingsTab): """ Load the settings. """ + powerpoint_available = False for key in self.controllers: controller = self.controllers[key] checkbox = self.presenter_check_boxes[controller.name] checkbox.setChecked(Settings().value(self.settings_section + '/' + controller.name)) + if controller.name == 'Powerpoint' and controller.is_available(): + powerpoint_available = True self.override_app_check_box.setChecked(Settings().value(self.settings_section + '/override app')) + # Load Powerpoint settings + self.ppt_slide_click_check_box.setChecked(Settings().value(self.settings_section + '/powerpoint slide click advance')) + self.ppt_slide_click_check_box.setEnabled(powerpoint_available) # load pdf-program settings enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program') self.pdf_program_check_box.setChecked(enable_pdf_program) @@ -161,6 +180,11 @@ class PresentationTab(SettingsTab): if Settings().value(setting_key) != self.override_app_check_box.checkState(): Settings().setValue(setting_key, self.override_app_check_box.checkState()) changed = True + # Save powerpoint settings + setting_key = self.settings_section + '/powerpoint slide click advance' + if Settings().value(setting_key) != self.ppt_slide_click_check_box.checkState(): + Settings().setValue(setting_key, self.ppt_slide_click_check_box.checkState()) + changed = True # Save pdf-settings pdf_program = self.pdf_program_path.text() enable_pdf_program = self.pdf_program_check_box.checkState() diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index aaa190c73..589fb1ecf 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -44,7 +44,8 @@ __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked, 'presentations/Powerpoint Viewer': QtCore.Qt.Checked, 'presentations/Pdf': QtCore.Qt.Checked, 'presentations/presentations files': [], - 'presentations/thumbnail_scheme': '' + 'presentations/thumbnail_scheme': '', + 'presentations/powerpoint slide click advance': QtCore.Qt.Unchecked } From 3f8a7975ee15bf8cb000537bb2e816977b33ab37 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Mon, 30 Mar 2015 15:31:41 +0200 Subject: [PATCH 3/5] Test fix --- .../openlp_plugins/presentations/test_powerpointcontroller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py b/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py index 2af76ee4f..dd1c1137a 100644 --- a/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py @@ -126,6 +126,7 @@ class TestPowerpointDocument(TestCase, TestMixin): instance = PowerpointDocument(self.mock_controller, self.mock_presentation) instance.presentation = MagicMock() instance.presentation.SlideShowWindow.View.GotoSlide = MagicMock(side_effect=pywintypes.com_error('1')) + instance.index_map[42] = 42 # WHEN: Calling goto_slide which will throw an exception instance.goto_slide(42) From d627982bfee69f94d463cda2d754e6d5a8dda7c8 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 1 Apr 2015 21:38:42 +0100 Subject: [PATCH 4/5] Go to previous effect instead of the previous slide. --- openlp/plugins/presentations/lib/impresscontroller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 0d87b9543..d70dfc8a4 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -428,7 +428,7 @@ class ImpressDocument(PresentationDocument): """ Triggers the previous slide on the running presentation. """ - self.control.gotoPreviousSlide() + self.control.gotoPreviousEffect() def get_slide_text(self, slide_no): """ From d5804567b5837973b7b9c8fd6ee0e2835a89400d Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Thu, 2 Apr 2015 09:33:46 +0100 Subject: [PATCH 5/5] Added test + some cleaning --- .../presentations/lib/powerpointcontroller.py | 18 ++++--------- .../presentations/lib/presentationtab.py | 3 ++- .../test_powerpointcontroller.py | 27 ++++++++++++++++++- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 9f192227d..2fb07f3ad 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -39,9 +39,8 @@ if is_win(): import pywintypes from openlp.core.lib import ScreenList -from openlp.core.common import Registry from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate -from openlp.core.common import trace_error_handler +from openlp.core.common import trace_error_handler, Registry from .presentationcontroller import PresentationController, PresentationDocument log = logging.getLogger(__name__) @@ -375,15 +374,6 @@ class PowerpointDocument(PresentationDocument): """ log.debug('get_slide_count') return self.slide_count - ret = 0 - try: - ret = self.presentation.Slides.Count - except (AttributeError, pywintypes.com_error) as e: - log.exception('Caught exception while in get_slide_count') - log.exception(e) - trace_error_handler(log) - self.show_error_msg() - return ret def goto_slide(self, slide_no): """ @@ -393,10 +383,12 @@ class PowerpointDocument(PresentationDocument): """ log.debug('goto_slide') try: - if Settings().value('presentations/powerpoint slide click advance') and self.get_slide_number() == self.index_map[slide_no]: + if Settings().value('presentations/powerpoint slide click advance') \ + and self.get_slide_number() == self.index_map[slide_no]: click_index = self.presentation.SlideShowWindow.View.GetClickIndex() click_count = self.presentation.SlideShowWindow.View.GetClickCount() - log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d' % (click_index, click_count)) + log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d' + % (click_index, click_count)) if click_index < click_count: self.next_step() else: diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index dbeeb56ba..4075bc25b 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -145,7 +145,8 @@ class PresentationTab(SettingsTab): powerpoint_available = True self.override_app_check_box.setChecked(Settings().value(self.settings_section + '/override app')) # Load Powerpoint settings - self.ppt_slide_click_check_box.setChecked(Settings().value(self.settings_section + '/powerpoint slide click advance')) + self.ppt_slide_click_check_box.setChecked(Settings().value(self.settings_section + + '/powerpoint slide click advance')) self.ppt_slide_click_check_box.setEnabled(powerpoint_available) # load pdf-program settings enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program') diff --git a/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py b/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py index dd1c1137a..52b3c1b08 100644 --- a/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py @@ -33,11 +33,15 @@ from tests.utils.constants import TEST_RESOURCES_PATH from openlp.plugins.presentations.lib.powerpointcontroller import PowerpointController, PowerpointDocument,\ _get_text_from_shapes -from openlp.core.common import is_win +from openlp.core.common import is_win, Settings if is_win(): import pywintypes +__default_settings__ = { + 'presentations/powerpoint slide click advance': True +} + class TestPowerpointController(TestCase, TestMixin): """ @@ -104,6 +108,7 @@ class TestPowerpointDocument(TestCase, TestMixin): self.mock_presentation_document_get_temp_folder.return_value = 'temp folder' self.file_name = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.pptx') self.real_controller = PowerpointController(self.mock_plugin) + Settings().extend_default_settings(__default_settings__) def tearDown(self): """ @@ -228,3 +233,23 @@ class TestPowerpointDocument(TestCase, TestMixin): # THEN: it should not fail but return empty string self.assertEqual(result, '', 'result should be empty') + + def goto_slide_test(self): + """ + Test that goto_slide goes to next effect if the slide is already displayed + """ + # GIVEN: A Document with mocked controller, presentation, and mocked functions get_slide_number and next_step + doc = PowerpointDocument(self.mock_controller, self.mock_presentation) + doc.presentation = MagicMock() + doc.presentation.SlideShowWindow.View.GetClickIndex.return_value = 1 + doc.presentation.SlideShowWindow.View.GetClickCount.return_value = 2 + doc.get_slide_number = MagicMock() + doc.get_slide_number.return_value = 1 + doc.next_step = MagicMock() + doc.index_map[1] = 1 + + # WHEN: Calling goto_slide + doc.goto_slide(1) + + # THEN: next_step() should be call to try to advance to the next effect. + self.assertTrue(doc.next_step.called, 'next_step() should have been called!')