forked from openlp/openlp
trunk
This commit is contained in:
commit
3861d340a3
27
nose2.cfg
27
nose2.cfg
@ -1,27 +0,0 @@
|
|||||||
[unittest]
|
|
||||||
verbose = true
|
|
||||||
plugins = nose2.plugins.mp
|
|
||||||
|
|
||||||
[log-capture]
|
|
||||||
always-on = true
|
|
||||||
clear-handlers = true
|
|
||||||
filter = -nose
|
|
||||||
log-level = ERROR
|
|
||||||
|
|
||||||
[test-result]
|
|
||||||
always-on = true
|
|
||||||
descriptions = true
|
|
||||||
|
|
||||||
[coverage]
|
|
||||||
always-on = true
|
|
||||||
coverage = openlp
|
|
||||||
coverage-report = html
|
|
||||||
|
|
||||||
[multiprocess]
|
|
||||||
always-on = false
|
|
||||||
processes = 4
|
|
||||||
|
|
||||||
[output-buffer]
|
|
||||||
always-on = true
|
|
||||||
stderr = true
|
|
||||||
stdout = true
|
|
@ -23,7 +23,6 @@ from contextlib import suppress
|
|||||||
from json import JSONDecoder, JSONEncoder
|
from json import JSONDecoder, JSONEncoder
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
_registered_classes = {}
|
_registered_classes = {}
|
||||||
|
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ class Registry(object):
|
|||||||
try:
|
try:
|
||||||
log.debug('Running function {} for {}'.format(function, event))
|
log.debug('Running function {} for {}'.format(function, event))
|
||||||
result = function(*args, **kwargs)
|
result = function(*args, **kwargs)
|
||||||
if result:
|
if result is not None:
|
||||||
results.append(result)
|
results.append(result)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# Who has called me can help in debugging
|
# Who has called me can help in debugging
|
||||||
|
@ -589,7 +589,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Add this item to the current service.
|
Add this item to the current service.
|
||||||
|
|
||||||
:param item: Item to be processed
|
:param QtWidgets.QListWidgetItem | QtWidgets.QTreeWidgetItem | None item: Item to be processed
|
||||||
:param replace: Replace the existing item
|
:param replace: Replace the existing item
|
||||||
:param remote: Triggered from remote
|
:param remote: Triggered from remote
|
||||||
:param position: Position to place item
|
:param position: Position to place item
|
||||||
@ -627,7 +627,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
|||||||
def build_service_item(self, item=None, remote=False, context=ServiceItemContext.Live):
|
def build_service_item(self, item=None, remote=False, context=ServiceItemContext.Live):
|
||||||
"""
|
"""
|
||||||
Common method for generating a service item
|
Common method for generating a service item
|
||||||
:param item: Service Item to be built.
|
:param QtWidgets.QListWidgetItem | QtWidgets.QTreeWidgetItem | None item: Service Item to be built.
|
||||||
:param remote: Remote triggered (False)
|
:param remote: Remote triggered (False)
|
||||||
:param context: The context on which this is called
|
:param context: The context on which this is called
|
||||||
"""
|
"""
|
||||||
|
@ -593,9 +593,11 @@ class ServiceItem(RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
return not bool(self.slides)
|
return not bool(self.slides)
|
||||||
|
|
||||||
def validate_item(self, suffix_list=None):
|
def validate_item(self, suffixes=None):
|
||||||
"""
|
"""
|
||||||
Validates a service item to make sure it is valid
|
Validates a service item to make sure it is valid
|
||||||
|
|
||||||
|
:param set[str] suffixes: A set of vaild suffixes
|
||||||
"""
|
"""
|
||||||
self.is_valid = True
|
self.is_valid = True
|
||||||
for slide in self.slides:
|
for slide in self.slides:
|
||||||
@ -612,8 +614,8 @@ class ServiceItem(RegistryProperties):
|
|||||||
if not os.path.exists(file_name):
|
if not os.path.exists(file_name):
|
||||||
self.is_valid = False
|
self.is_valid = False
|
||||||
break
|
break
|
||||||
if suffix_list and not self.is_text():
|
if suffixes and not self.is_text():
|
||||||
file_suffix = slide['title'].split('.')[-1]
|
file_suffix = slide['title'].split('.')[-1]
|
||||||
if file_suffix.lower() not in suffix_list:
|
if file_suffix.lower() not in suffixes:
|
||||||
self.is_valid = False
|
self.is_valid = False
|
||||||
break
|
break
|
||||||
|
@ -44,7 +44,7 @@ from openlp.core.lib.ui import critical_error_message_box
|
|||||||
from openlp.core.ui import DisplayControllerType
|
from openlp.core.ui import DisplayControllerType
|
||||||
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path
|
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path
|
||||||
from openlp.core.ui.media.endpoint import media_endpoint
|
from openlp.core.ui.media.endpoint import media_endpoint
|
||||||
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
|
from openlp.core.ui.media.vlcplayer import AUDIO_EXT, VIDEO_EXT, VlcPlayer, get_vlc
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -65,11 +65,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
current_media_players is an array of player instances keyed on ControllerType.
|
current_media_players is an array of player instances keyed on ControllerType.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, parent=None):
|
|
||||||
"""
|
|
||||||
Constructor
|
|
||||||
"""
|
|
||||||
super(MediaController, self).__init__(parent)
|
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.vlc_player = None
|
self.vlc_player = None
|
||||||
@ -95,28 +90,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
Registry().register_function('songs_hide', self.media_hide)
|
Registry().register_function('songs_hide', self.media_hide)
|
||||||
Registry().register_function('songs_blank', self.media_blank)
|
Registry().register_function('songs_blank', self.media_blank)
|
||||||
Registry().register_function('songs_unblank', self.media_unblank)
|
Registry().register_function('songs_unblank', self.media_unblank)
|
||||||
Registry().register_function('mediaitem_suffixes', self._generate_extensions_lists)
|
|
||||||
register_endpoint(media_endpoint)
|
register_endpoint(media_endpoint)
|
||||||
|
|
||||||
def _generate_extensions_lists(self):
|
|
||||||
"""
|
|
||||||
Set the active players and available media files
|
|
||||||
"""
|
|
||||||
suffix_list = []
|
|
||||||
self.audio_extensions_list = []
|
|
||||||
if self.vlc_player.is_active:
|
|
||||||
for item in self.vlc_player.audio_extensions_list:
|
|
||||||
if item not in self.audio_extensions_list:
|
|
||||||
self.audio_extensions_list.append(item)
|
|
||||||
suffix_list.append(item[2:])
|
|
||||||
self.video_extensions_list = []
|
|
||||||
if self.vlc_player.is_active:
|
|
||||||
for item in self.vlc_player.video_extensions_list:
|
|
||||||
if item not in self.video_extensions_list:
|
|
||||||
self.video_extensions_list.append(item)
|
|
||||||
suffix_list.append(item[2:])
|
|
||||||
self.service_manager.supported_suffixes(suffix_list)
|
|
||||||
|
|
||||||
def bootstrap_initialise(self):
|
def bootstrap_initialise(self):
|
||||||
"""
|
"""
|
||||||
Check to see if we have any media Player's available.
|
Check to see if we have any media Player's available.
|
||||||
@ -131,7 +106,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
else:
|
else:
|
||||||
State().missing_text('media_live', translate('OpenLP.SlideController',
|
State().missing_text('media_live', translate('OpenLP.SlideController',
|
||||||
'VLC or pymediainfo are missing, so you are unable to play any media'))
|
'VLC or pymediainfo are missing, so you are unable to play any media'))
|
||||||
self._generate_extensions_lists()
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def bootstrap_post_set_up(self):
|
def bootstrap_post_set_up(self):
|
||||||
@ -381,7 +355,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
if file.is_file:
|
if file.is_file:
|
||||||
suffix = '*%s' % file.suffix.lower()
|
suffix = '*%s' % file.suffix.lower()
|
||||||
file = str(file)
|
file = str(file)
|
||||||
if suffix in self.vlc_player.video_extensions_list:
|
if suffix in VIDEO_EXT:
|
||||||
if not controller.media_info.is_background or controller.media_info.is_background and \
|
if not controller.media_info.is_background or controller.media_info.is_background and \
|
||||||
self.vlc_player.can_background:
|
self.vlc_player.can_background:
|
||||||
self.resize(display, self.vlc_player)
|
self.resize(display, self.vlc_player)
|
||||||
@ -389,7 +363,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
self.current_media_players[controller.controller_type] = self.vlc_player
|
self.current_media_players[controller.controller_type] = self.vlc_player
|
||||||
controller.media_info.media_type = MediaType.Video
|
controller.media_info.media_type = MediaType.Video
|
||||||
return True
|
return True
|
||||||
if suffix in self.vlc_player.audio_extensions_list:
|
if suffix in AUDIO_EXT:
|
||||||
if self.vlc_player.load(display, file):
|
if self.vlc_player.load(display, file):
|
||||||
self.current_media_players[controller.controller_type] = self.vlc_player
|
self.current_media_players[controller.controller_type] = self.vlc_player
|
||||||
controller.media_info.media_type = MediaType.Audio
|
controller.media_info.media_type = MediaType.Audio
|
||||||
|
@ -38,13 +38,10 @@ class MediaPlayer(RegistryProperties):
|
|||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.name = name
|
self.name = name
|
||||||
self.available = self.check_available()
|
self.available = self.check_available()
|
||||||
self.is_active = False
|
|
||||||
self.can_background = False
|
self.can_background = False
|
||||||
self.can_folder = False
|
self.can_folder = False
|
||||||
self.state = {0: MediaState.Off, 1: MediaState.Off}
|
self.state = {0: MediaState.Off, 1: MediaState.Off}
|
||||||
self.has_own_widget = False
|
self.has_own_widget = False
|
||||||
self.audio_extensions_list = []
|
|
||||||
self.video_extensions_list = []
|
|
||||||
|
|
||||||
def check_available(self):
|
def check_available(self):
|
||||||
"""
|
"""
|
||||||
@ -166,12 +163,6 @@ class MediaPlayer(RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def get_info(self):
|
|
||||||
"""
|
|
||||||
Returns Information about the player
|
|
||||||
"""
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def get_live_state(self):
|
def get_live_state(self):
|
||||||
"""
|
"""
|
||||||
Get the state of the live player
|
Get the state of the live player
|
||||||
|
@ -32,7 +32,6 @@ from datetime import datetime
|
|||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
|
|
||||||
from openlp.core.common import is_linux, is_macosx, is_win
|
from openlp.core.common import is_linux, is_macosx, is_win
|
||||||
from openlp.core.common.i18n import translate
|
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.core.ui.media import MediaState, MediaType
|
from openlp.core.ui.media import MediaState, MediaType
|
||||||
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
||||||
@ -41,20 +40,18 @@ from openlp.core.ui.media.mediaplayer import MediaPlayer
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
||||||
AUDIO_EXT = ['*.3ga', '*.669', '*.a52', '*.aac', '*.ac3', '*.adt', '*.adts', '*.aif', '*.aifc', '*.aiff', '*.amr',
|
AUDIO_EXT = ('3ga', '669', 'a52', 'aac', 'ac3', 'adt', 'adts', 'aif', 'aifc', 'aiff', 'amr', 'aob', 'ape', 'awb', 'caf',
|
||||||
'*.aob', '*.ape', '*.awb', '*.caf', '*.dts', '*.flac', '*.it', '*.kar', '*.m4a', '*.m4b', '*.m4p', '*.m5p',
|
'dts', 'flac', 'it', 'kar', 'm4a', 'm4b', 'm4p', 'm5p', 'mid', 'mka', 'mlp', 'mod', 'mpa', 'mp1', 'mp2',
|
||||||
'*.mid', '*.mka', '*.mlp', '*.mod', '*.mpa', '*.mp1', '*.mp2', '*.mp3', '*.mpc', '*.mpga', '*.mus',
|
'mp3', 'mpc', 'mpga', 'mus', 'oga', 'ogg', 'oma', 'opus', 'qcp', 'ra', 'rmi', 's3m', 'sid', 'spx', 'thd',
|
||||||
'*.oga', '*.ogg', '*.oma', '*.opus', '*.qcp', '*.ra', '*.rmi', '*.s3m', '*.sid', '*.spx', '*.thd', '*.tta',
|
'tta', 'voc', 'vqf', 'w64', 'wav', 'wma', 'wv', 'xa', 'xm')
|
||||||
'*.voc', '*.vqf', '*.w64', '*.wav', '*.wma', '*.wv', '*.xa', '*.xm']
|
|
||||||
|
|
||||||
VIDEO_EXT = ['*.3g2', '*.3gp', '*.3gp2', '*.3gpp', '*.amv', '*.asf', '*.avi', '*.bik', '*.divx', '*.drc', '*.dv',
|
VIDEO_EXT = ('3g2', '3gp', '3gp2', '3gpp', 'amv', 'asf', 'avi', 'bik', 'divx', 'drc', 'dv', 'f4v', 'flv', 'gvi', 'gxf',
|
||||||
'*.f4v', '*.flv', '*.gvi', '*.gxf', '*.iso', '*.m1v', '*.m2v', '*.m2t', '*.m2ts', '*.m4v', '*.mkv',
|
'iso', 'm1v', 'm2v', 'm2t', 'm2ts', 'm4v', 'mkv', 'mov', 'mp2', 'mp2v', 'mp4', 'mp4v', 'mpe', 'mpeg',
|
||||||
'*.mov', '*.mp2', '*.mp2v', '*.mp4', '*.mp4v', '*.mpe', '*.mpeg', '*.mpeg1', '*.mpeg2', '*.mpeg4', '*.mpg',
|
'mpeg1', 'mpeg2', 'mpeg4', 'mpg', 'mpv2', 'mts', 'mtv', 'mxf', 'mxg', 'nsv', 'nuv', 'ogg', 'ogm', 'ogv',
|
||||||
'*.mpv2', '*.mts', '*.mtv', '*.mxf', '*.mxg', '*.nsv', '*.nuv', '*.ogg', '*.ogm', '*.ogv', '*.ogx', '*.ps',
|
'ogx', 'ps', 'rec', 'rm', 'rmvb', 'rpl', 'thp', 'tod', 'ts', 'tts', 'txd', 'vob', 'vro', 'webm', 'wm',
|
||||||
'*.rec', '*.rm', '*.rmvb', '*.rpl', '*.thp', '*.tod', '*.ts', '*.tts', '*.txd', '*.vob', '*.vro', '*.webm',
|
'wmv', 'wtv', 'xesc',
|
||||||
'*.wm', '*.wmv', '*.wtv', '*.xesc',
|
|
||||||
# These extensions was not in the official list, added manually.
|
# These extensions was not in the official list, added manually.
|
||||||
'*.nut', '*.rv', '*.xvid']
|
'nut', 'rv', 'xvid')
|
||||||
|
|
||||||
|
|
||||||
def get_vlc():
|
def get_vlc():
|
||||||
@ -110,8 +107,6 @@ class VlcPlayer(MediaPlayer):
|
|||||||
self.display_name = '&VLC'
|
self.display_name = '&VLC'
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.can_folder = True
|
self.can_folder = True
|
||||||
self.audio_extensions_list = AUDIO_EXT
|
|
||||||
self.video_extensions_list = VIDEO_EXT
|
|
||||||
|
|
||||||
def setup(self, output_display, live_display):
|
def setup(self, output_display, live_display):
|
||||||
"""
|
"""
|
||||||
@ -375,14 +370,3 @@ class VlcPlayer(MediaPlayer):
|
|||||||
else:
|
else:
|
||||||
controller.seek_slider.setSliderPosition(output_display.vlc_media_player.get_time())
|
controller.seek_slider.setSliderPosition(output_display.vlc_media_player.get_time())
|
||||||
controller.seek_slider.blockSignals(False)
|
controller.seek_slider.blockSignals(False)
|
||||||
|
|
||||||
def get_info(self):
|
|
||||||
"""
|
|
||||||
Return some information about this player
|
|
||||||
"""
|
|
||||||
return(translate('Media.player', 'VLC is an external player which '
|
|
||||||
'supports a number of different formats.') +
|
|
||||||
'<br/> <strong>' + translate('Media.player', 'Audio') +
|
|
||||||
'</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
|
|
||||||
translate('Media.player', 'Video') + '</strong><br/>' +
|
|
||||||
str(VIDEO_EXT) + '<br/>')
|
|
||||||
|
@ -48,6 +48,7 @@ from openlp.core.lib.plugin import PluginStatus
|
|||||||
from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem
|
from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem
|
||||||
from openlp.core.lib.ui import create_widget_action, critical_error_message_box, find_and_set_in_combo_box
|
from openlp.core.lib.ui import create_widget_action, critical_error_message_box, find_and_set_in_combo_box
|
||||||
from openlp.core.ui.icons import UiIcons
|
from openlp.core.ui.icons import UiIcons
|
||||||
|
from openlp.core.ui.media.vlcplayer import AUDIO_EXT, VIDEO_EXT
|
||||||
from openlp.core.ui.serviceitemeditform import ServiceItemEditForm
|
from openlp.core.ui.serviceitemeditform import ServiceItemEditForm
|
||||||
from openlp.core.ui.servicenoteform import ServiceNoteForm
|
from openlp.core.ui.servicenoteform import ServiceNoteForm
|
||||||
from openlp.core.ui.starttimeform import StartTimeForm
|
from openlp.core.ui.starttimeform import StartTimeForm
|
||||||
@ -320,7 +321,8 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
"""
|
"""
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.service_items = []
|
self.service_items = []
|
||||||
self.suffixes = []
|
self.suffixes = set()
|
||||||
|
self.add_media_suffixes()
|
||||||
self.drop_position = -1
|
self.drop_position = -1
|
||||||
self.service_id = 0
|
self.service_id = 0
|
||||||
# is a new service and has not been saved
|
# is a new service and has not been saved
|
||||||
@ -347,6 +349,13 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
self.service_item_edit_form = ServiceItemEditForm()
|
self.service_item_edit_form = ServiceItemEditForm()
|
||||||
self.start_time_form = StartTimeForm()
|
self.start_time_form = StartTimeForm()
|
||||||
|
|
||||||
|
def add_media_suffixes(self):
|
||||||
|
"""
|
||||||
|
Add the suffixes supported by :mod:`openlp.core.ui.media.vlcplayer`
|
||||||
|
"""
|
||||||
|
self.suffixes.update(AUDIO_EXT)
|
||||||
|
self.suffixes.update(VIDEO_EXT)
|
||||||
|
|
||||||
def set_modified(self, modified=True):
|
def set_modified(self, modified=True):
|
||||||
"""
|
"""
|
||||||
Setter for property "modified". Sets whether or not the current service has been modified.
|
Setter for property "modified". Sets whether or not the current service has been modified.
|
||||||
@ -401,22 +410,19 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
def reset_supported_suffixes(self):
|
def reset_supported_suffixes(self):
|
||||||
"""
|
"""
|
||||||
Resets the Suffixes list.
|
Resets the Suffixes list.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.suffixes = []
|
self.suffixes.clear()
|
||||||
|
|
||||||
def supported_suffixes(self, suffix_list):
|
def supported_suffixes(self, suffix_list):
|
||||||
"""
|
"""
|
||||||
Adds Suffixes supported to the master list. Called from Plugins.
|
Adds Suffixes supported to the master list. Called from Plugins.
|
||||||
|
|
||||||
:param suffix_list: New Suffix's to be supported
|
:param list[str] | str suffix_list: New suffix(s) to be supported
|
||||||
"""
|
"""
|
||||||
if isinstance(suffix_list, str):
|
if isinstance(suffix_list, str):
|
||||||
self.suffixes.append(suffix_list)
|
self.suffixes.add(suffix_list)
|
||||||
else:
|
else:
|
||||||
for suffix in suffix_list:
|
self.suffixes.update(suffix_list)
|
||||||
if suffix not in self.suffixes:
|
|
||||||
self.suffixes.append(suffix)
|
|
||||||
|
|
||||||
def on_new_service_clicked(self):
|
def on_new_service_clicked(self):
|
||||||
"""
|
"""
|
||||||
@ -475,9 +481,11 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
QtWidgets.QMessageBox.Save | QtWidgets.QMessageBox.Discard |
|
QtWidgets.QMessageBox.Save | QtWidgets.QMessageBox.Discard |
|
||||||
QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Save)
|
QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Save)
|
||||||
|
|
||||||
def on_recent_service_clicked(self):
|
def on_recent_service_clicked(self, checked):
|
||||||
"""
|
"""
|
||||||
Load a recent file as the service triggered by mainwindow recent service list.
|
Load a recent file as the service triggered by mainwindow recent service list.
|
||||||
|
|
||||||
|
:param bool checked: Not used
|
||||||
"""
|
"""
|
||||||
if self.is_modified():
|
if self.is_modified():
|
||||||
result = self.save_modified_service()
|
result = self.save_modified_service()
|
||||||
@ -976,8 +984,10 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
prev_item_last_slide = None
|
prev_item_last_slide = None
|
||||||
service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list)
|
service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list)
|
||||||
while service_iterator.value():
|
while service_iterator.value():
|
||||||
|
# Found the selected/current service item
|
||||||
if service_iterator.value() == selected:
|
if service_iterator.value() == selected:
|
||||||
if last_slide and prev_item_last_slide:
|
if last_slide and prev_item_last_slide:
|
||||||
|
# Go to the last slide of the previous service item
|
||||||
pos = prev_item.data(0, QtCore.Qt.UserRole)
|
pos = prev_item.data(0, QtCore.Qt.UserRole)
|
||||||
check_expanded = self.service_items[pos - 1]['expanded']
|
check_expanded = self.service_items[pos - 1]['expanded']
|
||||||
self.service_manager_list.setCurrentItem(prev_item_last_slide)
|
self.service_manager_list.setCurrentItem(prev_item_last_slide)
|
||||||
@ -986,13 +996,17 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||||||
self.make_live()
|
self.make_live()
|
||||||
self.service_manager_list.setCurrentItem(prev_item)
|
self.service_manager_list.setCurrentItem(prev_item)
|
||||||
elif prev_item:
|
elif prev_item:
|
||||||
|
# Go to the first slide of the previous service item
|
||||||
self.service_manager_list.setCurrentItem(prev_item)
|
self.service_manager_list.setCurrentItem(prev_item)
|
||||||
self.make_live()
|
self.make_live()
|
||||||
return
|
return
|
||||||
|
# Found the previous service item root
|
||||||
if service_iterator.value().parent() is None:
|
if service_iterator.value().parent() is None:
|
||||||
prev_item = service_iterator.value()
|
prev_item = service_iterator.value()
|
||||||
|
# Found the last slide of the previous item
|
||||||
if service_iterator.value().parent() is prev_item:
|
if service_iterator.value().parent() is prev_item:
|
||||||
prev_item_last_slide = service_iterator.value()
|
prev_item_last_slide = service_iterator.value()
|
||||||
|
# Go to next item in the tree
|
||||||
service_iterator += 1
|
service_iterator += 1
|
||||||
|
|
||||||
def on_set_item(self, message):
|
def on_set_item(self, message):
|
||||||
|
@ -1261,9 +1261,18 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||||||
if not self.service_item:
|
if not self.service_item:
|
||||||
return
|
return
|
||||||
if self.service_item.is_command():
|
if self.service_item.is_command():
|
||||||
Registry().execute('{text}_next'.format(text=self.service_item.name.lower()),
|
past_end = Registry().execute('{text}_next'.format(text=self.service_item.name.lower()),
|
||||||
[self.service_item, self.is_live])
|
[self.service_item, self.is_live])
|
||||||
if self.is_live:
|
# Check if we have gone past the end of the last slide
|
||||||
|
if self.is_live and past_end and past_end[0]:
|
||||||
|
if wrap is None:
|
||||||
|
if self.slide_limits == SlideLimits.Wrap:
|
||||||
|
self.on_slide_selected_index([0])
|
||||||
|
elif self.is_live and self.slide_limits == SlideLimits.Next:
|
||||||
|
self.service_next()
|
||||||
|
elif wrap:
|
||||||
|
self.on_slide_selected_index([0])
|
||||||
|
elif self.is_live:
|
||||||
self.update_preview()
|
self.update_preview()
|
||||||
else:
|
else:
|
||||||
row = self.preview_widget.current_slide_number() + 1
|
row = self.preview_widget.current_slide_number() + 1
|
||||||
@ -1290,9 +1299,16 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||||||
if not self.service_item:
|
if not self.service_item:
|
||||||
return
|
return
|
||||||
if self.service_item.is_command():
|
if self.service_item.is_command():
|
||||||
Registry().execute('{text}_previous'.format(text=self.service_item.name.lower()),
|
before_start = Registry().execute('{text}_previous'.format(text=self.service_item.name.lower()),
|
||||||
[self.service_item, self.is_live])
|
[self.service_item, self.is_live])
|
||||||
if self.is_live:
|
# Check id we have tried to go before that start slide
|
||||||
|
if self.is_live and before_start and before_start[0]:
|
||||||
|
if self.slide_limits == SlideLimits.Wrap:
|
||||||
|
self.on_slide_selected_index([self.preview_widget.slide_count() - 1])
|
||||||
|
elif self.is_live and self.slide_limits == SlideLimits.Next:
|
||||||
|
self.keypress_queue.append(ServiceItemAction.PreviousLastSlide)
|
||||||
|
self._process_queue()
|
||||||
|
elif self.is_live:
|
||||||
self.update_preview()
|
self.update_preview()
|
||||||
else:
|
else:
|
||||||
row = self.preview_widget.current_slide_number() - 1
|
row = self.preview_widget.current_slide_number() - 1
|
||||||
|
@ -32,7 +32,6 @@ from openlp.core.common.mixins import RegistryProperties
|
|||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.lib.theme import BackgroundGradientType, BackgroundType
|
from openlp.core.lib.theme import BackgroundGradientType, BackgroundType
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
# TODO: Fix this. Use a "get_video_extensions" method which uses the current media player
|
|
||||||
from openlp.core.ui.media.vlcplayer import VIDEO_EXT
|
from openlp.core.ui.media.vlcplayer import VIDEO_EXT
|
||||||
from openlp.core.ui.themelayoutform import ThemeLayoutForm
|
from openlp.core.ui.themelayoutform import ThemeLayoutForm
|
||||||
from openlp.core.ui.themewizard import Ui_ThemeWizard
|
from openlp.core.ui.themewizard import Ui_ThemeWizard
|
||||||
@ -76,9 +75,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
|||||||
self.image_path_edit.filters = \
|
self.image_path_edit.filters = \
|
||||||
'{name};;{text} (*)'.format(name=get_images_filter(), text=UiStrings().AllFiles)
|
'{name};;{text} (*)'.format(name=get_images_filter(), text=UiStrings().AllFiles)
|
||||||
self.image_path_edit.pathChanged.connect(self.on_image_path_edit_path_changed)
|
self.image_path_edit.pathChanged.connect(self.on_image_path_edit_path_changed)
|
||||||
# TODO: Should work
|
visible_formats = '(*.{name})'.format(name='; *.'.join(VIDEO_EXT))
|
||||||
visible_formats = '({name})'.format(name='; '.join(VIDEO_EXT))
|
actual_formats = '(*.{name})'.format(name=' *.'.join(VIDEO_EXT))
|
||||||
actual_formats = '({name})'.format(name=' '.join(VIDEO_EXT))
|
|
||||||
video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),
|
video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),
|
||||||
visible=visible_formats, actual=actual_formats)
|
visible=visible_formats, actual=actual_formats)
|
||||||
self.video_path_edit.filters = '{video};;{ui} (*)'.format(video=video_filter, ui=UiStrings().AllFiles)
|
self.video_path_edit.filters = '{video};;{ui} (*)'.format(video=video_filter, ui=UiStrings().AllFiles)
|
||||||
|
@ -38,7 +38,7 @@ from openlp.core.lib.serviceitem import ItemCapabilities
|
|||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.ui.icons import UiIcons
|
from openlp.core.ui.icons import UiIcons
|
||||||
from openlp.core.ui.media import parse_optical_path, format_milliseconds
|
from openlp.core.ui.media import parse_optical_path, format_milliseconds
|
||||||
from openlp.core.ui.media.vlcplayer import get_vlc
|
from openlp.core.ui.media.vlcplayer import AUDIO_EXT, VIDEO_EXT, get_vlc
|
||||||
|
|
||||||
|
|
||||||
if get_vlc() is not None:
|
if get_vlc() is not None:
|
||||||
@ -232,9 +232,9 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
# self.populate_display_types()
|
# self.populate_display_types()
|
||||||
self.on_new_file_masks = translate('MediaPlugin.MediaItem',
|
self.on_new_file_masks = translate('MediaPlugin.MediaItem',
|
||||||
'Videos ({video});;Audio ({audio});;{files} '
|
'Videos (*.{video});;Audio (*.{audio});;{files} '
|
||||||
'(*)').format(video=' '.join(self.media_controller.video_extensions_list),
|
'(*)').format(video=' *.'.join(VIDEO_EXT),
|
||||||
audio=' '.join(self.media_controller.audio_extensions_list),
|
audio=' *.'.join(AUDIO_EXT),
|
||||||
files=UiStrings().AllFiles)
|
files=UiStrings().AllFiles)
|
||||||
|
|
||||||
def on_delete_click(self):
|
def on_delete_click(self):
|
||||||
@ -302,9 +302,9 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
|||||||
media_file_paths = Settings().value(self.settings_section + '/media files')
|
media_file_paths = Settings().value(self.settings_section + '/media files')
|
||||||
media_file_paths.sort(key=lambda file_path: get_natural_key(file_path.name))
|
media_file_paths.sort(key=lambda file_path: get_natural_key(file_path.name))
|
||||||
if media_type == MediaType.Audio:
|
if media_type == MediaType.Audio:
|
||||||
extension = self.media_controller.audio_extensions_list
|
extension = AUDIO_EXT
|
||||||
else:
|
else:
|
||||||
extension = self.media_controller.video_extensions_list
|
extension = VIDEO_EXT
|
||||||
extension = [x[1:] for x in extension]
|
extension = [x[1:] for x in extension]
|
||||||
media = [x for x in media_file_paths if x.suffix in extension]
|
media = [x for x in media_file_paths if x.suffix in extension]
|
||||||
return media
|
return media
|
||||||
|
@ -36,31 +36,49 @@ import time
|
|||||||
|
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
from openlp.core.common import delete_file, get_uno_command, get_uno_instance, is_win
|
from openlp.core.common import delete_file, get_uno_command, get_uno_instance, is_win, trace_error_handler
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
|
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
|
||||||
TextType
|
TextType
|
||||||
|
|
||||||
|
# Load the XSlideShowListener class so we can inherit from it
|
||||||
if is_win():
|
if is_win():
|
||||||
from win32com.client import Dispatch
|
from win32com.client import Dispatch
|
||||||
import pywintypes
|
import pywintypes
|
||||||
uno_available = False
|
uno_available = False
|
||||||
# Declare an empty exception to match the exception imported from UNO
|
try:
|
||||||
|
service_manager = Dispatch('com.sun.star.ServiceManager')
|
||||||
|
service_manager._FlagAsMethod('Bridge_GetStruct')
|
||||||
|
XSlideShowListenerObj = service_manager.Bridge_GetStruct('com.sun.star.presentation.XSlideShowListener')
|
||||||
|
|
||||||
|
class SlideShowListenerImport(XSlideShowListenerObj.__class__):
|
||||||
|
pass
|
||||||
|
except (AttributeError, pywintypes.com_error):
|
||||||
|
class SlideShowListenerImport():
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Declare an empty exception to match the exception imported from UNO
|
||||||
class ErrorCodeIOException(Exception):
|
class ErrorCodeIOException(Exception):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
import uno
|
import uno
|
||||||
|
import unohelper
|
||||||
from com.sun.star.beans import PropertyValue
|
from com.sun.star.beans import PropertyValue
|
||||||
from com.sun.star.task import ErrorCodeIOException
|
from com.sun.star.task import ErrorCodeIOException
|
||||||
|
from com.sun.star.presentation import XSlideShowListener
|
||||||
|
|
||||||
|
class SlideShowListenerImport(unohelper.Base, XSlideShowListener):
|
||||||
|
pass
|
||||||
|
|
||||||
uno_available = True
|
uno_available = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
uno_available = False
|
uno_available = False
|
||||||
|
|
||||||
|
class SlideShowListenerImport():
|
||||||
|
pass
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -82,6 +100,8 @@ class ImpressController(PresentationController):
|
|||||||
self.process = None
|
self.process = None
|
||||||
self.desktop = None
|
self.desktop = None
|
||||||
self.manager = None
|
self.manager = None
|
||||||
|
self.conf_provider = None
|
||||||
|
self.presenter_screen_disabled_by_openlp = False
|
||||||
|
|
||||||
def check_available(self):
|
def check_available(self):
|
||||||
"""
|
"""
|
||||||
@ -90,7 +110,6 @@ class ImpressController(PresentationController):
|
|||||||
log.debug('check_available')
|
log.debug('check_available')
|
||||||
if is_win():
|
if is_win():
|
||||||
return self.get_com_servicemanager() is not None
|
return self.get_com_servicemanager() is not None
|
||||||
else:
|
|
||||||
return uno_available
|
return uno_available
|
||||||
|
|
||||||
def start_process(self):
|
def start_process(self):
|
||||||
@ -131,6 +150,7 @@ class ImpressController(PresentationController):
|
|||||||
self.manager = uno_instance.ServiceManager
|
self.manager = uno_instance.ServiceManager
|
||||||
log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
|
log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
|
||||||
desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
|
desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
|
||||||
|
self.toggle_presentation_screen(False)
|
||||||
return desktop
|
return desktop
|
||||||
except Exception:
|
except Exception:
|
||||||
log.warning('Failed to get UNO desktop')
|
log.warning('Failed to get UNO desktop')
|
||||||
@ -148,6 +168,7 @@ class ImpressController(PresentationController):
|
|||||||
desktop = self.manager.createInstance('com.sun.star.frame.Desktop')
|
desktop = self.manager.createInstance('com.sun.star.frame.Desktop')
|
||||||
except (AttributeError, pywintypes.com_error):
|
except (AttributeError, pywintypes.com_error):
|
||||||
log.warning('Failure to find desktop - Impress may have closed')
|
log.warning('Failure to find desktop - Impress may have closed')
|
||||||
|
self.toggle_presentation_screen(False)
|
||||||
return desktop if desktop else None
|
return desktop if desktop else None
|
||||||
|
|
||||||
def get_com_servicemanager(self):
|
def get_com_servicemanager(self):
|
||||||
@ -166,6 +187,8 @@ class ImpressController(PresentationController):
|
|||||||
Called at system exit to clean up any running presentations.
|
Called at system exit to clean up any running presentations.
|
||||||
"""
|
"""
|
||||||
log.debug('Kill OpenOffice')
|
log.debug('Kill OpenOffice')
|
||||||
|
if self.presenter_screen_disabled_by_openlp:
|
||||||
|
self.toggle_presentation_screen(True)
|
||||||
while self.docs:
|
while self.docs:
|
||||||
self.docs[0].close_presentation()
|
self.docs[0].close_presentation()
|
||||||
desktop = None
|
desktop = None
|
||||||
@ -195,6 +218,60 @@ class ImpressController(PresentationController):
|
|||||||
except Exception:
|
except Exception:
|
||||||
log.warning('Failed to terminate OpenOffice')
|
log.warning('Failed to terminate OpenOffice')
|
||||||
|
|
||||||
|
def toggle_presentation_screen(self, set_visible):
|
||||||
|
"""
|
||||||
|
Enable or disable the Presentation Screen/Console
|
||||||
|
|
||||||
|
:param bool set_visible: Should the presentation screen/console be set to be visible.
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
# Create Instance of ConfigurationProvider
|
||||||
|
if not self.conf_provider:
|
||||||
|
if is_win():
|
||||||
|
self.conf_provider = self.manager.createInstance('com.sun.star.configuration.ConfigurationProvider')
|
||||||
|
else:
|
||||||
|
self.conf_provider = self.manager.createInstanceWithContext(
|
||||||
|
'com.sun.star.configuration.ConfigurationProvider', uno.getComponentContext())
|
||||||
|
# Setup lookup properties to get Impress settings
|
||||||
|
properties = []
|
||||||
|
properties.append(self.create_property('nodepath', 'org.openoffice.Office.Impress'))
|
||||||
|
properties = tuple(properties)
|
||||||
|
try:
|
||||||
|
# Get an updateable configuration view
|
||||||
|
impress_conf_props = self.conf_provider.createInstanceWithArguments(
|
||||||
|
'com.sun.star.configuration.ConfigurationUpdateAccess', properties)
|
||||||
|
# Get the specific setting for presentation screen
|
||||||
|
presenter_screen_enabled = impress_conf_props.getHierarchicalPropertyValue(
|
||||||
|
'Misc/Start/EnablePresenterScreen')
|
||||||
|
# If the presentation screen is enabled we disable it
|
||||||
|
if presenter_screen_enabled != set_visible:
|
||||||
|
impress_conf_props.setHierarchicalPropertyValue('Misc/Start/EnablePresenterScreen', set_visible)
|
||||||
|
impress_conf_props.commitChanges()
|
||||||
|
# if set_visible is False this is an attempt to disable the Presenter Screen
|
||||||
|
# so we make a note that it has been disabled, so it can be enabled again on close.
|
||||||
|
if set_visible is False:
|
||||||
|
self.presenter_screen_disabled_by_openlp = True
|
||||||
|
except Exception as e:
|
||||||
|
log.exception(e)
|
||||||
|
trace_error_handler(log)
|
||||||
|
|
||||||
|
def create_property(self, name, value):
|
||||||
|
"""
|
||||||
|
Create an OOo style property object which are passed into some Uno methods.
|
||||||
|
|
||||||
|
:param str name: The name of the property
|
||||||
|
:param str value: The value of the property
|
||||||
|
:rtype: com.sun.star.beans.PropertyValue
|
||||||
|
"""
|
||||||
|
log.debug('create property OpenOffice')
|
||||||
|
if is_win():
|
||||||
|
property_object = self.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue')
|
||||||
|
else:
|
||||||
|
property_object = PropertyValue()
|
||||||
|
property_object.Name = name
|
||||||
|
property_object.Value = value
|
||||||
|
return property_object
|
||||||
|
|
||||||
|
|
||||||
class ImpressDocument(PresentationDocument):
|
class ImpressDocument(PresentationDocument):
|
||||||
"""
|
"""
|
||||||
@ -213,6 +290,8 @@ class ImpressDocument(PresentationDocument):
|
|||||||
self.document = None
|
self.document = None
|
||||||
self.presentation = None
|
self.presentation = None
|
||||||
self.control = None
|
self.control = None
|
||||||
|
self.slide_ended = False
|
||||||
|
self.slide_ended_reverse = False
|
||||||
|
|
||||||
def load_presentation(self):
|
def load_presentation(self):
|
||||||
"""
|
"""
|
||||||
@ -233,13 +312,16 @@ class ImpressDocument(PresentationDocument):
|
|||||||
return False
|
return False
|
||||||
self.desktop = desktop
|
self.desktop = desktop
|
||||||
properties = []
|
properties = []
|
||||||
properties.append(self.create_property('Hidden', True))
|
properties.append(self.controller.create_property('Hidden', True))
|
||||||
properties = tuple(properties)
|
properties = tuple(properties)
|
||||||
try:
|
try:
|
||||||
self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties)
|
self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties)
|
||||||
except Exception:
|
except Exception:
|
||||||
log.warning('Failed to load presentation {url}'.format(url=url))
|
log.warning('Failed to load presentation {url}'.format(url=url))
|
||||||
return False
|
return False
|
||||||
|
if self.document is None:
|
||||||
|
log.warning('Presentation {url} could not be loaded'.format(url=url))
|
||||||
|
return False
|
||||||
self.presentation = self.document.getPresentation()
|
self.presentation = self.document.getPresentation()
|
||||||
self.presentation.Display = ScreenList().current.number + 1
|
self.presentation.Display = ScreenList().current.number + 1
|
||||||
self.control = None
|
self.control = None
|
||||||
@ -257,7 +339,7 @@ class ImpressDocument(PresentationDocument):
|
|||||||
temp_folder_path = self.get_temp_folder()
|
temp_folder_path = self.get_temp_folder()
|
||||||
thumb_dir_url = temp_folder_path.as_uri()
|
thumb_dir_url = temp_folder_path.as_uri()
|
||||||
properties = []
|
properties = []
|
||||||
properties.append(self.create_property('FilterName', 'impress_png_Export'))
|
properties.append(self.controller.create_property('FilterName', 'impress_png_Export'))
|
||||||
properties = tuple(properties)
|
properties = tuple(properties)
|
||||||
doc = self.document
|
doc = self.document
|
||||||
pages = doc.getDrawPages()
|
pages = doc.getDrawPages()
|
||||||
@ -279,19 +361,6 @@ class ImpressDocument(PresentationDocument):
|
|||||||
except Exception:
|
except Exception:
|
||||||
log.exception('{path} - Unable to store openoffice preview'.format(path=path))
|
log.exception('{path} - Unable to store openoffice preview'.format(path=path))
|
||||||
|
|
||||||
def create_property(self, name, value):
|
|
||||||
"""
|
|
||||||
Create an OOo style property object which are passed into some Uno methods.
|
|
||||||
"""
|
|
||||||
log.debug('create property OpenOffice')
|
|
||||||
if is_win():
|
|
||||||
property_object = self.controller.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue')
|
|
||||||
else:
|
|
||||||
property_object = PropertyValue()
|
|
||||||
property_object.Name = name
|
|
||||||
property_object.Value = value
|
|
||||||
return property_object
|
|
||||||
|
|
||||||
def close_presentation(self):
|
def close_presentation(self):
|
||||||
"""
|
"""
|
||||||
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
|
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
|
||||||
@ -356,7 +425,6 @@ class ImpressDocument(PresentationDocument):
|
|||||||
log.debug('is blank OpenOffice')
|
log.debug('is blank OpenOffice')
|
||||||
if self.control and self.control.isRunning():
|
if self.control and self.control.isRunning():
|
||||||
return self.control.isPaused()
|
return self.control.isPaused()
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def stop_presentation(self):
|
def stop_presentation(self):
|
||||||
@ -384,6 +452,8 @@ class ImpressDocument(PresentationDocument):
|
|||||||
sleep_count += 1
|
sleep_count += 1
|
||||||
self.control = self.presentation.getController()
|
self.control = self.presentation.getController()
|
||||||
window.setVisible(False)
|
window.setVisible(False)
|
||||||
|
listener = SlideShowListener(self)
|
||||||
|
self.control.getSlideShow().addSlideShowListener(listener)
|
||||||
else:
|
else:
|
||||||
self.control.activate()
|
self.control.activate()
|
||||||
self.goto_slide(1)
|
self.goto_slide(1)
|
||||||
@ -415,17 +485,33 @@ class ImpressDocument(PresentationDocument):
|
|||||||
"""
|
"""
|
||||||
Triggers the next effect of slide on the running presentation.
|
Triggers the next effect of slide on the running presentation.
|
||||||
"""
|
"""
|
||||||
|
# if we are at the presentations end don't go further, just return True
|
||||||
|
if self.slide_ended and self.get_slide_count() == self.get_slide_number():
|
||||||
|
return True
|
||||||
|
self.slide_ended = False
|
||||||
|
self.slide_ended_reverse = False
|
||||||
|
past_end = False
|
||||||
is_paused = self.control.isPaused()
|
is_paused = self.control.isPaused()
|
||||||
self.control.gotoNextEffect()
|
self.control.gotoNextEffect()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
# If for some reason the presentation end was not detected above, this will catch it.
|
||||||
|
# The presentation is set to paused when going past the end.
|
||||||
if not is_paused and self.control.isPaused():
|
if not is_paused and self.control.isPaused():
|
||||||
self.control.gotoPreviousEffect()
|
self.control.gotoPreviousEffect()
|
||||||
|
past_end = True
|
||||||
|
return past_end
|
||||||
|
|
||||||
def previous_step(self):
|
def previous_step(self):
|
||||||
"""
|
"""
|
||||||
Triggers the previous slide on the running presentation.
|
Triggers the previous slide on the running presentation.
|
||||||
"""
|
"""
|
||||||
|
# if we are at the presentations start don't go further back, just return True
|
||||||
|
if self.slide_ended_reverse and self.get_slide_number() == 1:
|
||||||
|
return True
|
||||||
|
self.slide_ended = False
|
||||||
|
self.slide_ended_reverse = False
|
||||||
self.control.gotoPreviousEffect()
|
self.control.gotoPreviousEffect()
|
||||||
|
return False
|
||||||
|
|
||||||
def get_slide_text(self, slide_no):
|
def get_slide_text(self, slide_no):
|
||||||
"""
|
"""
|
||||||
@ -483,3 +569,100 @@ class ImpressDocument(PresentationDocument):
|
|||||||
note = ' '
|
note = ' '
|
||||||
notes.append(note)
|
notes.append(note)
|
||||||
self.save_titles_and_notes(titles, notes)
|
self.save_titles_and_notes(titles, notes)
|
||||||
|
|
||||||
|
|
||||||
|
class SlideShowListener(SlideShowListenerImport):
|
||||||
|
"""
|
||||||
|
Listener interface to receive global slide show events.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, document):
|
||||||
|
"""
|
||||||
|
Constructor
|
||||||
|
|
||||||
|
:param document: The ImpressDocument being presented
|
||||||
|
"""
|
||||||
|
self.document = document
|
||||||
|
|
||||||
|
def paused(self):
|
||||||
|
"""
|
||||||
|
Notify that the slide show is paused
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: paused')
|
||||||
|
|
||||||
|
def resumed(self):
|
||||||
|
"""
|
||||||
|
Notify that the slide show is resumed from a paused state
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: resumed')
|
||||||
|
|
||||||
|
def slideTransitionStarted(self):
|
||||||
|
"""
|
||||||
|
Notify that a new slide starts to become visible.
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: slideTransitionStarted')
|
||||||
|
|
||||||
|
def slideTransitionEnded(self):
|
||||||
|
"""
|
||||||
|
Notify that the slide transtion of the current slide ended.
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: slideTransitionEnded')
|
||||||
|
|
||||||
|
def slideAnimationsEnded(self):
|
||||||
|
"""
|
||||||
|
Notify that the last animation from the main sequence of the current slide has ended.
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: slideAnimationsEnded')
|
||||||
|
if not Registry().get('main_window').isActiveWindow():
|
||||||
|
log.debug('main window is not in focus - should update slidecontroller')
|
||||||
|
Registry().execute('slidecontroller_live_change', self.document.control.getCurrentSlideIndex() + 1)
|
||||||
|
|
||||||
|
def slideEnded(self, reverse):
|
||||||
|
"""
|
||||||
|
Notify that the current slide has ended, e.g. the user has clicked on the slide. Calling displaySlide()
|
||||||
|
twice will not issue this event.
|
||||||
|
|
||||||
|
:param bool reverse: Whether or not the direction of the "slide movement" is reversed/backwards.
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: slideEnded %d' % reverse)
|
||||||
|
if reverse:
|
||||||
|
self.document.slide_ended = False
|
||||||
|
self.document.slide_ended_reverse = True
|
||||||
|
else:
|
||||||
|
self.document.slide_ended = True
|
||||||
|
self.document.slide_ended_reverse = False
|
||||||
|
|
||||||
|
def hyperLinkClicked(self, hyperLink):
|
||||||
|
"""
|
||||||
|
Notifies that a hyperlink has been clicked.
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: hyperLinkClicked %s' % hyperLink)
|
||||||
|
|
||||||
|
def disposing(self, source):
|
||||||
|
"""
|
||||||
|
gets called when the broadcaster is about to be disposed.
|
||||||
|
:param source:
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: disposing')
|
||||||
|
|
||||||
|
def beginEvent(self, node):
|
||||||
|
"""
|
||||||
|
This event is raised when the element local timeline begins to play.
|
||||||
|
:param node:
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: beginEvent')
|
||||||
|
|
||||||
|
def endEvent(self, node):
|
||||||
|
"""
|
||||||
|
This event is raised at the active end of the element.
|
||||||
|
:param node:
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: endEvent')
|
||||||
|
|
||||||
|
def repeat(self, node):
|
||||||
|
"""
|
||||||
|
This event is raised when the element local timeline repeats.
|
||||||
|
:param node:
|
||||||
|
"""
|
||||||
|
log.debug('LibreOffice SlideShowListener event: repeat')
|
||||||
|
@ -169,24 +169,21 @@ class Controller(object):
|
|||||||
"""
|
"""
|
||||||
log.debug('Live = {live}, next'.format(live=self.is_live))
|
log.debug('Live = {live}, next'.format(live=self.is_live))
|
||||||
if not self.doc:
|
if not self.doc:
|
||||||
return
|
return False
|
||||||
if not self.is_live:
|
if not self.is_live:
|
||||||
return
|
return False
|
||||||
if self.hide_mode:
|
if self.hide_mode:
|
||||||
if not self.doc.is_active():
|
if not self.doc.is_active():
|
||||||
return
|
return False
|
||||||
if self.doc.slidenumber < self.doc.get_slide_count():
|
if self.doc.slidenumber < self.doc.get_slide_count():
|
||||||
self.doc.slidenumber += 1
|
self.doc.slidenumber += 1
|
||||||
self.poll()
|
self.poll()
|
||||||
return
|
return False
|
||||||
if not self.activate():
|
if not self.activate():
|
||||||
return
|
return False
|
||||||
# The "End of slideshow" screen is after the last slide. Note, we can't just stop on the last slide, since it
|
ret = self.doc.next_step()
|
||||||
# may contain animations that need to be stepped through.
|
|
||||||
if self.doc.slidenumber > self.doc.get_slide_count():
|
|
||||||
return
|
|
||||||
self.doc.next_step()
|
|
||||||
self.poll()
|
self.poll()
|
||||||
|
return ret
|
||||||
|
|
||||||
def previous(self):
|
def previous(self):
|
||||||
"""
|
"""
|
||||||
@ -194,20 +191,21 @@ class Controller(object):
|
|||||||
"""
|
"""
|
||||||
log.debug('Live = {live}, previous'.format(live=self.is_live))
|
log.debug('Live = {live}, previous'.format(live=self.is_live))
|
||||||
if not self.doc:
|
if not self.doc:
|
||||||
return
|
return False
|
||||||
if not self.is_live:
|
if not self.is_live:
|
||||||
return
|
return False
|
||||||
if self.hide_mode:
|
if self.hide_mode:
|
||||||
if not self.doc.is_active():
|
if not self.doc.is_active():
|
||||||
return
|
return False
|
||||||
if self.doc.slidenumber > 1:
|
if self.doc.slidenumber > 1:
|
||||||
self.doc.slidenumber -= 1
|
self.doc.slidenumber -= 1
|
||||||
self.poll()
|
self.poll()
|
||||||
return
|
return False
|
||||||
if not self.activate():
|
if not self.activate():
|
||||||
return
|
return False
|
||||||
self.doc.previous_step()
|
ret = self.doc.previous_step()
|
||||||
self.poll()
|
self.poll()
|
||||||
|
return ret
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""
|
"""
|
||||||
@ -418,11 +416,12 @@ class MessageListener(object):
|
|||||||
"""
|
"""
|
||||||
is_live = message[1]
|
is_live = message[1]
|
||||||
if is_live:
|
if is_live:
|
||||||
self.live_handler.next()
|
ret = self.live_handler.next()
|
||||||
if Settings().value('core/click live slide to unblank'):
|
if Settings().value('core/click live slide to unblank'):
|
||||||
Registry().execute('slidecontroller_live_unblank')
|
Registry().execute('slidecontroller_live_unblank')
|
||||||
|
return ret
|
||||||
else:
|
else:
|
||||||
self.preview_handler.next()
|
return self.preview_handler.next()
|
||||||
|
|
||||||
def previous(self, message):
|
def previous(self, message):
|
||||||
"""
|
"""
|
||||||
@ -432,11 +431,12 @@ class MessageListener(object):
|
|||||||
"""
|
"""
|
||||||
is_live = message[1]
|
is_live = message[1]
|
||||||
if is_live:
|
if is_live:
|
||||||
self.live_handler.previous()
|
ret = self.live_handler.previous()
|
||||||
if Settings().value('core/click live slide to unblank'):
|
if Settings().value('core/click live slide to unblank'):
|
||||||
Registry().execute('slidecontroller_live_unblank')
|
Registry().execute('slidecontroller_live_unblank')
|
||||||
|
return ret
|
||||||
else:
|
else:
|
||||||
self.preview_handler.previous()
|
return self.preview_handler.previous()
|
||||||
|
|
||||||
def shutdown(self, message):
|
def shutdown(self, message):
|
||||||
"""
|
"""
|
||||||
|
@ -145,8 +145,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
try:
|
try:
|
||||||
if not self.controller.process:
|
if not self.controller.process:
|
||||||
self.controller.start_process()
|
self.controller.start_process()
|
||||||
self.controller.process.Presentations.Open(str(self.file_path), False, False, False)
|
self.presentation = self.controller.process.Presentations.Open(str(self.file_path), False, False, False)
|
||||||
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
|
log.debug('Loaded presentation %s' % self.presentation.FullName)
|
||||||
self.create_thumbnails()
|
self.create_thumbnails()
|
||||||
self.create_titles_and_notes()
|
self.create_titles_and_notes()
|
||||||
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
|
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
|
||||||
@ -170,12 +170,15 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
However, for the moment, we want a physical file since it makes life easier elsewhere.
|
However, for the moment, we want a physical file since it makes life easier elsewhere.
|
||||||
"""
|
"""
|
||||||
log.debug('create_thumbnails')
|
log.debug('create_thumbnails')
|
||||||
|
generate_thumbs = True
|
||||||
if self.check_thumbnails():
|
if self.check_thumbnails():
|
||||||
return
|
# No need for thumbnails but we still need the index
|
||||||
|
generate_thumbs = False
|
||||||
key = 1
|
key = 1
|
||||||
for num in range(self.presentation.Slides.Count):
|
for num in range(self.presentation.Slides.Count):
|
||||||
if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden:
|
if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden:
|
||||||
self.index_map[key] = num + 1
|
self.index_map[key] = num + 1
|
||||||
|
if generate_thumbs:
|
||||||
self.presentation.Slides(num + 1).Export(
|
self.presentation.Slides(num + 1).Export(
|
||||||
str(self.get_thumbnail_folder() / 'slide{key:d}.png'.format(key=key)), 'png', 320, 240)
|
str(self.get_thumbnail_folder() / 'slide{key:d}.png'.format(key=key)), 'png', 320, 240)
|
||||||
key += 1
|
key += 1
|
||||||
@ -318,6 +321,9 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
size = ScreenList().current.display_geometry
|
size = ScreenList().current.display_geometry
|
||||||
ppt_window = None
|
ppt_window = None
|
||||||
try:
|
try:
|
||||||
|
# Disable the presentation console
|
||||||
|
self.presentation.SlideShowSettings.ShowPresenterView = 0
|
||||||
|
# Start the presentation
|
||||||
ppt_window = self.presentation.SlideShowSettings.Run()
|
ppt_window = self.presentation.SlideShowSettings.Run()
|
||||||
except (AttributeError, pywintypes.com_error):
|
except (AttributeError, pywintypes.com_error):
|
||||||
log.exception('Caught exception while in start_presentation')
|
log.exception('Caught exception while in start_presentation')
|
||||||
@ -437,6 +443,12 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
Triggers the next effect of slide on the running presentation.
|
Triggers the next effect of slide on the running presentation.
|
||||||
"""
|
"""
|
||||||
log.debug('next_step')
|
log.debug('next_step')
|
||||||
|
# if we are at the presentations end don't go further, just return True
|
||||||
|
if self.presentation.SlideShowWindow.View.GetClickCount() == \
|
||||||
|
self.presentation.SlideShowWindow.View.GetClickIndex() \
|
||||||
|
and self.get_slide_number() == self.get_slide_count():
|
||||||
|
return True
|
||||||
|
past_end = False
|
||||||
try:
|
try:
|
||||||
self.presentation.SlideShowWindow.Activate()
|
self.presentation.SlideShowWindow.Activate()
|
||||||
self.presentation.SlideShowWindow.View.Next()
|
self.presentation.SlideShowWindow.View.Next()
|
||||||
@ -444,28 +456,35 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
log.exception('Caught exception while in next_step')
|
log.exception('Caught exception while in next_step')
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
return
|
return past_end
|
||||||
|
# If for some reason the presentation end was not detected above, this will catch it.
|
||||||
if self.get_slide_number() > self.get_slide_count():
|
if self.get_slide_number() > self.get_slide_count():
|
||||||
log.debug('past end, stepping back to previous')
|
log.debug('past end, stepping back to previous')
|
||||||
self.previous_step()
|
self.previous_step()
|
||||||
|
past_end = True
|
||||||
# Stop powerpoint from flashing in the taskbar
|
# Stop powerpoint from flashing in the taskbar
|
||||||
if self.presentation_hwnd:
|
if self.presentation_hwnd:
|
||||||
win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)
|
win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)
|
||||||
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
|
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
|
||||||
if len(ScreenList()) > 1:
|
if len(ScreenList()) > 1:
|
||||||
Registry().get('main_window').activateWindow()
|
Registry().get('main_window').activateWindow()
|
||||||
|
return past_end
|
||||||
|
|
||||||
def previous_step(self):
|
def previous_step(self):
|
||||||
"""
|
"""
|
||||||
Triggers the previous slide on the running presentation.
|
Triggers the previous slide on the running presentation.
|
||||||
"""
|
"""
|
||||||
log.debug('previous_step')
|
log.debug('previous_step')
|
||||||
|
# if we are at the presentations start we can't go further back, just return True
|
||||||
|
if self.presentation.SlideShowWindow.View.GetClickIndex() == 0 and self.get_slide_number() == 1:
|
||||||
|
return True
|
||||||
try:
|
try:
|
||||||
self.presentation.SlideShowWindow.View.Previous()
|
self.presentation.SlideShowWindow.View.Previous()
|
||||||
except (AttributeError, pywintypes.com_error):
|
except (AttributeError, pywintypes.com_error):
|
||||||
log.exception('Caught exception while in previous_step')
|
log.exception('Caught exception while in previous_step')
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
|
return False
|
||||||
|
|
||||||
def get_slide_text(self, slide_no):
|
def get_slide_text(self, slide_no):
|
||||||
"""
|
"""
|
||||||
|
@ -248,15 +248,17 @@ class PresentationDocument(object):
|
|||||||
def next_step(self):
|
def next_step(self):
|
||||||
"""
|
"""
|
||||||
Triggers the next effect of slide on the running presentation. This might be the next animation on the current
|
Triggers the next effect of slide on the running presentation. This might be the next animation on the current
|
||||||
slide, or the next slide
|
slide, or the next slide.
|
||||||
|
:rtype bool: True if we stepped beyond the slides of the presentation
|
||||||
"""
|
"""
|
||||||
pass
|
return False
|
||||||
|
|
||||||
def previous_step(self):
|
def previous_step(self):
|
||||||
"""
|
"""
|
||||||
Triggers the previous slide on the running presentation
|
Triggers the previous slide on the running presentation
|
||||||
|
:rtype bool: True if we stepped beyond the slides of the presentation
|
||||||
"""
|
"""
|
||||||
pass
|
return False
|
||||||
|
|
||||||
def convert_thumbnail(self, image_path, index):
|
def convert_thumbnail(self, image_path, index):
|
||||||
"""
|
"""
|
||||||
|
@ -100,12 +100,13 @@ OPTIONAL_MODULES = [
|
|||||||
('pyodbc', '(ODBC support)'),
|
('pyodbc', '(ODBC support)'),
|
||||||
('psycopg2', '(PostgreSQL support)'),
|
('psycopg2', '(PostgreSQL support)'),
|
||||||
('enchant', '(spell checker)'),
|
('enchant', '(spell checker)'),
|
||||||
|
('fitz', '(executable-independent PDF support)'),
|
||||||
('pysword', '(import SWORD bibles)'),
|
('pysword', '(import SWORD bibles)'),
|
||||||
('uno', '(LibreOffice/OpenOffice support)'),
|
('uno', '(LibreOffice/OpenOffice support)'),
|
||||||
# development/testing modules
|
# development/testing modules
|
||||||
('jenkins', '(access jenkins api)'),
|
('jenkins', '(access jenkins api)'),
|
||||||
('launchpadlib', '(launchpad script support)'),
|
('launchpadlib', '(launchpad script support)'),
|
||||||
('nose2', '(testing framework)'),
|
('pytest', '(testing framework)'),
|
||||||
('pylint', '(linter)')
|
('pylint', '(linter)')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
# E722 do not use bare except, specify exception instead
|
# E722 do not use bare except, specify exception instead
|
||||||
# F841 local variable '<variable>' is assigned to but never used
|
# F841 local variable '<variable>' is assigned to but never used
|
||||||
|
|
||||||
|
[aliases]
|
||||||
|
test=pytest
|
||||||
|
|
||||||
[pep8]
|
[pep8]
|
||||||
exclude=resources.py,vlc.py
|
exclude=resources.py,vlc.py
|
||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
|
5
setup.py
5
setup.py
@ -178,6 +178,7 @@ using a computer and a data projector.""",
|
|||||||
'pyobjc-framework-Cocoa; platform_system=="Darwin"',
|
'pyobjc-framework-Cocoa; platform_system=="Darwin"',
|
||||||
'PyQt5 >= 5.12',
|
'PyQt5 >= 5.12',
|
||||||
'PyQtWebEngine',
|
'PyQtWebEngine',
|
||||||
|
'python-vlc',
|
||||||
'pywin32; platform_system=="Windows"',
|
'pywin32; platform_system=="Windows"',
|
||||||
'QtAwesome',
|
'QtAwesome',
|
||||||
'requests',
|
'requests',
|
||||||
@ -199,13 +200,13 @@ using a computer and a data projector.""",
|
|||||||
'launchpad': ['launchpadlib']
|
'launchpad': ['launchpadlib']
|
||||||
},
|
},
|
||||||
tests_require=[
|
tests_require=[
|
||||||
'nose2',
|
|
||||||
'pylint',
|
'pylint',
|
||||||
'PyMuPDF',
|
'PyMuPDF',
|
||||||
'pyodbc',
|
'pyodbc',
|
||||||
'pysword',
|
'pysword',
|
||||||
|
'pytest',
|
||||||
'python-xlib; platform_system=="Linux"'
|
'python-xlib; platform_system=="Linux"'
|
||||||
],
|
],
|
||||||
test_suite='nose2.collector.collector',
|
setup_requires=['pytest-runner'],
|
||||||
entry_points={'gui_scripts': ['openlp = run_openlp:start']}
|
entry_points={'gui_scripts': ['openlp = run_openlp:start']}
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
"""
|
"""
|
||||||
Package to test the openlp.core.common.path package.
|
Package to test the openlp.core.common.path package.
|
||||||
"""
|
"""
|
||||||
# TODO: fix patches
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
@ -27,7 +27,6 @@ from unittest.mock import MagicMock, patch
|
|||||||
|
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.ui.media.mediacontroller import MediaController
|
from openlp.core.ui.media.mediacontroller import MediaController
|
||||||
from openlp.core.ui.media.vlcplayer import VlcPlayer
|
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
|
|
||||||
from tests.utils.constants import RESOURCE_PATH
|
from tests.utils.constants import RESOURCE_PATH
|
||||||
@ -43,26 +42,6 @@ class TestMediaController(TestCase, TestMixin):
|
|||||||
Registry.create()
|
Registry.create()
|
||||||
Registry().register('service_manager', MagicMock())
|
Registry().register('service_manager', MagicMock())
|
||||||
|
|
||||||
def test_generate_extensions_lists(self):
|
|
||||||
"""
|
|
||||||
Test that the extensions are create correctly
|
|
||||||
"""
|
|
||||||
# GIVEN: A MediaController and an active player with audio and video extensions
|
|
||||||
media_controller = MediaController()
|
|
||||||
media_controller.vlc_player = VlcPlayer(None)
|
|
||||||
media_controller.vlc_player.is_active = True
|
|
||||||
media_controller.vlc_player.audio_extensions_list = ['*.mp3', '*.wav', '*.wma', '*.ogg']
|
|
||||||
media_controller.vlc_player.video_extensions_list = ['*.mp4', '*.mov', '*.avi', '*.ogm']
|
|
||||||
|
|
||||||
# WHEN: calling _generate_extensions_lists
|
|
||||||
media_controller._generate_extensions_lists()
|
|
||||||
|
|
||||||
# THEN: extensions list should have been copied from the player to the mediacontroller
|
|
||||||
assert media_controller.video_extensions_list == media_controller.video_extensions_list, \
|
|
||||||
'Video extensions should be the same'
|
|
||||||
assert media_controller.audio_extensions_list == media_controller.audio_extensions_list, \
|
|
||||||
'Audio extensions should be the same'
|
|
||||||
|
|
||||||
def test_resize(self):
|
def test_resize(self):
|
||||||
"""
|
"""
|
||||||
Test that the resize method is called correctly
|
Test that the resize method is called correctly
|
||||||
|
@ -30,7 +30,7 @@ from unittest.mock import MagicMock, call, patch
|
|||||||
|
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.ui.media import MediaState, MediaType
|
from openlp.core.ui.media import MediaState, MediaType
|
||||||
from openlp.core.ui.media.vlcplayer import AUDIO_EXT, VIDEO_EXT, VlcPlayer, get_vlc
|
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
|
||||||
from tests.helpers import MockDateTime
|
from tests.helpers import MockDateTime
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
|
|
||||||
@ -95,8 +95,6 @@ class TestVLCPlayer(TestCase, TestMixin):
|
|||||||
assert '&VLC' == vlc_player.display_name
|
assert '&VLC' == vlc_player.display_name
|
||||||
assert vlc_player.parent is None
|
assert vlc_player.parent is None
|
||||||
assert vlc_player.can_folder is True
|
assert vlc_player.can_folder is True
|
||||||
assert AUDIO_EXT == vlc_player.audio_extensions_list
|
|
||||||
assert VIDEO_EXT == vlc_player.video_extensions_list
|
|
||||||
|
|
||||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||||
@patch('openlp.core.ui.media.vlcplayer.is_macosx')
|
@patch('openlp.core.ui.media.vlcplayer.is_macosx')
|
||||||
@ -958,20 +956,3 @@ class TestVLCPlayer(TestCase, TestMixin):
|
|||||||
mocked_controller.seek_slider.setSliderPosition.assert_called_with(300)
|
mocked_controller.seek_slider.setSliderPosition.assert_called_with(300)
|
||||||
expected_calls = [call(True), call(False)]
|
expected_calls = [call(True), call(False)]
|
||||||
assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list
|
assert expected_calls == mocked_controller.seek_slider.blockSignals.call_args_list
|
||||||
|
|
||||||
@patch('openlp.core.ui.media.vlcplayer.translate')
|
|
||||||
def test_get_info(self, mocked_translate):
|
|
||||||
"""
|
|
||||||
Test that get_info() returns some information about the VLC player
|
|
||||||
"""
|
|
||||||
# GIVEN: A VlcPlayer
|
|
||||||
mocked_translate.side_effect = lambda *x: x[1]
|
|
||||||
vlc_player = VlcPlayer(None)
|
|
||||||
|
|
||||||
# WHEN: get_info() is run
|
|
||||||
info = vlc_player.get_info()
|
|
||||||
|
|
||||||
# THEN: The information should be correct
|
|
||||||
assert 'VLC is an external player which supports a number of different formats.<br/> ' \
|
|
||||||
'<strong>Audio</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>Video</strong><br/>' + \
|
|
||||||
str(VIDEO_EXT) + '<br/>' == info
|
|
||||||
|
Loading…
Reference in New Issue
Block a user