forked from openlp/openlp
Fix sequential presentations - proper fix
This commit is contained in:
parent
b81d7e753c
commit
5793526bcb
@ -28,6 +28,7 @@ from openlp.core.common.registry import Registry
|
|||||||
from openlp.core.lib import ServiceItemContext
|
from openlp.core.lib import ServiceItemContext
|
||||||
from openlp.core.ui import HideMode
|
from openlp.core.ui import HideMode
|
||||||
from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
|
from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
|
||||||
|
from openlp.plugins.presentations.lib.presentationcontroller import PresentationList
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -49,21 +50,20 @@ class Controller(object):
|
|||||||
self.hide_mode = None
|
self.hide_mode = None
|
||||||
log.info('{name} controller loaded'.format(name=live))
|
log.info('{name} controller loaded'.format(name=live))
|
||||||
|
|
||||||
def add_handler(self, controller, file, hide_mode, slide_no):
|
def add_handler(self, controller, file, hide_mode, slide_no, unique_id):
|
||||||
"""
|
"""
|
||||||
Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller
|
Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller
|
||||||
has a display then load the presentation.
|
has a display then load the presentation.
|
||||||
"""
|
"""
|
||||||
log.debug('Live = {live}, add_handler {handler}'.format(live=self.is_live, handler=file))
|
log.debug('Live = {live}, add_handler {handler}'.format(live=self.is_live, handler=file))
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
if self.doc is not None:
|
|
||||||
self.shutdown()
|
|
||||||
self.doc = self.controller.add_document(file)
|
self.doc = self.controller.add_document(file)
|
||||||
if not self.doc.load_presentation():
|
if not self.doc.load_presentation():
|
||||||
# Display error message to user
|
# Display error message to user
|
||||||
# Inform slidecontroller that the action failed?
|
# Inform slidecontroller that the action failed?
|
||||||
self.doc.slidenumber = 0
|
self.doc.slidenumber = 0
|
||||||
return
|
return
|
||||||
|
PresentationList().add(self.doc, unique_id)
|
||||||
self.doc.slidenumber = slide_no
|
self.doc.slidenumber = slide_no
|
||||||
self.hide_mode = hide_mode
|
self.hide_mode = hide_mode
|
||||||
log.debug('add_handler, slide_number: {slide:d}'.format(slide=slide_no))
|
log.debug('add_handler, slide_number: {slide:d}'.format(slide=slide_no))
|
||||||
@ -205,15 +205,15 @@ class Controller(object):
|
|||||||
self.poll()
|
self.poll()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self, unique_id):
|
||||||
"""
|
"""
|
||||||
Based on the handler passed at startup triggers slide show to shut down.
|
Based on the handler passed at startup triggers slide show to shut down.
|
||||||
"""
|
"""
|
||||||
log.debug('Live = {live}, shutdown'.format(live=self.is_live))
|
log.debug('Live = {live}, shutdown'.format(live=self.is_live))
|
||||||
if not self.doc:
|
presentation_to_close = PresentationList().get_presentation_by_id(unique_id)
|
||||||
return
|
if presentation_to_close:
|
||||||
self.doc.close_presentation()
|
presentation_to_close.close_presentation()
|
||||||
self.doc = None
|
PresentationList().remove(unique_id)
|
||||||
|
|
||||||
def blank(self, hide_mode):
|
def blank(self, hide_mode):
|
||||||
"""
|
"""
|
||||||
@ -365,7 +365,8 @@ class MessageListener(object):
|
|||||||
if self.handler is None:
|
if self.handler is None:
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
else:
|
else:
|
||||||
controller.add_handler(self.controllers[self.handler], file_path, hide_mode, message[3])
|
controller.add_handler(self.controllers[self.handler], file_path, hide_mode, message[3],
|
||||||
|
message[0].unique_identifier)
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
|
|
||||||
def slide(self, message):
|
def slide(self, message):
|
||||||
@ -443,10 +444,10 @@ class MessageListener(object):
|
|||||||
"""
|
"""
|
||||||
is_live = message[1]
|
is_live = message[1]
|
||||||
if is_live:
|
if is_live:
|
||||||
self.live_handler.shutdown()
|
self.live_handler.shutdown(message[0].unique_identifier)
|
||||||
self.timer.stop()
|
self.timer.stop()
|
||||||
else:
|
else:
|
||||||
self.preview_handler.shutdown()
|
self.preview_handler.shutdown(message[0].unique_identifier)
|
||||||
|
|
||||||
def hide(self, message):
|
def hide(self, message):
|
||||||
"""
|
"""
|
||||||
|
@ -24,7 +24,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
from openlp.core.common import md5_hash, sha256_file_hash
|
from openlp.core.common import Singleton, md5_hash, sha256_file_hash
|
||||||
from openlp.core.common.applocation import AppLocation
|
from openlp.core.common.applocation import AppLocation
|
||||||
from openlp.core.common.path import create_paths
|
from openlp.core.common.path import create_paths
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
@ -386,6 +386,37 @@ class PresentationDocument(object):
|
|||||||
return self._sha256_file_hash
|
return self._sha256_file_hash
|
||||||
|
|
||||||
|
|
||||||
|
class PresentationList(metaclass=Singleton):
|
||||||
|
"""
|
||||||
|
This is a singleton class which maintains a list of instances for presentations
|
||||||
|
which have been started.
|
||||||
|
The document load_presentation() method is called several times - for example, when the
|
||||||
|
presentation files are being loaded into the library - but a document is included in this
|
||||||
|
PresentationList only when the presentation is actually displayed.
|
||||||
|
In this case the loading is initiated by a Registry 'presentation_start' event, the message
|
||||||
|
includes the service item, and the unique_identifier from the service item is used as the id
|
||||||
|
to differentiate the presentation document instances within this PresentationList.
|
||||||
|
The purpose of this is so that the 'presentation_stop' event, which also includes the service
|
||||||
|
item and its unique identifier, can result in the correct presentation being stopped.
|
||||||
|
This fixes issue #700
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._presentations = {}
|
||||||
|
|
||||||
|
def add(self, document, unique_id):
|
||||||
|
self._presentations[unique_id] = document
|
||||||
|
|
||||||
|
def remove(self, unique_id):
|
||||||
|
del self._presentations[unique_id]
|
||||||
|
|
||||||
|
def get_presentation_by_id(self, unique_id):
|
||||||
|
if unique_id in self._presentations:
|
||||||
|
return self._presentations[unique_id]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class PresentationController(object):
|
class PresentationController(object):
|
||||||
"""
|
"""
|
||||||
This class is used to control interactions with presentation applications by creating a runtime environment.
|
This class is used to control interactions with presentation applications by creating a runtime environment.
|
||||||
|
@ -122,7 +122,7 @@ def test_add_handler_failure():
|
|||||||
mocked_doc_controller.add_document.return_value = mocked_doc
|
mocked_doc_controller.add_document.return_value = mocked_doc
|
||||||
|
|
||||||
# WHEN: calling add_handler that fails
|
# WHEN: calling add_handler that fails
|
||||||
controller.add_handler(mocked_doc_controller, MagicMock(), True, 0)
|
controller.add_handler(mocked_doc_controller, MagicMock(), True, 0, "uuid")
|
||||||
|
|
||||||
# THEN: slidenumber should be 0
|
# THEN: slidenumber should be 0
|
||||||
assert controller.doc.slidenumber == 0, 'doc.slidenumber should be 0'
|
assert controller.doc.slidenumber == 0, 'doc.slidenumber should be 0'
|
||||||
|
@ -26,7 +26,8 @@ import pytest
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import MagicMock, call, patch
|
from unittest.mock import MagicMock, call, patch
|
||||||
|
|
||||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
|
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
|
||||||
|
PresentationList
|
||||||
|
|
||||||
|
|
||||||
FOLDER_TO_PATCH = 'openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder'
|
FOLDER_TO_PATCH = 'openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder'
|
||||||
@ -215,3 +216,45 @@ def test_load_presentation(get_thumbnail_folder):
|
|||||||
|
|
||||||
# THEN: load_presentation should return false
|
# THEN: load_presentation should return false
|
||||||
assert result is False, "PresentationDocument.load_presentation should return false."
|
assert result is False, "PresentationDocument.load_presentation should return false."
|
||||||
|
|
||||||
|
|
||||||
|
def test_presentation_list_is_singleton():
|
||||||
|
"""
|
||||||
|
Test PresentationList is a singleton class
|
||||||
|
"""
|
||||||
|
# GIVEN: a PresentationList
|
||||||
|
presentation_list = PresentationList()
|
||||||
|
|
||||||
|
# WHEN: I try to create another instance
|
||||||
|
presentation_list_2 = PresentationList()
|
||||||
|
|
||||||
|
# THEN: I get the same instance returned
|
||||||
|
assert presentation_list_2 is presentation_list
|
||||||
|
|
||||||
|
|
||||||
|
def test_presentation_list_add_and_retrieve(document):
|
||||||
|
"""
|
||||||
|
Test adding a presentation document and later retrieving it
|
||||||
|
"""
|
||||||
|
# GIVEN: a fixture with a mocked document which is added to the PresentationList
|
||||||
|
PresentationList().add(document, "unique id")
|
||||||
|
|
||||||
|
# WHEN: I retrieve the presentation document
|
||||||
|
retrieved_presentation = PresentationList().get_presentation_by_id("unique id")
|
||||||
|
|
||||||
|
# THEN: I get the same instance returned
|
||||||
|
retrieved_presentation is document
|
||||||
|
|
||||||
|
|
||||||
|
def test_presentation_list_remove(document):
|
||||||
|
"""
|
||||||
|
Test removing a presentation document from the list
|
||||||
|
"""
|
||||||
|
# GIVEN: a fixture with a mocked document which is added to the PresentationList
|
||||||
|
PresentationList().add(document, "unique id")
|
||||||
|
|
||||||
|
# WHEN: I remove the presentation document
|
||||||
|
PresentationList().remove("unique id")
|
||||||
|
|
||||||
|
# THEN: That document shouldn't be in the list
|
||||||
|
PresentationList().get_presentation_by_id("unique id") is None
|
||||||
|
Loading…
Reference in New Issue
Block a user