diff --git a/openlp/core/api/endpoint/controller.py b/openlp/core/api/endpoint/controller.py index 4e897f370..3523568aa 100644 --- a/openlp/core/api/endpoint/controller.py +++ b/openlp/core/api/endpoint/controller.py @@ -91,6 +91,7 @@ def controller_text(request): item['text'] = str(frame['title']) item['html'] = str(frame['title']) item['selected'] = (live_controller.selected_row == index) + item['title'] = current_item.title data.append(item) json_data = {'results': {'slides': data}} if current_item: @@ -117,12 +118,11 @@ def controller_set(request): return {'results': {'success': True}} -@controller_endpoint.route('{action:next|previous}') +@controller_endpoint.route('{controller}/{action:next|previous}') @requires_auth def controller_direction(request, controller, action): """ Handles requests for setting service items in the slide controller -11 :param request: The http request object. :param controller: the controller slides forward or backward. :param action: the controller slides forward or backward. @@ -137,7 +137,7 @@ def controller_direction(request, controller, action): def controller_direction_api(request, controller, action): """ Handles requests for setting service items in the slide controller -11 + :param request: The http request object. :param controller: the controller slides forward or backward. :param action: the controller slides forward or backward. diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index d1f62fb8b..8d939a97f 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -62,7 +62,7 @@ def get_local_ip4(): """ # Get the local IPv4 active address(es) that are NOT localhost (lo or '127.0.0.1') log.debug('Getting local IPv4 interface(es) information') - MY_IP4 = {} + my_ip4 = {} for iface in QNetworkInterface.allInterfaces(): if not iface.isValid() or not (iface.flags() & (QNetworkInterface.IsUp | QNetworkInterface.IsRunning)): continue @@ -70,25 +70,25 @@ def get_local_ip4(): ip = address.ip() # NOTE: Next line will skip if interface is localhost - keep for now until we decide about it later # if (ip.protocol() == QAbstractSocket.IPv4Protocol) and (ip != QHostAddress.LocalHost): - if (ip.protocol() == QAbstractSocket.IPv4Protocol): - MY_IP4[iface.name()] = {'ip': ip.toString(), + if ip.protocol() == QAbstractSocket.IPv4Protocol: + my_ip4[iface.name()] = {'ip': ip.toString(), 'broadcast': address.broadcast().toString(), 'netmask': address.netmask().toString(), 'prefix': address.prefixLength(), 'localnet': QHostAddress(address.netmask().toIPv4Address() & - ip.toIPv4Address()).toString() + ip.toIPv4Address()).toString() } log.debug('Adding {iface} to active list'.format(iface=iface.name())) - if len(MY_IP4) == 1: - if 'lo' in MY_IP4: + if len(my_ip4) == 1: + if 'lo' in my_ip4: # No active interfaces - so leave localhost in there log.warning('No active IPv4 interfaces found except localhost') else: # Since we have a valid IP4 interface, remove localhost log.debug('Found at least one IPv4 interface, removing localhost') - MY_IP4.pop('lo') + my_ip4.pop('lo') - return MY_IP4 + return my_ip4 def trace_error_handler(logger): diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 94a0e19f1..570c6d843 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -121,6 +121,9 @@ class ItemCapabilities(object): ``HasThumbnails`` The item has related thumbnails available + + ``HasMetaData`` + The item has Meta Data about item """ CanPreview = 1 CanEdit = 2 @@ -143,6 +146,7 @@ class ItemCapabilities(object): HasDisplayTitle = 19 HasNotes = 20 HasThumbnails = 21 + HasMetaData = 22 class ServiceItem(RegistryProperties): @@ -200,6 +204,7 @@ class ServiceItem(RegistryProperties): self.will_auto_start = False self.has_original_files = True self._new_item() + self.metadata = [] def _new_item(self): """ @@ -375,7 +380,8 @@ class ServiceItem(RegistryProperties): 'background_audio': self.background_audio, 'theme_overwritten': self.theme_overwritten, 'will_auto_start': self.will_auto_start, - 'processor': self.processor + 'processor': self.processor, + 'metadata': self.metadata } service_data = [] if self.service_item_type == ServiceItemType.Text: @@ -426,6 +432,7 @@ class ServiceItem(RegistryProperties): self.will_auto_start = header.get('will_auto_start', False) self.processor = header.get('processor', None) self.has_original_files = True + self.metadata = header.get('item_meta_data', []) if 'background_audio' in header: self.background_audio = [] for file_path in header['background_audio']: diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index faa481455..1d8ce5f01 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -127,9 +127,6 @@ class UiFirstTimeWizard(object): self.media_check_box.setChecked(True) self.media_check_box.setObjectName('media_check_box') self.plugin_layout.addWidget(self.media_check_box) - self.remote_check_box = QtWidgets.QCheckBox(self.plugin_page) - self.remote_check_box.setObjectName('remote_check_box') - self.plugin_layout.addWidget(self.remote_check_box) self.song_usage_check_box = QtWidgets.QCheckBox(self.plugin_page) self.song_usage_check_box.setChecked(True) self.song_usage_check_box.setObjectName('song_usage_check_box') @@ -138,13 +135,6 @@ class UiFirstTimeWizard(object): self.alert_check_box.setChecked(True) self.alert_check_box.setObjectName('alert_check_box') self.plugin_layout.addWidget(self.alert_check_box) - self.projectors_check_box = QtWidgets.QCheckBox(self.plugin_page) - # If visibility setting for projector panel is True, check the box. - if Settings().value('projector/show after wizard'): - self.projectors_check_box.setChecked(True) - self.projectors_check_box.setObjectName('projectors_check_box') - self.projectors_check_box.clicked.connect(self.on_projectors_check_box_clicked) - self.plugin_layout.addWidget(self.projectors_check_box) first_time_wizard.setPage(FirstTimePage.Plugins, self.plugin_page) # The song samples page self.songs_page = QtWidgets.QWizardPage() @@ -256,13 +246,9 @@ class UiFirstTimeWizard(object): self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Presentations – Show .ppt, .odp and .pdf files')) self.media_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Media – Playback of Audio and Video files')) - self.remote_check_box.setText(str(UiStrings().WebDownloadText)) self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Song Usage Monitor')) self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Alerts – Display informative messages while showing other slides')) - self.projectors_check_box.setText(translate('OpenLP.FirstTimeWizard', - 'Projector Controller – Control PJLink compatible projects on your' - ' network from OpenLP')) self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection')) self.no_internet_page.setSubTitle( translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.')) diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index ed593dd88..1368be39d 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -179,7 +179,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties): """ Check to see if we have any media Player's available. """ - log.debug('_check_available_media_players') controller_dir = os.path.join('core', 'ui', 'media') # Find all files that do not begin with '.' (lp:#1738047) and end with player.py glob_pattern = os.path.join(controller_dir, '[!.]*player.py') diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 5969f7bda..2e5d29e4d 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -751,7 +751,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def context_menu(self, point): """ - The Right click context menu from the Serviceitem list + The Right click context menu from the Service item list :param point: The location of the cursor. """ @@ -1136,7 +1136,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def on_delete_from_service(self): """ Remove the current ServiceItem from the list. - :param field: """ item = self.find_service_item()[0] if item != -1: @@ -1205,6 +1204,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi tips.append('{text1}: {text2}'.format(text1=text1, text2=text2)) if item['service_item'].is_capable(ItemCapabilities.HasVariableStartTime): tips.append(item['service_item'].get_media_time()) + if item['service_item'].is_capable(ItemCapabilities.HasMetaData): + for meta in item['service_item'].metadata: + tips.append(meta) tree_widget_item.setToolTip(0, '
'.join(tips)) tree_widget_item.setData(0, QtCore.Qt.UserRole, item['order']) tree_widget_item.setSelected(item['selected']) @@ -1362,7 +1364,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def make_preview(self): """ Send the current item to the Preview slide controller - :param field: """ self.application.set_busy_cursor() item, child = self.find_service_item() @@ -1387,7 +1388,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def on_double_click_live(self): """ Send the current item to the Live slide controller but triggered by a tablewidget click event. - :param field: """ self.list_double_clicked = True self.make_live() @@ -1396,7 +1396,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi """ If single click previewing is enabled, and triggered by a tablewidget click event, start a timeout to verify a double-click hasn't triggered. - :param field: """ if Settings().value('advanced/single click service preview'): if not self.list_double_clicked: @@ -1407,7 +1406,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def on_single_click_preview_timeout(self): """ If a single click ok, but double click not triggered, send the current item to the Preview slide controller. - :param field: """ if self.list_double_clicked: # If a double click has registered, clear it. @@ -1447,7 +1445,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def remote_edit(self): """ Triggers a remote edit to a plugin to allow item to be edited. - :param field: """ item = self.find_service_item()[0] if self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEdit): @@ -1459,8 +1456,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def on_service_item_rename(self): """ Opens a dialog to rename the service item. - - :param field: Not used, but PyQt needs this. """ item = self.find_service_item()[0] if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle): @@ -1477,7 +1472,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def create_custom(self): """ Saves the current text item as a custom slide - :param field: """ item = self.find_service_item()[0] Registry().execute('custom_create_from_service', self.service_items[item]['service_item']) @@ -1597,8 +1591,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi def on_theme_change_action(self): """ Handles theme change events - - :param field: """ theme = self.sender().objectName() # No object name means that the "Default" theme is supposed to be used. diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 68c1b2e53..581e01253 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -318,6 +318,10 @@ class SlideController(DisplayController, LogMixin, RegistryProperties): tooltip=translate('OpenLP.SlideController', 'Edit and reload song preview.'), triggers=self.on_edit_song) + self.toolbar.add_toolbar_action('clear', icon=':/general/general_delete.png', + tooltip=translate('OpenLP.SlideController', + 'Clear'), + triggers=self.on_clear) self.controller_layout.addWidget(self.toolbar) # Build the Media Toolbar self.media_controller.register_controller(self) @@ -356,7 +360,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties): self.audio_time_label.setObjectName('audio_time_label') self.toolbar.add_toolbar_widget(self.audio_time_label) self.toolbar.set_widget_visible(AUDIO_LIST, False) - self.toolbar.set_widget_visible(['song_menu'], False) + self.toolbar.set_widget_visible('song_menu', False) # Screen preview area self.preview_frame = QtWidgets.QFrame(self.splitter) self.preview_frame.setGeometry(QtCore.QRect(0, 0, 300, 300 * self.ratio)) @@ -427,7 +431,8 @@ class SlideController(DisplayController, LogMixin, RegistryProperties): self.__add_actions_to_widget(self.controller) else: self.preview_widget.doubleClicked.connect(self.on_preview_double_click) - self.toolbar.set_widget_visible(['editSong'], False) + self.toolbar.set_widget_visible('editSong', False) + self.toolbar.set_widget_visible('clear', False) self.controller.addActions([self.next_item, self.previous_item]) Registry().register_function('slidecontroller_{text}_stop_loop'.format(text=self.type_prefix), self.on_stop_loop) @@ -726,7 +731,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties): self.mediabar.hide() self.song_menu.hide() self.toolbar.set_widget_visible(LOOP_LIST, False) - self.toolbar.set_widget_visible(['song_menu'], False) + self.toolbar.set_widget_visible('song_menu', False) # Reset the button self.play_slides_once.setChecked(False) self.play_slides_once.setIcon(build_icon(':/media/media_time.png')) @@ -737,7 +742,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties): if item.is_text(): if (Settings().value(self.main_window.songs_settings_section + '/display songbar') and not self.song_menu.menu().isEmpty()): - self.toolbar.set_widget_visible(['song_menu'], True) + self.toolbar.set_widget_visible('song_menu', True) if item.is_capable(ItemCapabilities.CanLoop) and len(item.get_frames()) > 1: self.toolbar.set_widget_visible(LOOP_LIST) if item.is_media(): @@ -762,9 +767,10 @@ class SlideController(DisplayController, LogMixin, RegistryProperties): # See bug #791050 self.toolbar.hide() self.mediabar.hide() - self.toolbar.set_widget_visible(['editSong'], False) + self.toolbar.set_widget_visible('editSong', False) + self.toolbar.set_widget_visible('clear', True) if item.is_capable(ItemCapabilities.CanEdit) and item.from_plugin: - self.toolbar.set_widget_visible(['editSong']) + self.toolbar.set_widget_visible('editSong') elif item.is_media(): self.mediabar.show() self.previous_item.setVisible(not item.is_media()) @@ -1381,6 +1387,14 @@ class SlideController(DisplayController, LogMixin, RegistryProperties): if new_item: self.add_service_item(new_item) + def on_clear(self): + """ + Clear the preview bar. + """ + self.preview_widget.clear_list() + self.toolbar.set_widget_visible('editSong', False) + self.toolbar.set_widget_visible('clear', False) + def on_preview_add_to_service(self): """ From the preview display request the Item to be added to service diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 4a544b11d..a1b1d7a55 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -428,8 +428,8 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R self.log_exception('Export Theme Failed') critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'), translate('OpenLP.ThemeManager', - 'The theme_name export failed because this error occurred: {err}') - .format(err=ose.strerror)) + 'The {theme_name} export failed because this error occurred: {err}') + .format(theme_name=theme_name, err=ose.strerror)) if theme_path.exists(): theme_path.rmtree(ignore_errors=True) return False diff --git a/openlp/core/widgets/toolbar.py b/openlp/core/widgets/toolbar.py index 758a62605..e0ba4e301 100644 --- a/openlp/core/widgets/toolbar.py +++ b/openlp/core/widgets/toolbar.py @@ -67,14 +67,20 @@ class OpenLPToolbar(QtWidgets.QToolBar): """ Set the visibility for a widget or a list of widgets. - :param widgets: A list of string with widget object names. + :param widgets: A list of strings or individual string with widget object names. :param visible: The new state as bool. """ - for handle in widgets: - if handle in self.actions: - self.actions[handle].setVisible(visible) + if isinstance(widgets, list): + for handle in widgets: + if handle in self.actions: + self.actions[handle].setVisible(visible) + else: + log.warning('No handle "%s" in actions list.', str(handle)) + else: + if widgets in self.actions: + self.actions[widgets].setVisible(visible) else: - log.warning('No handle "%s" in actions list.', str(handle)) + log.warning('No handle "%s" in actions list.', str(widgets)) def set_widget_enabled(self, widgets, enabled=True): """ diff --git a/openlp/core/widgets/views.py b/openlp/core/widgets/views.py index 5adec3e27..bcd96edb9 100644 --- a/openlp/core/widgets/views.py +++ b/openlp/core/widgets/views.py @@ -146,6 +146,14 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): self.screen_ratio = screen_ratio self.__recalculate_layout() + def clear_list(self): + """ + Clear the preview list + :return: + """ + self.setRowCount(0) + self.clear() + def replace_service_item(self, service_item, width, slide_number): """ Replace the current preview items with the ones in service_item and display the given slide @@ -156,8 +164,7 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties): """ self.service_item = service_item self.setRowCount(0) - self.clear() - self.setColumnWidth(0, width) + self.clear_list() row = 0 text = [] for frame_number, frame in enumerate(self.service_item.get_frames()): diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index d4e7f8cfd..95076221a 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -30,6 +30,7 @@ from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus, \ check_item_selected +from openlp.core.lib.ui import create_widget_action from openlp.plugins.custom.forms.editcustomform import EditCustomForm from openlp.plugins.custom.lib import CustomXMLParser, CustomXMLBuilder from openlp.plugins.custom.lib.db import CustomSlide @@ -84,6 +85,12 @@ class CustomMediaItem(MediaManagerItem): Registry().register_function('custom_preview', self.on_preview_click) Registry().register_function('custom_create_from_service', self.create_from_service_item) + def add_custom_context_actions(self): + create_widget_action(self.list_view, separator=True) + create_widget_action( + self.list_view, text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=':/general/general_clone.png', + triggers=self.on_clone_click) + def config_update(self): """ Config has been updated so reload values @@ -243,6 +250,23 @@ class CustomMediaItem(MediaManagerItem): service_item.raw_footer.append('') return True + def on_clone_click(self): + """ + Clone the selected Custom item + """ + item = self.list_view.currentItem() + item_id = item.data(QtCore.Qt.UserRole) + old_custom_slide = self.plugin.db_manager.get_object(CustomSlide, item_id) + new_custom_slide = CustomSlide() + new_custom_slide.title = '{title} <{text}>'.format(title=old_custom_slide.title, + text=translate('CustomPlugin.MediaItem', + 'copy', 'For item cloning')) + new_custom_slide.text = old_custom_slide.text + new_custom_slide.credits = old_custom_slide.credits + new_custom_slide.theme_name = old_custom_slide.theme_name + self.plugin.db_manager.save_object(new_custom_slide) + self.on_search_text_button_clicked() + def on_search_text_button_clicked(self): """ Search the plugin database diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index bf78d8d55..fef160010 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -164,7 +164,7 @@ class Song(BaseModel): """ Add a Songbook Entry to the song if it not yet exists - :param songbook_name: Name of the Songbook. + :param songbook: Name of the Songbook. :param entry: Entry in the Songbook (usually a number) """ for songbook_entry in self.songbook_entries: diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index a99dd0a13..b3888fceb 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -572,10 +572,19 @@ class SongMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.OnLoadUpdate) service_item.add_capability(ItemCapabilities.AddIfNewItem) service_item.add_capability(ItemCapabilities.CanSoftBreak) + service_item.add_capability(ItemCapabilities.HasMetaData) song = self.plugin.manager.get_object(Song, item_id) service_item.theme = song.theme_name service_item.edit_id = item_id verse_list = SongXML().get_verses(song.lyrics) + if Settings().value('songs/add songbook slide') and song.songbook_entries: + first_slide = '\n' + for songbook_entry in song.songbook_entries: + first_slide = first_slide + '{book}/{num}/{pub}\n\n'.format(book=songbook_entry.songbook.name, + num=songbook_entry.entry, + pub=songbook_entry.songbook.publisher) + + service_item.add_from_text(first_slide, 'O1') # no verse list or only 1 space (in error) verse_tags_translated = False if VerseType.from_translated_string(str(verse_list[0][0]['type'])) is not None: @@ -622,6 +631,9 @@ class SongMediaItem(MediaManagerItem): if song.media_files: service_item.add_capability(ItemCapabilities.HasBackgroundAudio) service_item.background_audio = [m.file_path for m in song.media_files] + item.metadata.append('{label}: {media}'. + format(label=translate('SongsPlugin.MediaItem', 'Media'), + media=service_item.background_audio)) return True def generate_footer(self, item, song): @@ -685,6 +697,23 @@ class SongMediaItem(MediaManagerItem): if Settings().value('core/ccli number'): item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') + Settings().value('core/ccli number')) + item.metadata.append('{label}: {title}'.format(label=translate('SongsPlugin.MediaItem', 'Title'), + title=song.title)) + if song.alternate_title: + item.metadata.append('{label}: {title}'. + format(label=translate('SongsPlugin.MediaItem', 'Alt Title'), + title=song.alternate_title)) + if song.songbook_entries: + for songbook_entry in song.songbook_entries: + item.metadata.append('{label}: {book}/{num}/{pub}'. + format(label=translate('SongsPlugin.MediaItem', 'Songbook'), + book=songbook_entry.songbook.name, + num=songbook_entry.entry, + pub=songbook_entry.songbook.publisher)) + if song.topics: + for topics in song.topics: + item.metadata.append('{label}: {topic}'. + format(label=translate('SongsPlugin.MediaItem', 'Topic'), topic=topics.name)) return authors_all def service_load(self, item): diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index 173957b82..37335f16f 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -51,6 +51,9 @@ class SongsTab(SettingsTab): self.add_from_service_check_box = QtWidgets.QCheckBox(self.mode_group_box) self.add_from_service_check_box.setObjectName('add_from_service_check_box') self.mode_layout.addWidget(self.add_from_service_check_box) + self.songbook_slide_check_box = QtWidgets.QCheckBox(self.mode_group_box) + self.songbook_slide_check_box.setObjectName('songbook_slide_check_box') + self.mode_layout.addWidget(self.songbook_slide_check_box) self.display_songbook_check_box = QtWidgets.QCheckBox(self.mode_group_box) self.display_songbook_check_box.setObjectName('songbook_check_box') self.mode_layout.addWidget(self.display_songbook_check_box) @@ -95,6 +98,7 @@ class SongsTab(SettingsTab): self.tool_bar_active_check_box.stateChanged.connect(self.on_tool_bar_active_check_box_changed) self.update_on_edit_check_box.stateChanged.connect(self.on_update_on_edit_check_box_changed) self.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed) + self.songbook_slide_check_box.stateChanged.connect(self.on_songbook_slide_check_box_changed) self.display_songbook_check_box.stateChanged.connect(self.on_songbook_check_box_changed) self.display_written_by_check_box.stateChanged.connect(self.on_written_by_check_box_changed) self.display_copyright_check_box.stateChanged.connect(self.on_copyright_check_box_changed) @@ -111,6 +115,8 @@ class SongsTab(SettingsTab): self.update_on_edit_check_box.setText(translate('SongsPlugin.SongsTab', 'Update service from song edit')) self.add_from_service_check_box.setText(translate('SongsPlugin.SongsTab', 'Import missing songs from Service files')) + self.songbook_slide_check_box.setText(translate('SongsPlugin.SongsTab', + 'Add Songbooks as first side')) self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer')) self.display_written_by_check_box.setText(translate( 'SongsPlugin.SongsTab', 'Show "Written by:" in footer for unspecified authors')) @@ -141,6 +147,9 @@ class SongsTab(SettingsTab): def on_add_from_service_check_box_changed(self, check_state): self.update_load = (check_state == QtCore.Qt.Checked) + def on_songbook_slide_check_box_changed(self, check_state): + self.songbook_slide = (check_state == QtCore.Qt.Checked) + def on_songbook_check_box_changed(self, check_state): self.display_songbook = (check_state == QtCore.Qt.Checked) @@ -171,6 +180,7 @@ class SongsTab(SettingsTab): self.tool_bar = settings.value('display songbar') self.update_edit = settings.value('update service on edit') self.update_load = settings.value('add song from service') + self.songbook_slide = settings.value('add songbook slide') self.display_songbook = settings.value('display songbook') self.display_written_by = settings.value('display written by') self.display_copyright_symbol = settings.value('display copyright symbol') @@ -208,6 +218,7 @@ class SongsTab(SettingsTab): settings.setValue('mainview chords', self.mainview_chords) settings.setValue('disable chords import', self.disable_chords_import) settings.setValue('chord notation', self.chord_notation) + settings.setValue('add songbook slide', self.songbook_slide) settings.endGroup() if self.tab_visited: self.settings_form.register_post_process('songs_config_updated') diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index e8fff3f2a..31f239063 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -61,6 +61,7 @@ __default_settings__ = { 'songs/last import type': SongFormat.OpenLyrics, 'songs/update service on edit': False, 'songs/add song from service': True, + 'songs/add songbook slide': False, 'songs/display songbar': True, 'songs/display songbook': False, 'songs/display written by': True, diff --git a/scripts/websocket_client.py b/scripts/websocket_client.py index 03a208a6f..753825edf 100755 --- a/scripts/websocket_client.py +++ b/scripts/websocket_client.py @@ -25,6 +25,7 @@ import asyncio import websockets import random + async def tester(): async with websockets.connect('ws://localhost:4317/poll') as websocket: diff --git a/tests/functional/openlp_core/api/endpoint/test_controller.py b/tests/functional/openlp_core/api/endpoint/test_controller.py index 08b1f5d69..90aba6e03 100644 --- a/tests/functional/openlp_core/api/endpoint/test_controller.py +++ b/tests/functional/openlp_core/api/endpoint/test_controller.py @@ -20,10 +20,10 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### from unittest import TestCase -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock from openlp.core.common.registry import Registry -from openlp.core.api.endpoint.controller import controller_text +from openlp.core.api.endpoint.controller import controller_text, controller_direction class TestController(TestCase): @@ -42,7 +42,7 @@ class TestController(TestCase): def test_controller_text(self): """ - Remote Deploy tests - test the dummy zip file is processed correctly + Remote API Tests : test the controller text method can be called """ # GIVEN: A mocked service with a dummy service item self.mocked_live_controller.service_item = MagicMock() @@ -52,3 +52,25 @@ class TestController(TestCase): results = ret['results'] assert isinstance(results['item'], MagicMock) assert len(results['slides']) == 0 + + def test_controller_direction_next(self): + """ + Text the live next method is triggered + """ + # GIVEN: A mocked service with a dummy service item + self.mocked_live_controller.service_item = MagicMock() + # WHEN: I trigger the method + controller_direction(None, 'live', 'next') + # THEN: The correct method is called + self.mocked_live_controller.slidecontroller_live_next.emit.assert_called_once_with() + + def test_controller_direction_previous(self): + """ + Text the live next method is triggered + """ + # GIVEN: A mocked service with a dummy service item + self.mocked_live_controller.service_item = MagicMock() + # WHEN: I trigger the method + controller_direction(None, 'live', 'previous') + # THEN: The correct method is called + self.mocked_live_controller.slidecontroller_live_previous.emit.assert_called_once_with() diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 9af310e26..afe8b370b 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -431,14 +431,16 @@ class TestMediaItem(TestCase, TestMixin): # GIVEN: A Song and a Service Item song = Song() song.title = 'My Song' + song.alternate_title = '' song.copyright = 'My copyright' song.authors_songs = [] song.songbook_entries = [] song.ccli_number = '' + song.topics = None book1 = MagicMock() - book1.name = "My songbook" + book1.name = 'My songbook' book2 = MagicMock() - book2.name = "Thy songbook" + book2.name = 'Thy songbook' song.songbookentries = [] song.add_songbook_entry(book1, '12') song.add_songbook_entry(book2, '502A')