diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index e6f70b1e8..c527a4e20 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -251,8 +251,7 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication): if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'), translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n' 'a backup of the old data folder?'), - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: # Create copy of data folder data_folder_path = AppLocation.get_data_path() timestamp = time.strftime("%Y%m%d-%H%M%S") diff --git a/openlp/core/common/languagemanager.py b/openlp/core/common/languagemanager.py index 5023f7383..026a6ddda 100644 --- a/openlp/core/common/languagemanager.py +++ b/openlp/core/common/languagemanager.py @@ -140,8 +140,8 @@ class LanguageManager(object): reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm") if reg_ex.exactMatch(qmf): name = '{regex}'.format(regex=reg_ex.cap(1)) - # TODO: Test before converting to python3 string format - LanguageManager.__qm_list__['%#2i %s' % (counter + 1, LanguageManager.language_name(qmf))] = name + LanguageManager.__qm_list__[ + '{count:>2i} {name}'.format(count=counter + 1, name=LanguageManager.language_name(qmf))] = name @staticmethod def get_qm_list(): diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 39320ae96..7a1e31553 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -238,7 +238,11 @@ class Settings(QtCore.QSettings): ('shortcuts/offlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6. ('shortcuts/onlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6. ('bibles/advanced bible', '', []), # Common bible search widgets combined in 2.6 - ('bibles/quick bible', 'bibles/primary bible', []) # Common bible search widgets combined in 2.6 + ('bibles/quick bible', 'bibles/primary bible', []), # Common bible search widgets combined in 2.6 + # Last search type was renamed to last used search type in 2.6 since Bible search value type changed in 2.6. + ('songs/last search type', 'songs/last used search type', []), + ('bibles/last search type', '', []), + ('custom/last search type', 'custom/last used search type', []) ] @staticmethod diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py index a2481ab84..b818d3b31 100644 --- a/openlp/core/common/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -152,8 +152,6 @@ class UiStrings(object): self.Split = translate('OpenLP.Ui', 'Optional &Split') self.SplitToolTip = translate('OpenLP.Ui', 'Split a slide into two only if it does not fit on the screen as one slide.') - # TODO: WHERE is this used at? cannot find where it's used at in code. - self.StartTimeCode = translate('OpenLP.Ui', 'Start {code}') self.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop') self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End') self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular') diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 3e2187b77..6407f7a78 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -144,6 +144,7 @@ def upgrade_db(url, upgrade): :param url: The url of the database to upgrade. :param upgrade: The python module that contains the upgrade instructions. """ + log.debug('Checking upgrades for DB {db}'.format(db=url)) session, metadata = init_db(url) class Metadata(BaseModel): @@ -160,17 +161,15 @@ def upgrade_db(url, upgrade): metadata_table.create(checkfirst=True) mapper(Metadata, metadata_table) version_meta = session.query(Metadata).get('version') - if version_meta is None: - # Tables have just been created - fill the version field with the most recent version - if session.query(Metadata).get('dbversion'): - version = 0 - else: - version = upgrade.__version__ + if version_meta: + version = int(version_meta.value) + else: + # Due to issues with other checks, if the version is not set in the DB then default to 0 + # and let the upgrade function handle the checks + version = 0 version_meta = Metadata.populate(key='version', value=version) session.add(version_meta) session.commit() - else: - version = int(version_meta.value) if version > upgrade.__version__: session.remove() return version, upgrade.__version__ diff --git a/openlp/core/lib/exceptions.py b/openlp/core/lib/exceptions.py index 8e1645fae..26ff56a79 100644 --- a/openlp/core/lib/exceptions.py +++ b/openlp/core/lib/exceptions.py @@ -24,7 +24,6 @@ The :mod:`~openlp.core.lib.exceptions` module contains custom exceptions """ -# TODO: Test __init__ & __str__ class ValidationError(Exception): """ The :class:`~openlp.core.lib.exceptions.ValidationError` exception provides a custom exception for validating diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 4ddbaa401..f2071a190 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -110,6 +110,8 @@ class Image(object): :param width: The width of the image, defaults to -1 meaning that the screen width will be used. :param height: The height of the image, defaults to -1 meaning that the screen height will be used. """ + if not os.path.exists(path): + raise FileNotFoundError('{path} not found'.format(path=path)) self.path = path self.image = None self.image_bytes = None @@ -119,9 +121,7 @@ class Image(object): self.timestamp = 0 self.width = width self.height = height - # FIXME: We assume that the path exist. The caller has to take care that it exists! - if os.path.exists(path): - self.timestamp = os.stat(path).st_mtime + self.timestamp = os.stat(path).st_mtime self.secondary_priority = Image.secondary_priority Image.secondary_priority += 1 diff --git a/openlp/core/lib/projector/constants.py b/openlp/core/lib/projector/constants.py index 5177aa691..38331f500 100644 --- a/openlp/core/lib/projector/constants.py +++ b/openlp/core/lib/projector/constants.py @@ -57,35 +57,115 @@ LF = chr(0x0A) # \n PJLINK_PORT = 4352 TIMEOUT = 30.0 PJLINK_MAX_PACKET = 136 -# NOTE: Change format to account for some commands are both class 1 and 2 +# NOTE: Changed format to account for some commands are both class 1 and 2 PJLINK_VALID_CMD = { - 'ACKN': ['2', ], # UDP Reply to 'SRCH' - 'AVMT': ['1', ], # Shutter option - 'CLSS': ['1', ], # PJLink class support query - 'ERST': ['1', '2'], # Error status option - 'FILT': ['2', ], # Get current filter usage time - 'FREZ': ['2', ], # Set freeze/unfreeze picture being projected - 'INF1': ['1', ], # Manufacturer name query - 'INF2': ['1', ], # Product name query - 'INFO': ['1', ], # Other information query - 'INNM': ['2', ], # Get Video source input terminal name - 'INPT': ['1', ], # Video sources option - 'INST': ['1', ], # Input sources available query - 'IRES': ['2', ], # Get Video source resolution - 'LAMP': ['1', ], # Lamp(s) query (Includes fans) - 'LKUP': ['2', ], # UPD Linkup status notification - 'MVOL': ['2', ], # Set microphone volume - 'NAME': ['1', ], # Projector name query - 'PJLINK': ['1', ], # Initial connection - 'POWR': ['1', ], # Power option - 'RFIL': ['2', ], # Get replacement air filter model number - 'RLMP': ['2', ], # Get lamp replacement model number - 'RRES': ['2', ], # Get projector recommended video resolution - 'SNUM': ['2', ], # Get projector serial number - 'SRCH': ['2', ], # UDP broadcast search for available projectors on local network - 'SVER': ['2', ], # Get projector software version - 'SVOL': ['2', ] # Set speaker volume + 'ACKN': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Acknowledge a PJLink SRCH command - returns MAC address.') + }, + 'AVMT': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Blank/unblank video and/or mute audio.') + }, + 'CLSS': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector PJLink class support.') + }, + 'ERST': {'version': ['1', '2'], + 'description': translate('OpenLP.PJLinkConstants', + 'Query error status from projector. ' + 'Returns fan/lamp/temp/cover/filter/other error status.') + }, + 'FILT': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query number of hours on filter.') + }, + 'FREZ': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Freeze or unfreeze current image being projected.') + }, + 'INF1': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector manufacturer name.') + }, + 'INF2': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector product name.') + }, + 'INFO': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector for other information set by manufacturer.') + }, + 'INNM': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query specified input source name') + }, + 'INPT': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Switch to specified video source.') + }, + 'INST': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query available input sources.') + }, + 'IRES': {'version:': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query current input resolution.') + }, + 'LAMP': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query lamp time and on/off status. Multiple lamps supported.') + }, + 'LKUP': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'UDP Status notify. Includes MAC address.') + }, + 'MVOL': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Adjust microphone volume by 1 step.') + }, + 'NAME': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query customer-set projector name.') + }, + 'PJLINK': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Initial connection with authentication/no authentication request.') + }, + 'POWR': {'version': ['1', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Turn lamp on or off/standby.') + }, + 'RFIL': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query replacement air filter model number.') + }, + 'RLMP': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query replacement lamp model number.') + }, + 'RRES': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query recommended resolution.') + }, + 'SNUM': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector serial number.') + }, + 'SRCH': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'UDP broadcast search request for available projectors.') + }, + 'SVER': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Query projector software version number.') + }, + 'SVOL': {'version': ['2', ], + 'description': translate('OpenLP.PJLinkConstants', + 'Adjust speaker volume by 1 step.') + } } + # Error and status codes S_OK = E_OK = 0 # E_OK included since I sometimes forget # Error codes. Start at 200 so we don't duplicate system error codes. diff --git a/openlp/core/lib/projector/db.py b/openlp/core/lib/projector/db.py index 11b84b019..89b807f21 100644 --- a/openlp/core/lib/projector/db.py +++ b/openlp/core/lib/projector/db.py @@ -44,6 +44,7 @@ from sqlalchemy.orm import relationship from openlp.core.lib.db import Manager, init_db, init_url from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES +from openlp.core.lib.projector import upgrade Base = declarative_base(MetaData()) @@ -166,13 +167,14 @@ class Projector(CommonBase, Base): """ Return basic representation of Source table entry. """ - return '< Projector(id="{data}", ip="{ip}", port="{port}", pin="{pin}", name="{name}", ' \ + return '< Projector(id="{data}", ip="{ip}", port="{port}", mac_adx="{mac}", pin="{pin}", name="{name}", ' \ 'location="{location}", notes="{notes}", pjlink_name="{pjlink_name}", ' \ 'manufacturer="{manufacturer}", model="{model}", serial_no="{serial}", other="{other}", ' \ 'sources="{sources}", source_list="{source_list}", model_filter="{mfilter}", ' \ 'model_lamp="{mlamp}", sw_version="{sw_ver}") >'.format(data=self.id, ip=self.ip, port=self.port, + mac=self.mac_adx, pin=self.pin, name=self.name, location=self.location, @@ -189,6 +191,7 @@ class Projector(CommonBase, Base): sw_ver=self.sw_version) ip = Column(String(100)) port = Column(String(8)) + mac_adx = Column(String(18)) pin = Column(String(20)) name = Column(String(20)) location = Column(String(30)) @@ -243,7 +246,9 @@ class ProjectorDB(Manager): """ def __init__(self, *args, **kwargs): log.debug('ProjectorDB().__init__(args="{arg}", kwargs="{kwarg}")'.format(arg=args, kwarg=kwargs)) - super().__init__(plugin_name='projector', init_schema=self.init_schema) + super().__init__(plugin_name='projector', + init_schema=self.init_schema, + upgrade_mod=upgrade) log.debug('ProjectorDB() Initialized using db url {db}'.format(db=self.db_url)) log.debug('Session: {session}'.format(session=self.session)) diff --git a/openlp/core/lib/projector/pjlink1.py b/openlp/core/lib/projector/pjlink1.py index 2252c566c..dfc261f0a 100644 --- a/openlp/core/lib/projector/pjlink1.py +++ b/openlp/core/lib/projector/pjlink1.py @@ -129,7 +129,7 @@ class PJLink(QtNetwork.QTcpSocket): self.ip = ip self.port = port self.pin = pin - super(PJLink, self).__init__() + super().__init__() self.dbid = None self.location = None self.notes = None @@ -186,10 +186,15 @@ class PJLink(QtNetwork.QTcpSocket): self.pjlink_name = None self.manufacturer = None self.model = None + self.serial_no = None + self.sw_version = None self.shutter = None self.mute = None self.lamp = None + self.model_lamp = None self.fan = None + self.filter_time = None + self.model_filter = None self.source_available = None self.source = None self.other_info = None @@ -451,18 +456,18 @@ class PJLink(QtNetwork.QTcpSocket): return data_split = data.split('=') try: - (prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1]) + (prefix, version, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1]) except ValueError as e: log.warning('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip)) log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip())) self.change_status(E_INVALID_DATA) self.receive_data_signal() return - if not (cmd in PJLINK_VALID_CMD and class_ in PJLINK_VALID_CMD[cmd]): + if cmd not in PJLINK_VALID_CMD: log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd)) self.receive_data_signal() return - if int(self.pjlink_class) < int(class_): + if int(self.pjlink_class) < int(version): log.warn('({ip}) get_data(): Projector returned class reply higher ' 'than projector stated class'.format(ip=self.ip)) return self.process_command(cmd, data) @@ -507,14 +512,25 @@ class PJLink(QtNetwork.QTcpSocket): log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip)) self.send_queue = [] return + if cmd not in PJLINK_VALID_CMD: + log.error('({ip}) send_command(): Invalid command requested - ignoring.'.format(ip=self.ip)) + return self.projectorNetwork.emit(S_NETWORK_SENDING) log.debug('({ip}) send_command(): Building cmd="{command}" opts="{data}"{salt}'.format(ip=self.ip, command=cmd, data=opts, salt='' if salt is None else ' with hash')) - # TODO: Check for class of command rather than default to projector PJLink class - header = PJLINK_HEADER.format(linkclass=self.pjlink_class) + cmd_ver = PJLINK_VALID_CMD[cmd]['version'] + if self.pjlink_class in cmd_ver: + header = PJLINK_HEADER.format(linkclass=self.pjlink_class) + elif len(cmd_ver) == 1 and (int(cmd_ver[0]) < int(self.pjlink_class)): + # Typically a class 1 only command + header = PJLINK_HEADER.format(linkclass=cmd_ver[0]) + else: + # NOTE: Once we get to version 3 then think about looping + log.error('({ip}): send_command(): PJLink class check issue? aborting'.format(ip=self.ip)) + return out = '{salt}{header}{command} {options}{suffix}'.format(salt="" if salt is None else salt, header=header, command=cmd, @@ -589,10 +605,13 @@ class PJLink(QtNetwork.QTcpSocket): cmd=cmd, data=data)) # Check if we have a future command not available yet - if cmd in self.pjlink_future: - self._not_implemented(cmd) + if cmd not in PJLINK_VALID_CMD: + log.error('({ip}) Unknown command received - ignoring'.format(ip=self.ip)) return - if data in PJLINK_ERRORS: + elif cmd not in self.pjlink_functions: + log.warn('({ip}) Future command received - unable to process yet'.format(ip=self.ip)) + return + elif data in PJLINK_ERRORS: # Oops - projector error log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data)) if data.upper() == 'ERRA': @@ -624,14 +643,11 @@ class PJLink(QtNetwork.QTcpSocket): self.send_busy = False self.projectorReceivedData.emit() return - - if cmd in self.pjlink_functions: - log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd)) - self.pjlink_functions[cmd](data) - else: - log.warning('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd)) + # Command checks already passed + log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd)) self.send_busy = False self.projectorReceivedData.emit() + self.pjlink_functions[cmd](data) def process_lamp(self, data): """ diff --git a/openlp/core/lib/projector/upgrade.py b/openlp/core/lib/projector/upgrade.py new file mode 100644 index 000000000..4d2f4532e --- /dev/null +++ b/openlp/core/lib/projector/upgrade.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 OpenLP Developers # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`upgrade` module provides a way for the database and schema that is the +backend for the projector setup. +""" +import logging + +# Not all imports used at this time, but keep for future upgrades +from sqlalchemy import Column, types +from sqlalchemy.sql.expression import null + +from openlp.core.common.db import drop_columns +from openlp.core.lib.db import get_upgrade_op + +log = logging.getLogger(__name__) + +# Initial projector DB was unversioned +__version__ = 2 + +log.debug('Projector DB upgrade module loading') + + +def upgrade_1(session, metadata): + """ + Version 1 upgrade - old db might/might not be versioned. + """ + pass + + +def upgrade_2(session, metadata): + """ + Version 2 upgrade. + + Update Projector() table to include new data defined in PJLink version 2 changes + + serial_no: Column(String(30)) + sw_version: Column(String(30)) + model_filter: Column(String(30)) + model_lamp: Column(String(30)) + + :param session: DB session instance + :param metadata: Metadata of current DB + """ + + new_op = get_upgrade_op(session) + if 'serial_no' not in [t.name for t in metadata.tables.values()]: + log.debug("Upgrading projector DB to version '2'") + new_op.add_column('projector', Column('mac_adx', types.String(18), server_default=null())) + new_op.add_column('projector', Column('serial_no', types.String(30), server_default=null())) + new_op.add_column('projector', Column('sw_version', types.String(30), server_default=null())) + new_op.add_column('projector', Column('model_filter', types.String(30), server_default=null())) + new_op.add_column('projector', Column('model_lamp', types.String(30), server_default=null())) + else: + log.warn("Skipping upgrade_2 of projector DB") diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index 52baf0d2c..e2e70a934 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -105,7 +105,7 @@ class SearchEdit(QtWidgets.QLineEdit): self.setPlaceholderText(action.placeholder_text) self.menu_button.setDefaultAction(action) self._current_search_type = identifier - Settings().setValue('{section}/last search type'.format(section=self.settings_section), identifier) + Settings().setValue('{section}/last used search type'.format(section=self.settings_section), identifier) self.searchTypeChanged.emit(identifier) return True @@ -141,7 +141,7 @@ class SearchEdit(QtWidgets.QLineEdit): self.menu_button.resize(QtCore.QSize(28, 18)) self.menu_button.setMenu(menu) self.set_current_search_type( - Settings().value('{section}/last search type'.format(section=self.settings_section))) + Settings().value('{section}/last used search type'.format(section=self.settings_section))) self.menu_button.show() self._update_style_sheet() diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 8cd68384f..fe67c53ba 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -49,6 +49,7 @@ def add_welcome_page(parent, image): parent.title_label = QtWidgets.QLabel(parent.welcome_page) parent.title_label.setObjectName('title_label') parent.welcome_layout.addWidget(parent.title_label) + parent.title_label.setWordWrap(True) parent.welcome_layout.addSpacing(40) parent.information_label = QtWidgets.QLabel(parent.welcome_page) parent.information_label.setWordWrap(True) diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 1b71229b5..2b8383755 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -99,7 +99,7 @@ from .themelayoutform import ThemeLayoutForm from .themeform import ThemeForm from .filerenameform import FileRenameForm from .starttimeform import StartTimeForm -from .maindisplay import MainDisplay, Display +from .maindisplay import MainDisplay, Display, AudioPlayer from .servicenoteform import ServiceNoteForm from .serviceitemeditform import ServiceItemEditForm from .slidecontroller import SlideController, DisplayController, PreviewController, LiveController @@ -120,8 +120,8 @@ from .projector.tab import ProjectorTab from .projector.editform import ProjectorEditForm __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeForm', - 'ThemeManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', - 'Display', 'ServiceNoteForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', + 'ThemeManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'Display', 'AudioPlayer', + 'ServiceNoteForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', 'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm', 'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'SingleColumnTableWidget', 'ProjectorManager', 'ProjectorTab', 'ProjectorEditForm'] diff --git a/openlp/core/ui/aboutform.py b/openlp/core/ui/aboutform.py index f4f9b06a0..e1768b127 100644 --- a/openlp/core/ui/aboutform.py +++ b/openlp/core/ui/aboutform.py @@ -40,7 +40,8 @@ class AboutForm(QtWidgets.QDialog, UiAboutDialog): """ Do some initialisation stuff """ - super(AboutForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(AboutForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self._setup() def _setup(self): diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index d3955fb63..8d64bfecc 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -495,9 +495,7 @@ class AdvancedTab(SettingsTab): 'location of the OpenLP data directory to:\n\n{path}' '\n\nThe data directory will be changed when OpenLP is ' 'closed.').format(path=new_data_path), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.No) + defaultButton=QtWidgets.QMessageBox.No) if answer != QtWidgets.QMessageBox.Yes: self.data_directory_path_edit.path = AppLocation.get_data_path() return diff --git a/openlp/core/ui/filerenameform.py b/openlp/core/ui/filerenameform.py index 0fb9c8668..2314fda43 100644 --- a/openlp/core/ui/filerenameform.py +++ b/openlp/core/ui/filerenameform.py @@ -38,8 +38,8 @@ class FileRenameForm(QtWidgets.QDialog, Ui_FileRenameDialog, RegistryProperties) """ Constructor """ - super(FileRenameForm, self).__init__(Registry().get('main_window'), - QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(FileRenameForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint | + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self._setup() def _setup(self): diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index cc7668587..2a9682c36 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -206,7 +206,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): trace_error_handler(log) self.update_screen_list_combo() self.application.process_events() - # TODO: Tested at home self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading {name}...') if self.has_run_wizard: self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active()) @@ -561,7 +560,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): item = self.songs_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename, sha256 = item.data(QtCore.Qt.UserRole) - # TODO: Tested at home self._increment_progress_bar(self.downloading.format(name=filename), 0) self.previous_size = 0 destination = os.path.join(songs_destination, str(filename)) @@ -574,7 +572,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): item = bibles_iterator.value() if item.parent() and item.checkState(0) == QtCore.Qt.Checked: bible, sha256 = item.data(0, QtCore.Qt.UserRole) - # TODO: Tested at home self._increment_progress_bar(self.downloading.format(name=bible), 0) self.previous_size = 0 if not url_get_file(self, '{path}{name}'.format(path=self.bibles_url, name=bible), @@ -587,7 +584,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): item = self.themes_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: theme, sha256 = item.data(QtCore.Qt.UserRole) - # TODO: Tested at home self._increment_progress_bar(self.downloading.format(name=theme), 0) self.previous_size = 0 if not url_get_file(self, '{path}{name}'.format(path=self.themes_url, name=theme), diff --git a/openlp/core/ui/firsttimelanguageform.py b/openlp/core/ui/firsttimelanguageform.py index 9e9d664ca..bfe593b83 100644 --- a/openlp/core/ui/firsttimelanguageform.py +++ b/openlp/core/ui/firsttimelanguageform.py @@ -37,7 +37,8 @@ class FirstTimeLanguageForm(QtWidgets.QDialog, Ui_FirstTimeLanguageDialog): """ Constructor """ - super(FirstTimeLanguageForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(FirstTimeLanguageForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self.qm_list = LanguageManager.get_qm_list() self.language_combo_box.addItem('Autodetect') diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 62e1833d4..062eb21fc 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -130,8 +130,7 @@ class FormattingTagController(object): elif not match.group('empty'): end_tags.append(tag) match = self.html_tag_regex.search(start_html, match.end()) - # TODO: Verify format() works with lambda - return ''.join(map(lambda tag: '' % tag, reversed(end_tags))) + return ''.join(map(lambda tag: ''.format(tag=tag), reversed(end_tags))) def start_tag_changed(self, start_html, end_html): """ diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index 1aeda9bc3..a4c32440a 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -51,7 +51,8 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag """ Constructor """ - super(FormattingTagForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(FormattingTagForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self._setup() @@ -122,8 +123,7 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag self.tag_table_widget.item(count, 2).text(), self.tag_table_widget.item(count, 3).text()) if error: - QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), error, - QtWidgets.QMessageBox.Ok) + QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), error) self.tag_table_widget.selectRow(count) return count += 1 @@ -198,6 +198,5 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag if tag: self.tag_table_widget.setItem(pre_row, 3, QtWidgets.QTableWidgetItem(tag)) if errors: - QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), errors, - QtWidgets.QMessageBox.Ok) + QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), errors) self.tag_table_widget.resizeRowsToContents() diff --git a/openlp/core/ui/lib/pathedit.py b/openlp/core/ui/lib/pathedit.py index c489daa33..9c66478b2 100755 --- a/openlp/core/ui/lib/pathedit.py +++ b/openlp/core/ui/lib/pathedit.py @@ -48,7 +48,7 @@ class PathEdit(QtWidgets.QWidget): :type parent: QWidget or None :param dialog_caption: Used to customise the caption in the QFileDialog. - :param dialog_caption: str + :type dialog_caption: str :param default_path: The default path. This is set as the path when the revert button is clicked :type default_path: str diff --git a/openlp/core/ui/lib/wizard.py b/openlp/core/ui/lib/wizard.py index 088bc3c1f..936682585 100644 --- a/openlp/core/ui/lib/wizard.py +++ b/openlp/core/ui/lib/wizard.py @@ -25,7 +25,7 @@ The :mod:``wizard`` module provides generic wizard tools for OpenLP. import logging import os -from PyQt5 import QtGui, QtWidgets +from PyQt5 import QtCore, QtGui, QtWidgets from openlp.core.common import Registry, RegistryProperties, Settings, UiStrings, translate, is_macosx from openlp.core.lib import build_icon @@ -50,13 +50,13 @@ class WizardStrings(object): # These strings should need a good reason to be retranslated elsewhere. FinishedImport = translate('OpenLP.Ui', 'Finished import.') FormatLabel = translate('OpenLP.Ui', 'Format:') - HeaderStyle = '%s' + HeaderStyle = '{text}' Importing = translate('OpenLP.Ui', 'Importing') - ImportingType = translate('OpenLP.Ui', 'Importing "%s"...') + ImportingType = translate('OpenLP.Ui', 'Importing "{source}"...') ImportSelect = translate('OpenLP.Ui', 'Select Import Source') ImportSelectLong = translate('OpenLP.Ui', 'Select the import format and the location to import from.') - OpenTypeFile = translate('OpenLP.Ui', 'Open %s File') - OpenTypeFolder = translate('OpenLP.Ui', 'Open %s Folder') + OpenTypeFile = translate('OpenLP.Ui', 'Open {file_type} File') + OpenTypeFolder = translate('OpenLP.Ui', 'Open {folder_name} Folder') PercentSymbolFormat = translate('OpenLP.Ui', '%p%') Ready = translate('OpenLP.Ui', 'Ready.') StartingImport = translate('OpenLP.Ui', 'Starting import...') @@ -93,7 +93,10 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties): """ Constructor """ - super(OpenLPWizard, self).__init__(parent) + # QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint remove the "?" buttons from windows, + # QtCore.Qt.WindowCloseButtonHint enables the "x" button to close these windows. + super(OpenLPWizard, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.plugin = plugin self.with_progress_page = add_progress_page self.setFixedWidth(640) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index a40ade826..23151395c 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -689,7 +689,7 @@ class AudioPlayer(OpenLPMixin, QtCore.QObject): """ Skip forward to the next track in the list """ - self.playerlist.next() + self.playlist.next() def go_to(self, index): """ diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 79c19adf9..8299fc985 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -926,8 +926,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties): QtWidgets.QMessageBox.information(self, translate('OpenLP.MainWindow', 'Import settings'), translate('OpenLP.MainWindow', 'OpenLP will now close. Imported settings will ' - 'be applied the next time you start OpenLP.'), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok)) + 'be applied the next time you start OpenLP.')) self.settings_imported = True self.clean_up() QtCore.QCoreApplication.exit() @@ -1322,7 +1321,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties): self.recent_files_menu.clear() for file_id, filename in enumerate(recent_files_to_display): log.debug('Recent file name: {name}'.format(name=filename)) - # TODO: Should be good action = create_action(self, '', text='&{n} {name}'.format(n=file_id + 1, name=os.path.splitext(os.path.basename(str(filename)))[0]), diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index e84fbee63..f0fb47cc6 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -468,9 +468,10 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): player = self.media_players[used_players[0]] if suffix not in player.video_extensions_list and suffix not in player.audio_extensions_list: # Media could not be loaded correctly - critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported Media File'), - translate('MediaPlugin.MediaItem', 'File %s not supported using player %s') % - (service_item.get_frame_path(), used_players[0])) + critical_error_message_box( + translate('MediaPlugin.MediaItem', 'Unsupported Media File'), + translate('MediaPlugin.MediaItem', 'File {file_path} not supported using player {player_name}' + ).format(file_path=service_item.get_frame_path(), player_name=used_players[0])) return False media_data = MediaInfoWrapper.parse(service_item.get_frame_path()) # duration returns in milli seconds diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 69b96a7ac..0299683a5 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -41,7 +41,8 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): """ Constructor """ - super(PluginForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(PluginForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.active_plugin = None self.programatic_change = False self.setupUi(self) @@ -60,7 +61,6 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): self._clear_details() self.programatic_change = True plugin_list_width = 0 - # TODO: Tested at home for plugin in self.plugin_manager.plugins: item = QtWidgets.QListWidgetItem(self.plugin_list_widget) # We do this just to make 100% sure the status is an integer as @@ -137,7 +137,6 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties): self.active_plugin.app_startup() else: self.active_plugin.toggle_status(PluginStatus.Inactive) - # TODO: Tested at home status_text = translate('OpenLP.PluginForm', '{name} (Inactive)') if self.active_plugin.status == PluginStatus.Active: status_text = translate('OpenLP.PluginForm', '{name} (Active)') diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 7b3d80c8b..5a26a001d 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -125,8 +125,8 @@ class PrintServiceForm(QtWidgets.QDialog, Ui_PrintServiceDialog, RegistryPropert """ Constructor """ - super(PrintServiceForm, self).__init__(Registry().get('main_window'), - QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(PrintServiceForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint | + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.printer = QtPrintSupport.QPrinter() self.print_dialog = QtPrintSupport.QPrintDialog(self.printer, self) self.document = QtGui.QTextDocument() diff --git a/openlp/core/ui/projector/editform.py b/openlp/core/ui/projector/editform.py index eec7a19de..6a6f0a362 100644 --- a/openlp/core/ui/projector/editform.py +++ b/openlp/core/ui/projector/editform.py @@ -142,7 +142,8 @@ class ProjectorEditForm(QtWidgets.QDialog, Ui_ProjectorEditForm): editProjector = QtCore.pyqtSignal(object) def __init__(self, parent=None, projectordb=None): - super(ProjectorEditForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(ProjectorEditForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.projectordb = projectordb self.setupUi(self) self.button_box.accepted.connect(self.accept_me) diff --git a/openlp/core/ui/projector/manager.py b/openlp/core/ui/projector/manager.py index 747a63a85..e47b3c1f9 100644 --- a/openlp/core/ui/projector/manager.py +++ b/openlp/core/ui/projector/manager.py @@ -662,6 +662,20 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjecto message = '%s%s: %s
' % (message, translate('OpenLP.ProjectorManager', 'Current source input is'), projector.link.source) + if projector.link.pjlink_class == '2': + # Information only available for PJLink Class 2 projectors + message += '{title}: {data}

'.format(title=translate('OpenLP.ProjectorManager', + 'Serial Number'), + data=projector.serial_no) + message += '{title}: {data}

'.format(title=translate('OpenLP.ProjectorManager', + 'Software Version'), + data=projector.sw_version) + message += '{title}: {data}

'.format(title=translate('OpenLP.ProjectorManager', + 'Lamp type'), + data=projector.model_lamp) + message += '{title}: {data}

'.format(title=translate('OpenLP.ProjectorManager', + 'Filter type'), + data=projector.model_filter) count = 1 for item in projector.link.lamp: message += '{title} {count} {status} '.format(title=translate('OpenLP.ProjectorManager', diff --git a/openlp/core/ui/projector/sourceselectform.py b/openlp/core/ui/projector/sourceselectform.py index c9fe4e1f1..1d17c07cd 100644 --- a/openlp/core/ui/projector/sourceselectform.py +++ b/openlp/core/ui/projector/sourceselectform.py @@ -233,7 +233,8 @@ class SourceSelectTabs(QtWidgets.QDialog): :param projectordb: ProjectorDB session to use """ log.debug('Initializing SourceSelectTabs()') - super(SourceSelectTabs, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(SourceSelectTabs, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setMinimumWidth(350) self.projectordb = projectordb self.edit = edit @@ -388,7 +389,8 @@ class SourceSelectSingle(QtWidgets.QDialog): """ log.debug('Initializing SourceSelectSingle()') self.projectordb = projectordb - super(SourceSelectSingle, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(SourceSelectSingle, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.edit = edit if self.edit: title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text') diff --git a/openlp/core/ui/serviceitemeditform.py b/openlp/core/ui/serviceitemeditform.py index dead6160a..f23956e70 100644 --- a/openlp/core/ui/serviceitemeditform.py +++ b/openlp/core/ui/serviceitemeditform.py @@ -37,8 +37,8 @@ class ServiceItemEditForm(QtWidgets.QDialog, Ui_ServiceItemEditDialog, RegistryP """ Constructor """ - super(ServiceItemEditForm, self).__init__(Registry().get('main_window'), - QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(ServiceItemEditForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint | + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self.item_list = [] self.list_widget.currentRowChanged.connect(self.on_current_row_changed) diff --git a/openlp/core/ui/servicenoteform.py b/openlp/core/ui/servicenoteform.py index dd1f42674..fc4da438a 100644 --- a/openlp/core/ui/servicenoteform.py +++ b/openlp/core/ui/servicenoteform.py @@ -37,8 +37,8 @@ class ServiceNoteForm(QtWidgets.QDialog, RegistryProperties): """ Constructor """ - super(ServiceNoteForm, self).__init__(Registry().get('main_window'), - QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(ServiceNoteForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint | + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.setupUi() self.retranslateUi() diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 2985883d4..8663ad018 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -47,7 +47,8 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties): """ Registry().register('settings_form', self) Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up) - super(SettingsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(SettingsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.processes = [] self.setupUi(self) self.setting_list_widget.currentRowChanged.connect(self.list_item_changed) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index a42600193..fc9a77ac8 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -44,7 +44,8 @@ class ShortcutListForm(QtWidgets.QDialog, Ui_ShortcutListDialog, RegistryPropert """ Constructor """ - super(ShortcutListForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(ShortcutListForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self.changed_actions = {} self.action_list = ActionList.get_instance() @@ -279,9 +280,7 @@ class ShortcutListForm(QtWidgets.QDialog, Ui_ShortcutListDialog, RegistryPropert return if QtWidgets.QMessageBox.question(self, translate('OpenLP.ShortcutListDialog', 'Restore Default Shortcuts'), translate('OpenLP.ShortcutListDialog', 'Do you want to restore all ' - 'shortcuts to their defaults?'), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No) + 'shortcuts to their defaults?') ) == QtWidgets.QMessageBox.No: return self._adjust_button(self.primary_push_button, False, text='') diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index 1b453a9c2..ae82d4c9e 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -38,8 +38,8 @@ class StartTimeForm(QtWidgets.QDialog, Ui_StartTimeDialog, RegistryProperties): """ Constructor """ - super(StartTimeForm, self).__init__(Registry().get('main_window'), - QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(StartTimeForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint | + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) def exec(self): diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 59576808b..969d676a8 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -257,10 +257,9 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage Renames an existing theme to a new name :param field: """ - # TODO: Check for delayed format() conversions if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'), translate('OpenLP.ThemeManager', 'Rename Confirmation'), - translate('OpenLP.ThemeManager', 'Rename %s theme?'), False, False): + translate('OpenLP.ThemeManager', 'Rename {theme_name} theme?'), False, False): item = self.theme_list_widget.currentItem() old_theme_name = item.data(QtCore.Qt.UserRole) self.file_rename_form.file_name_edit.setText(old_theme_name) @@ -334,10 +333,9 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage Delete a theme triggered by the UI. :param field: """ - # TODO: Verify delayed format() conversions if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to delete.'), translate('OpenLP.ThemeManager', 'Delete Confirmation'), - translate('OpenLP.ThemeManager', 'Delete %s theme?')): + translate('OpenLP.ThemeManager', 'Delete {theme_name} theme?')): item = self.theme_list_widget.currentItem() theme = item.text() row = self.theme_list_widget.row(item) @@ -539,9 +537,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage translate('OpenLP.ThemeManager', 'Theme {name} already exists. ' 'Do you want to replace it?').format(name=theme_name), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.No) + defaultButton=QtWidgets.QMessageBox.No) return ret == QtWidgets.QMessageBox.Yes def unzip_theme(self, file_name, directory): @@ -785,9 +781,8 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage # confirm deletion if confirm: answer = QtWidgets.QMessageBox.question( - self, confirm_title, confirm_text % theme, - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.No) + self, confirm_title, confirm_text.format(theme_name=theme), + defaultButton=QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.No: return False # should be the same unless default diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index f944fcd43..878540dbf 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -90,21 +90,20 @@ JAVASCRIPT = """ } } """ -# TODO: Verify format() with variable templates CSS = """ - #alert { + #alert {{ position: absolute; left: 0px; top: 0px; z-index: 10; - width: 100%%; - vertical-align: %s; - font-family: %s; - font-size: %spt; - color: %s; - background-color: %s; + width: 100%; + vertical-align: {vertical_align}; + font-family: {font_family}; + font-size: {font_size:d}pt; + color: {color}; + background-color: {background_color}; word-wrap: break-word; - } + }} """ HTML = """ @@ -232,8 +231,11 @@ class AlertsPlugin(Plugin): Add CSS to the main display. """ align = VerticalType.Names[self.settings_tab.location] - return CSS % (align, self.settings_tab.font_face, self.settings_tab.font_size, self.settings_tab.font_color, - self.settings_tab.background_color) + return CSS.format(vertical_align=align, + font_family=self.settings_tab.font_face, + font_size=self.settings_tab.font_size, + color=self.settings_tab.font_color, + background_color=self.settings_tab.background_color) @staticmethod def get_display_html(): diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index 88f44210e..a9cb6cac3 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -36,8 +36,8 @@ class AlertForm(QtWidgets.QDialog, Ui_AlertDialog): """ Initialise the alert form """ - super(AlertForm, self).__init__(Registry().get('main_window'), - QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(AlertForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint | + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.manager = plugin.manager self.plugin = plugin self.item_id = None @@ -180,9 +180,7 @@ class AlertForm(QtWidgets.QDialog, Ui_AlertDialog): translate('AlertsPlugin.AlertForm', 'No Parameter Found'), translate('AlertsPlugin.AlertForm', 'You have not entered a parameter to be replaced.\n' - 'Do you want to continue anyway?'), - QtWidgets.QMessageBox.StandardButtons( - QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes) + 'Do you want to continue anyway?') ) == QtWidgets.QMessageBox.No: self.parameter_edit.setFocus() return False @@ -193,9 +191,7 @@ class AlertForm(QtWidgets.QDialog, Ui_AlertDialog): translate('AlertsPlugin.AlertForm', 'No Placeholder Found'), translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n' - 'Do you want to continue anyway?'), - QtWidgets.QMessageBox.StandardButtons( - QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes) + 'Do you want to continue anyway?') ) == QtWidgets.QMessageBox.No: self.parameter_edit.setFocus() return False diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 1bcf8ff5d..4bcdab563 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -40,7 +40,7 @@ __default_settings__ = { 'bibles/db password': '', 'bibles/db hostname': '', 'bibles/db database': '', - 'bibles/last search type': BibleSearch.Combined, + 'bibles/last used search type': BibleSearch.Combined, 'bibles/reset to combined quick search': True, 'bibles/verse layout style': LayoutStyle.VersePerSlide, 'bibles/book name language': LanguageSelection.Bible, diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 0a0de0b24..a6a2a4299 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -421,8 +421,8 @@ class BibleImportForm(OpenLPWizard): Allow for localisation of the bible import wizard. """ self.setWindowTitle(translate('BiblesPlugin.ImportWizardForm', 'Bible Import Wizard')) - self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', - 'Welcome to the Bible Import Wizard')) + self.title_label.setText(WizardStrings.HeaderStyle.format(text=translate('OpenLP.Ui', + 'Welcome to the Bible Import Wizard'))) self.information_label.setText( translate('BiblesPlugin.ImportWizardForm', 'This wizard will help you to import Bibles from a variety of ' diff --git a/openlp/plugins/bibles/forms/booknameform.py b/openlp/plugins/bibles/forms/booknameform.py index 0a2411131..588ba96d4 100644 --- a/openlp/plugins/bibles/forms/booknameform.py +++ b/openlp/plugins/bibles/forms/booknameform.py @@ -49,7 +49,8 @@ class BookNameForm(QDialog, Ui_BookNameDialog): """ Constructor """ - super(BookNameForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(BookNameForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self.custom_signals() self.book_names = BibleStrings().BookNames diff --git a/openlp/plugins/bibles/forms/editbibleform.py b/openlp/plugins/bibles/forms/editbibleform.py index c0570e772..543531c2d 100644 --- a/openlp/plugins/bibles/forms/editbibleform.py +++ b/openlp/plugins/bibles/forms/editbibleform.py @@ -45,7 +45,8 @@ class EditBibleForm(QtWidgets.QDialog, Ui_EditBibleDialog, RegistryProperties): """ Constructor """ - super(EditBibleForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(EditBibleForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.media_item = media_item self.book_names = BibleStrings().BookNames self.setupUi(self) diff --git a/openlp/plugins/bibles/forms/languageform.py b/openlp/plugins/bibles/forms/languageform.py index 46571e262..6494950c8 100644 --- a/openlp/plugins/bibles/forms/languageform.py +++ b/openlp/plugins/bibles/forms/languageform.py @@ -47,7 +47,8 @@ class LanguageForm(QDialog, Ui_LanguageDialog): """ Constructor """ - super(LanguageForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(LanguageForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) def exec(self, bible_name): diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index e4025411c..9d90a94e2 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -221,18 +221,16 @@ def update_reference_separators(): REFERENCE_SEPARATORS['sep_{role}'.format(role=role)] = '\s*(?:{source})\s*'.format(source=source_string) REFERENCE_SEPARATORS['sep_{role}_default'.format(role=role)] = default_separators[index] # verse range match: (:)?(-((:)?|end)?)? - # TODO: Check before converting this string - range_regex = '(?:(?P[0-9]+)%(sep_v)s)?' \ - '(?P[0-9]+)(?P%(sep_r)s(?:(?:(?P' \ - '[0-9]+)%(sep_v)s)?(?P[0-9]+)|%(sep_e)s)?)?' % REFERENCE_SEPARATORS - # TODO: Test before converting re.compile strings - REFERENCE_MATCHES['range'] = re.compile('^\s*%s\s*$' % range_regex, re.UNICODE) + range_regex = '(?:(?P[0-9]+){sep_v})?' \ + '(?P[0-9]+)(?P{sep_r}(?:(?:(?P' \ + '[0-9]+){sep_v})?(?P[0-9]+)|{sep_e})?)?'.format_map(REFERENCE_SEPARATORS) + REFERENCE_MATCHES['range'] = re.compile(r'^\s*{range}\s*$'.format(range=range_regex), re.UNICODE) REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE) # full reference match: ((,(?!$)|(?=$)))+ REFERENCE_MATCHES['full'] = \ - re.compile('^\s*(?!\s)(?P[\d]*[^\d\.]+)\.*(?(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$' - % dict(list(REFERENCE_SEPARATORS.items()) + [('range_regex', range_regex)]), re.UNICODE) + re.compile(r'^\s*(?!\s)(?P[\d]*[.]?[^\d\.]+)\.*(?(?:{range_regex}(?:{sep_l}(?!\s*$)|(?=\s*$)))+)\s*$'.format( + range_regex=range_regex, sep_l=REFERENCE_SEPARATORS['sep_l']), re.UNICODE) def get_reference_separator(separator_type): @@ -326,7 +324,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): ``^\s*(?!\s)(?P[\d]*[^\d]+)(?(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$`` The second group contains all ``ranges``. This can be multiple declarations of range_regex separated by a list diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 9afdd2efc..65460c92e 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -306,9 +306,8 @@ class BibleDB(Manager): book_escaped = book for character in RESERVED_CHARACTERS: book_escaped = book_escaped.replace(character, '\\' + character) - # TODO: Verify regex patters before using format() - regex_book = re.compile('\s*%s\s*' % '\s*'.join( - book_escaped.split()), re.UNICODE | re.IGNORECASE) + regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())), + re.UNICODE | re.IGNORECASE) if language_selection == LanguageSelection.Bible: db_book = self.get_book(book) if db_book: diff --git a/openlp/plugins/bibles/lib/importers/http.py b/openlp/plugins/bibles/lib/importers/http.py index 76daa8e25..4f032784d 100644 --- a/openlp/plugins/bibles/lib/importers/http.py +++ b/openlp/plugins/bibles/lib/importers/http.py @@ -90,6 +90,8 @@ class BGExtract(RegistryProperties): """ Extract verses from BibleGateway """ + NAME = 'BibleGateway' + def __init__(self, proxy_url=None): log.debug('BGExtract.init("{url}")'.format(url=proxy_url)) self.proxy_url = proxy_url @@ -357,6 +359,8 @@ class BSExtract(RegistryProperties): """ Extract verses from Bibleserver.com """ + NAME = 'BibleServer' + def __init__(self, proxy_url=None): log.debug('BSExtract.init("{url}")'.format(url=proxy_url)) self.proxy_url = proxy_url @@ -458,6 +462,8 @@ class CWExtract(RegistryProperties): """ Extract verses from CrossWalk/BibleStudyTools """ + NAME = 'Crosswalk' + def __init__(self, proxy_url=None): log.debug('CWExtract.init("{url}")'.format(url=proxy_url)) self.proxy_url = proxy_url diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index f0ef34dd6..1c210c9ed 100755 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -414,7 +414,9 @@ class BibleMediaItem(MediaManagerItem): if self.bible: book_data = self.get_common_books(self.bible, self.second_bible) language_selection = self.plugin.manager.get_language_selection(self.bible.name) - books = [book.get_name(language_selection) for book in book_data] + # Get book names + add a space to the end. Thus Psalm23 becomes Psalm 23 + # when auto complete is used and user does not need to add the space manually. + books = [book.get_name(language_selection) + ' ' for book in book_data] books.sort(key=get_locale_key) set_case_insensitive_completer(books, self.search_edit) diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index a081f8623..99eda1d52 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -42,7 +42,7 @@ __default_settings__ = { 'custom/db password': '', 'custom/db hostname': '', 'custom/db database': '', - 'custom/last search type': CustomSearch.Titles, + 'custom/last used search type': CustomSearch.Titles, 'custom/display footer': True, 'custom/add custom from service': True } diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 993acc688..1bd2187f2 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -44,7 +44,8 @@ class EditCustomForm(QtWidgets.QDialog, Ui_CustomEditDialog): """ Constructor """ - super(EditCustomForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(EditCustomForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.manager = manager self.media_item = media_item self.setupUi(self) diff --git a/openlp/plugins/custom/forms/editcustomslideform.py b/openlp/plugins/custom/forms/editcustomslideform.py index b3c77e859..77f086dbb 100644 --- a/openlp/plugins/custom/forms/editcustomslideform.py +++ b/openlp/plugins/custom/forms/editcustomslideform.py @@ -39,7 +39,8 @@ class EditCustomSlideForm(QtWidgets.QDialog, Ui_CustomSlideEditDialog): """ Constructor """ - super(EditCustomSlideForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(EditCustomSlideForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) # Connecting signals and slots self.insert_button.clicked.connect(self.on_insert_button_clicked) diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index c9a8aa046..c90d14e10 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -190,9 +190,7 @@ class CustomMediaItem(MediaManagerItem): translate('CustomPlugin.MediaItem', 'Are you sure you want to delete the "{items:d}" ' 'selected custom slide(s)?').format(items=len(items)), - QtWidgets.QMessageBox.StandardButtons( - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: return row_list = [item.row() for item in self.list_view.selectedIndexes()] row_list.sort(reverse=True) diff --git a/openlp/plugins/images/forms/addgroupform.py b/openlp/plugins/images/forms/addgroupform.py index 3ea91c29e..953d02339 100644 --- a/openlp/plugins/images/forms/addgroupform.py +++ b/openlp/plugins/images/forms/addgroupform.py @@ -35,7 +35,8 @@ class AddGroupForm(QtWidgets.QDialog, Ui_AddGroupDialog): """ Constructor """ - super(AddGroupForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(AddGroupForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) def exec(self, clear=True, show_top_level_group=False, selected_group=None): diff --git a/openlp/plugins/images/forms/choosegroupform.py b/openlp/plugins/images/forms/choosegroupform.py index 88f4e72de..5867481b9 100644 --- a/openlp/plugins/images/forms/choosegroupform.py +++ b/openlp/plugins/images/forms/choosegroupform.py @@ -33,7 +33,8 @@ class ChooseGroupForm(QtWidgets.QDialog, Ui_ChooseGroupDialog): """ Constructor """ - super(ChooseGroupForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(ChooseGroupForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) def exec(self, selected_group=None): diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index e3f39143b..97a9dd956 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -246,9 +246,7 @@ class ImageMediaItem(MediaManagerItem): translate('ImagePlugin.MediaItem', 'Remove group'), translate('ImagePlugin.MediaItem', 'Are you sure you want to remove "{name}" and everything in it?' - ).format(name=item_data.group_name), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No) + ).format(name=item_data.group_name) ) == QtWidgets.QMessageBox.Yes: self.recursively_delete_group(item_data) self.manager.delete_object(ImageGroups, row_item.data(0, QtCore.Qt.UserRole).id) @@ -597,8 +595,7 @@ class ImageMediaItem(MediaManagerItem): self, translate('ImagePlugin.MediaItem', 'Missing Image(s)'), translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: {names}\n' 'Do you want to add the other images anyway?' - ).format(names='\n'.join(missing_items_file_names)), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes)) == \ + ).format(names='\n'.join(missing_items_file_names))) == \ QtWidgets.QMessageBox.No: return False # Continue with the existing images. diff --git a/openlp/plugins/media/forms/mediaclipselectorform.py b/openlp/plugins/media/forms/mediaclipselectorform.py index c02e79232..d6e6fd352 100644 --- a/openlp/plugins/media/forms/mediaclipselectorform.py +++ b/openlp/plugins/media/forms/mediaclipselectorform.py @@ -52,7 +52,8 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro """ Constructor """ - super(MediaClipSelectorForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(MediaClipSelectorForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.vlc_instance = None self.vlc_media_player = None self.vlc_media = None diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index d36db36f0..d59fd83a4 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -253,15 +253,14 @@ class PdfDocument(PresentationDocument): try: if not os.path.isdir(self.get_temp_folder()): os.makedirs(self.get_temp_folder()) + # The %03d in the file name is handled by each binary if self.controller.mudrawbin: log.debug('loading presentation using mudraw') - # TODO: Find out where the string conversion actually happens runlog = check_output([self.controller.mudrawbin, '-w', str(size.width()), '-h', str(size.height()), '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path], startupinfo=self.startupinfo) elif self.controller.mutoolbin: log.debug('loading presentation using mutool') - # TODO: Find out where the string convertsion actually happens runlog = check_output([self.controller.mutoolbin, 'draw', '-w', str(size.width()), '-h', str(size.height()), '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path], @@ -269,7 +268,6 @@ class PdfDocument(PresentationDocument): elif self.controller.gsbin: log.debug('loading presentation using gs') resolution = self.gs_get_resolution(size) - # TODO: Find out where the string conversion actually happens runlog = check_output([self.controller.gsbin, '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m', '-r' + str(resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4', '-sOutputFile=' + os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 0ee165deb..09ab940e7 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -81,7 +81,7 @@ class PowerpointController(PresentationController): if app_version >= 12: self.also_supports = ['odp'] except (OSError, ValueError): - log.warning('Detection of powerpoint version using registry failed.') + log.exception('Detection of powerpoint version using registry failed.') return True except OSError: pass @@ -109,9 +109,8 @@ class PowerpointController(PresentationController): if self.process.Presentations.Count > 0: return self.process.Quit() - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Exception caught while killing powerpoint process') - log.exception(e) trace_error_handler(log) self.process = None @@ -154,9 +153,8 @@ class PowerpointDocument(PresentationDocument): if len(ScreenList().screen_list) > 1: Registry().get('main_window').activateWindow() return True - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Exception caught while loading Powerpoint presentation') - log.exception(e) trace_error_handler(log) return False @@ -192,9 +190,8 @@ class PowerpointDocument(PresentationDocument): if self.presentation: try: self.presentation.Close() - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while closing powerpoint presentation') - log.exception(e) trace_error_handler(log) self.presentation = None self.controller.remove_doc(self) @@ -210,9 +207,8 @@ class PowerpointDocument(PresentationDocument): try: if self.controller.process.Presentations.Count == 0: return False - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in is_loaded') - log.exception(e) trace_error_handler(log) return False return True @@ -229,9 +225,8 @@ class PowerpointDocument(PresentationDocument): return False if self.presentation.SlideShowWindow.View is None: return False - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in is_active') - log.exception(e) trace_error_handler(log) return False return True @@ -249,9 +244,8 @@ class PowerpointDocument(PresentationDocument): self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[self.blank_slide], False) if self.blank_click: self.presentation.SlideShowWindow.View.GotoClick(self.blank_click) - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in unblank_screen') - log.exception(e) trace_error_handler(log) self.show_error_msg() # Stop powerpoint from flashing in the taskbar @@ -273,9 +267,8 @@ class PowerpointDocument(PresentationDocument): self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex() # ppSlideShowBlackScreen = 3 self.presentation.SlideShowWindow.View.State = 3 - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in blank_screen') - log.exception(e) trace_error_handler(log) self.show_error_msg() @@ -288,9 +281,8 @@ class PowerpointDocument(PresentationDocument): try: # ppSlideShowBlackScreen = 3 return self.presentation.SlideShowWindow.View.State == 3 - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in is_blank') - log.exception(e) trace_error_handler(log) self.show_error_msg() else: @@ -303,9 +295,8 @@ class PowerpointDocument(PresentationDocument): log.debug('stop_presentation') try: self.presentation.SlideShowWindow.View.Exit() - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in stop_presentation') - log.exception(e) trace_error_handler(log) self.show_error_msg() @@ -328,9 +319,8 @@ class PowerpointDocument(PresentationDocument): ppt_window = None try: ppt_window = self.presentation.SlideShowSettings.Run() - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in start_presentation') - log.exception(e) trace_error_handler(log) self.show_error_msg() if ppt_window and not Settings().value('presentations/powerpoint control window'): @@ -339,9 +329,8 @@ class PowerpointDocument(PresentationDocument): ppt_window.Height = size.height() * 72 / dpi ppt_window.Left = size.x() * 72 / dpi ppt_window.Width = size.width() * 72 / dpi - except AttributeError as e: + except AttributeError: log.exception('AttributeError while in start_presentation') - log.exception(e) # Find the presentation window and save the handle for later self.presentation_hwnd = None if ppt_window: @@ -399,9 +388,8 @@ class PowerpointDocument(PresentationDocument): ret = next((key for key, slidenum in self.index_map.items() if slidenum == ret), None) else: ret = self.presentation.SlideShowWindow.View.CurrentShowPosition - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in get_slide_number') - log.exception(e) trace_error_handler(log) self.show_error_msg() return ret @@ -431,9 +419,8 @@ class PowerpointDocument(PresentationDocument): self.next_step() else: self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[slide_no]) - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in goto_slide') - log.exception(e) trace_error_handler(log) self.show_error_msg() @@ -445,9 +432,8 @@ class PowerpointDocument(PresentationDocument): try: self.presentation.SlideShowWindow.Activate() self.presentation.SlideShowWindow.View.Next() - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in next_step') - log.exception(e) trace_error_handler(log) self.show_error_msg() return @@ -468,9 +454,8 @@ class PowerpointDocument(PresentationDocument): log.debug('previous_step') try: self.presentation.SlideShowWindow.View.Previous() - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Caught exception while in previous_step') - log.exception(e) trace_error_handler(log) self.show_error_msg() @@ -503,8 +488,8 @@ class PowerpointDocument(PresentationDocument): slide = self.presentation.Slides(self.index_map[num + 1]) try: text = slide.Shapes.Title.TextFrame.TextRange.Text - except Exception as e: - log.exception(e) + except Exception: + log.exception('Exception raised when getting title text') text = '' titles.append(text.replace('\n', ' ').replace('\x0b', ' ') + '\n') note = _get_text_from_shapes(slide.NotesPage.Shapes) @@ -519,9 +504,8 @@ class PowerpointDocument(PresentationDocument): """ try: self.presentation.SlideShowWindow.View.Exit() - except (AttributeError, pywintypes.com_error) as e: + except (AttributeError, pywintypes.com_error): log.exception('Failed to exit Powerpoint presentation after error') - log.exception(e) critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument', 'An error occurred in the PowerPoint integration ' 'and the presentation will be stopped. ' @@ -540,7 +524,6 @@ def _get_text_from_shapes(shapes): if shape.PlaceholderFormat.Type == 2: # 2 from is enum PpPlaceholderType.ppPlaceholderBody if shape.HasTextFrame and shape.TextFrame.HasText: text += shape.TextFrame.TextRange.Text + '\n' - except pywintypes.com_error as e: - log.warning('Failed to extract text from powerpoint slide') - log.warning(e) + except pywintypes.com_error: + log.exception('Failed to extract text from powerpoint slide') return text diff --git a/openlp/plugins/songs/forms/authorsform.py b/openlp/plugins/songs/forms/authorsform.py index bec967561..84e0343e9 100644 --- a/openlp/plugins/songs/forms/authorsform.py +++ b/openlp/plugins/songs/forms/authorsform.py @@ -35,7 +35,8 @@ class AuthorsForm(QtWidgets.QDialog, Ui_AuthorsDialog): """ Set up the screen and common data """ - super(AuthorsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(AuthorsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self.auto_display_name = False self.first_name_edit.textEdited.connect(self.on_first_name_edited) diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index dbebf19bc..b89748402 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -82,6 +82,9 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): self.finish_button.clicked.connect(self.on_wizard_exit) self.cancel_button.clicked.connect(self.on_wizard_exit) + def closeEvent(self, event): + self.on_wizard_exit() + def add_custom_pages(self): """ Add song wizard specific pages. @@ -130,9 +133,9 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): Song wizard localisation. """ self.setWindowTitle(translate('Wizard', 'Wizard')) - # TODO: Check format() using template strings - self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', - 'Welcome to the Duplicate Song Removal Wizard')) + self.title_label.setText( + WizardStrings.HeaderStyle.format(text=translate('OpenLP.Ui', + 'Welcome to the Duplicate Song Removal Wizard'))) self.information_label.setText( translate("Wizard", 'This wizard will help you to remove duplicate songs from the song database. You will have a ' @@ -216,8 +219,7 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties): self.button(QtWidgets.QWizard.CancelButton).hide() QtWidgets.QMessageBox.information( self, translate('Wizard', 'Information'), - translate('Wizard', 'No duplicate songs have been found in the database.'), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok)) + translate('Wizard', 'No duplicate songs have been found in the database.')) def add_duplicates_to_song_list(self, search_song, duplicate_song): """ diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index d7f614245..dd965f733 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -56,7 +56,8 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): """ Constructor """ - super(EditSongForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(EditSongForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.media_item = media_item self.song = None # can this be automated? @@ -203,8 +204,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): 'There is no verse corresponding to "{invalid}". Valid entries are {valid}.\n' 'Please enter the verses separated by spaces.').format(invalid=invalid_verses[0], valid=valid) - critical_error_message_box(title=translate('SongsPlugin.EditSongForm', 'Invalid Verse Order'), - message=msg) + critical_error_message_box(title=translate('SongsPlugin.EditSongForm', 'Invalid Verse Order'), message=msg) return len(invalid_verses) == 0 def _validate_song(self): @@ -579,8 +579,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): self, translate('SongsPlugin.EditSongForm', 'Add Author'), translate('SongsPlugin.EditSongForm', 'This author does not exist, do you want to add them?'), - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: if text.find(' ') == -1: author = Author.populate(first_name='', last_name='', display_name=text) else: @@ -658,8 +657,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): if QtWidgets.QMessageBox.question( self, translate('SongsPlugin.EditSongForm', 'Add Topic'), translate('SongsPlugin.EditSongForm', 'This topic does not exist, do you want to add it?'), - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: topic = Topic.populate(name=text) self.manager.save_object(topic) topic_item = QtWidgets.QListWidgetItem(str(topic.name)) @@ -705,8 +703,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties): if QtWidgets.QMessageBox.question( self, translate('SongsPlugin.EditSongForm', 'Add Songbook'), translate('SongsPlugin.EditSongForm', 'This Songbook does not exist, do you want to add it?'), - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: songbook = Book.populate(name=text) self.manager.save_object(songbook) self.add_songbook_entry_to_list(songbook.id, songbook.name, self.songbook_entry_edit.text()) diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index f033f47d9..1e36df1e4 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -43,7 +43,8 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog): """ Constructor """ - super(EditVerseForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(EditVerseForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self.has_single_verse = False self.insert_button.clicked.connect(self.on_insert_button_clicked) diff --git a/openlp/plugins/songs/forms/mediafilesform.py b/openlp/plugins/songs/forms/mediafilesform.py index 0839b273e..10e9334f7 100644 --- a/openlp/plugins/songs/forms/mediafilesform.py +++ b/openlp/plugins/songs/forms/mediafilesform.py @@ -37,7 +37,8 @@ class MediaFilesForm(QtWidgets.QDialog, Ui_MediaFilesDialog): log.info('{name} MediaFilesForm loaded'.format(name=__name__)) def __init__(self, parent): - super(MediaFilesForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(MediaFilesForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) def populate_files(self, files): diff --git a/openlp/plugins/songs/forms/songbookform.py b/openlp/plugins/songs/forms/songbookform.py index 132611d65..fc1b87c09 100644 --- a/openlp/plugins/songs/forms/songbookform.py +++ b/openlp/plugins/songs/forms/songbookform.py @@ -38,7 +38,8 @@ class SongBookForm(QtWidgets.QDialog, Ui_SongBookDialog): """ Constructor """ - super(SongBookForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(SongBookForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) def exec(self, clear=True): diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 1780e7c7c..03ac9ee22 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -121,7 +121,7 @@ class SongExportForm(OpenLPWizard): self.selected_list_widget = QtWidgets.QListWidget(self.export_song_page) self.selected_list_widget.setObjectName('selected_list_widget') self.grid_layout.addWidget(self.selected_list_widget, 1, 0, 1, 1) - # FIXME: self.horizontal_layout is already defined above?!?!? + # FIXME: self.horizontal_layout is already defined above?!?!? Replace with Path Eidt! self.horizontal_layout = QtWidgets.QHBoxLayout() self.horizontal_layout.setObjectName('horizontal_layout') self.directory_label = QtWidgets.QLabel(self.export_song_page) @@ -143,9 +143,8 @@ class SongExportForm(OpenLPWizard): Song wizard localisation. """ self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard')) - # TODO: Verify format() with template variables - self.title_label.setText(WizardStrings.HeaderStyle % - translate('OpenLP.Ui', 'Welcome to the Song Export Wizard')) + self.title_label.setText( + WizardStrings.HeaderStyle.format(text=translate('OpenLP.Ui', 'Welcome to the Song Export Wizard'))) self.information_label.setText( translate('SongsPlugin.ExportWizardForm', 'This wizard will help to export your songs to the open and free ' 'OpenLyrics worship song format.')) diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 253702b56..3547521c9 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -132,9 +132,8 @@ class SongImportForm(OpenLPWizard, RegistryProperties): Song wizard localisation. """ self.setWindowTitle(translate('SongsPlugin.ImportWizardForm', 'Song Import Wizard')) - # TODO: Verify format() with template variables - self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui', - 'Welcome to the Song Import Wizard')) + self.title_label.setText( + WizardStrings.HeaderStyle.format(text=translate('OpenLP.Ui', 'Welcome to the Song Import Wizard'))) self.information_label.setText( translate('SongsPlugin.ImportWizardForm', 'This wizard will help you to import songs from a variety of formats. Click the next button ' @@ -272,12 +271,11 @@ class SongImportForm(OpenLPWizard, RegistryProperties): select_mode, format_name, ext_filter = SongFormat.get(this_format, 'selectMode', 'name', 'filter') file_path_edit = self.format_widgets[this_format]['file_path_edit'] if select_mode == SongFormatSelect.SingleFile: - # TODO: Verify format() with template variables - self.get_file_name( - WizardStrings.OpenTypeFile % format_name, file_path_edit, 'last directory import', ext_filter) + self.get_file_name(WizardStrings.OpenTypeFile.format(file_type=format_name), + file_path_edit, 'last directory import', ext_filter) elif select_mode == SongFormatSelect.SingleFolder: - # TODO: Verify format() with template variables - self.get_folder(WizardStrings.OpenTypeFolder % format_name, file_path_edit, 'last directory import') + self.get_folder( + WizardStrings.OpenTypeFolder.format(folder_name=format_name), file_path_edit, 'last directory import') def on_add_button_clicked(self): """ @@ -286,8 +284,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): this_format = self.current_format select_mode, format_name, ext_filter, custom_title = \ SongFormat.get(this_format, 'selectMode', 'name', 'filter', 'getFilesTitle') - # TODO: Verify format() with template variables - title = custom_title if custom_title else WizardStrings.OpenTypeFile % format_name + title = custom_title if custom_title else WizardStrings.OpenTypeFile.format(file_type=format_name) if select_mode == SongFormatSelect.MultipleFiles: self.get_files(title, self.format_widgets[this_format]['file_list_widget'], ext_filter) self.source_page.completeChanged.emit() diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index 2ed284298..be226f55e 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -39,7 +39,7 @@ class Ui_SongMaintenanceDialog(object): song_maintenance_dialog.setObjectName('song_maintenance_dialog') song_maintenance_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg')) song_maintenance_dialog.setWindowModality(QtCore.Qt.ApplicationModal) - song_maintenance_dialog.resize(10, 350) + song_maintenance_dialog.resize(600, 600) self.dialog_layout = QtWidgets.QGridLayout(song_maintenance_dialog) self.dialog_layout.setObjectName('dialog_layout') self.type_list_widget = QtWidgets.QListWidget(song_maintenance_dialog) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index a29c048e2..766231712 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -44,7 +44,8 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP """ Constructor """ - super(SongMaintenanceForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(SongMaintenanceForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self.manager = manager self.author_form = AuthorsForm(self) diff --git a/openlp/plugins/songs/forms/songselectform.py b/openlp/plugins/songs/forms/songselectform.py index 1169cb672..4863529a9 100644 --- a/openlp/plugins/songs/forms/songselectform.py +++ b/openlp/plugins/songs/forms/songselectform.py @@ -81,7 +81,8 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog): """ def __init__(self, parent=None, plugin=None, db_manager=None): - QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.plugin = plugin self.db_manager = db_manager self.setup_ui(self) @@ -248,8 +249,7 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog): translate('SongsPlugin.SongSelectForm', 'WARNING: Saving your username and password is INSECURE, your ' 'password is stored in PLAIN TEXT. Click Yes to save your ' 'password or No to cancel this.'), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.No) + defaultButton=QtWidgets.QMessageBox.No) if answer == QtWidgets.QMessageBox.No: self.save_password_checkbox.setChecked(False) @@ -397,8 +397,7 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog): translate('SongsPlugin.SongSelectForm', 'Your song has been imported, would you ' 'like to import more songs?'), - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: self.on_back_button_clicked() else: self.application.process_events() diff --git a/openlp/plugins/songs/forms/topicsform.py b/openlp/plugins/songs/forms/topicsform.py index a811b277a..f0c7f0ab1 100644 --- a/openlp/plugins/songs/forms/topicsform.py +++ b/openlp/plugins/songs/forms/topicsform.py @@ -38,7 +38,8 @@ class TopicsForm(QtWidgets.QDialog, Ui_TopicsDialog): """ Constructor """ - super(TopicsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(TopicsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) def exec(self, clear=True): diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index d6287b065..9e1f4f166 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -265,7 +265,7 @@ class SongFormat(object): }, EasyWorshipService: { 'class': EasyWorshipSongImport, - 'name': 'EasyWorship Service File', + 'name': 'EasyWorship Service', 'prefix': 'ew', 'selectMode': SongFormatSelect.SingleFile, 'filter': '{text} (*.ews)'.format(text=translate('SongsPlugin.ImportWizardForm', diff --git a/openlp/plugins/songs/lib/importers/foilpresenter.py b/openlp/plugins/songs/lib/importers/foilpresenter.py index 7a8146aa4..06f45c89c 100644 --- a/openlp/plugins/songs/lib/importers/foilpresenter.py +++ b/openlp/plugins/songs/lib/importers/foilpresenter.py @@ -121,8 +121,8 @@ class FoilPresenterImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - # TODO: Verify format() with template strings - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) + self.import_wizard.increment_progress_bar( + WizardStrings.ImportingType.format(source=os.path.basename(file_path))) try: parsed_file = etree.parse(file_path, parser) xml = etree.tostring(parsed_file).decode() diff --git a/openlp/plugins/songs/lib/importers/openlp.py b/openlp/plugins/songs/lib/importers/openlp.py index c0506bf6a..a42e7e37c 100644 --- a/openlp/plugins/songs/lib/importers/openlp.py +++ b/openlp/plugins/songs/lib/importers/openlp.py @@ -275,11 +275,9 @@ class OpenLPSongImport(SongImport): self.manager.save_object(new_song) if progress_dialog: progress_dialog.setValue(progress_dialog.value() + 1) - # TODO: Verify format() with template strings - progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title) + progress_dialog.setLabelText(WizardStrings.ImportingType.format(source=new_song.title)) else: - # TODO: Verify format() with template strings - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=new_song.title)) if self.stop_import_flag: break self.source_session.close() diff --git a/openlp/plugins/songs/lib/importers/openlyrics.py b/openlp/plugins/songs/lib/importers/openlyrics.py index fbdfd7b67..09cc1ef91 100644 --- a/openlp/plugins/songs/lib/importers/openlyrics.py +++ b/openlp/plugins/songs/lib/importers/openlyrics.py @@ -58,8 +58,8 @@ class OpenLyricsImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - # TODO: Verify format() with template strings - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) + self.import_wizard.increment_progress_bar( + WizardStrings.ImportingType.format(source=os.path.basename(file_path))) try: # Pass a file object, because lxml does not cope with some # special characters in the path (see lp:757673 and lp:744337). diff --git a/openlp/plugins/songs/lib/importers/powerpraise.py b/openlp/plugins/songs/lib/importers/powerpraise.py index 358f01e10..a652cf58c 100644 --- a/openlp/plugins/songs/lib/importers/powerpraise.py +++ b/openlp/plugins/songs/lib/importers/powerpraise.py @@ -41,8 +41,8 @@ class PowerPraiseImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - # TODO: Verify format() with template strings - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) + self.import_wizard.increment_progress_bar( + WizardStrings.ImportingType.format(source=os.path.basename(file_path))) root = objectify.parse(open(file_path, 'rb')).getroot() self.process_song(root) diff --git a/openlp/plugins/songs/lib/importers/presentationmanager.py b/openlp/plugins/songs/lib/importers/presentationmanager.py index 7113842c4..4031431ba 100644 --- a/openlp/plugins/songs/lib/importers/presentationmanager.py +++ b/openlp/plugins/songs/lib/importers/presentationmanager.py @@ -44,8 +44,8 @@ class PresentationManagerImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - # TODO: Verify format() with template strings - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) + self.import_wizard.increment_progress_bar( + WizardStrings.ImportingType.format(source=os.path.basename(file_path))) try: tree = etree.parse(file_path, parser=etree.XMLParser(recover=True)) except etree.XMLSyntaxError: diff --git a/openlp/plugins/songs/lib/importers/propresenter.py b/openlp/plugins/songs/lib/importers/propresenter.py index f1401c8a0..9a3fa372d 100644 --- a/openlp/plugins/songs/lib/importers/propresenter.py +++ b/openlp/plugins/songs/lib/importers/propresenter.py @@ -46,8 +46,8 @@ class ProPresenterImport(SongImport): for file_path in self.import_source: if self.stop_import_flag: return - # TODO: Verify format() with template strings - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) + self.import_wizard.increment_progress_bar( + WizardStrings.ImportingType.format(source=os.path.basename(file_path))) root = objectify.parse(open(file_path, 'rb')).getroot() self.process_song(root, file_path) diff --git a/openlp/plugins/songs/lib/importers/songimport.py b/openlp/plugins/songs/lib/importers/songimport.py index 7ea66a3da..070919c44 100644 --- a/openlp/plugins/songs/lib/importers/songimport.py +++ b/openlp/plugins/songs/lib/importers/songimport.py @@ -347,8 +347,7 @@ class SongImport(QtCore.QObject): song = Song() song.title = self.title if self.import_wizard is not None: - # TODO: Verify format() with template variables - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % song.title) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=song.title)) song.alternate_title = self.alternate_title # Values will be set when cleaning the song. song.search_title = '' diff --git a/openlp/plugins/songs/lib/importers/songshowplus.py b/openlp/plugins/songs/lib/importers/songshowplus.py index 4848349f0..b0841b672 100644 --- a/openlp/plugins/songs/lib/importers/songshowplus.py +++ b/openlp/plugins/songs/lib/importers/songshowplus.py @@ -100,8 +100,7 @@ class SongShowPlusImport(SongImport): self.other_count = 0 self.other_list = {} file_name = os.path.split(file)[1] - # TODO: Verify format() with template variables - self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % file_name, 0) + self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_name), 0) song_data = open(file, 'rb') while True: block_key, = struct.unpack("I", song_data.read(4)) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 7c8bba200..c5f424a9a 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -231,9 +231,14 @@ class SongMediaItem(MediaManagerItem): def search_entire(self, search_keywords): search_string = '%{text}%'.format(text=clean_string(search_keywords)) - return self.plugin.manager.get_all_objects( - Song, or_(Song.search_title.like(search_string), Song.search_lyrics.like(search_string), - Song.comments.like(search_string))) + return self.plugin.manager.session.query(Song) \ + .join(SongBookEntry, isouter=True) \ + .join(Book, isouter=True) \ + .filter(or_(Book.name.like(search_string), SongBookEntry.entry.like(search_string), + # hint: search_title contains alternate title + Song.search_title.like(search_string), Song.search_lyrics.like(search_string), + Song.comments.like(search_string))) \ + .all() def on_song_list_load(self): """ @@ -500,8 +505,7 @@ class SongMediaItem(MediaManagerItem): translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the "{items:d}" ' 'selected song(s)?').format(items=len(items)), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: return self.application.set_busy_cursor() self.main_window.display_progress_bar(len(items)) diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py index bc02043ac..807ea5593 100644 --- a/openlp/plugins/songs/lib/openlyricsxml.py +++ b/openlp/plugins/songs/lib/openlyricsxml.py @@ -70,8 +70,7 @@ from openlp.plugins.songs.lib.db import Author, AuthorType, Book, Song, Topic log = logging.getLogger(__name__) NAMESPACE = 'http://openlyrics.info/namespace/2009/song' -# TODO: Verify format() with template variable -NSMAP = '{' + NAMESPACE + '}' + '%s' +NSMAP = '{{' + NAMESPACE + '}}{tag}' class SongXML(object): @@ -616,15 +615,13 @@ class OpenLyrics(object): text = '' use_endtag = True # Skip elements - not yet supported. - # TODO: Verify format() with template variables - if element.tag == NSMAP % 'comment': + if element.tag == NSMAP.format(tag='comment'): if element.tail: # Append tail text at comment element. text += element.tail return text # Convert chords to ChordPro format which OpenLP uses internally - # TODO: Verify format() with template variables - elif element.tag == NSMAP % 'chord': + elif element.tag == NSMAP.format(tag='chord'): if Settings().value('songs/enable chords') and not Settings().value('songs/disable chords import'): text += '[{chord}]'.format(chord=element.get('name')) if element.tail: @@ -632,15 +629,13 @@ class OpenLyrics(object): text += element.tail return text # Convert line breaks
to \n. - # TODO: Verify format() with template variables - elif newlines and element.tag == NSMAP % 'br': + elif newlines and element.tag == NSMAP.format(tag='br'): text += '\n' if element.tail: text += element.tail return text # Start formatting tag. - # TODO: Verify format() with template variables - if element.tag == NSMAP % 'tag': + if element.tag == NSMAP.format(tag='tag'): text += '{{{name}}}'.format(name=element.get('name')) # Some formattings may have only start tag. # Handle this case if element has no children and contains no text. @@ -654,8 +649,7 @@ class OpenLyrics(object): # Use recursion since nested formatting tags are allowed. text += self._process_lines_mixed_content(child, newlines) # Append text from tail and add formatting end tag. - # TODO: Verify format() with template variables - if element.tag == NSMAP % 'tag' and use_endtag: + if element.tag == NSMAP.format(tag='tag') and use_endtag: text += '{{/{name}}}'.format(name=element.get('name')) # Append text from tail. if element.tail: @@ -682,8 +676,7 @@ class OpenLyrics(object): # Loop over the "line" elements removing comments for line in element: # Skip comment lines. - # TODO: Verify format() with template variables - if line.tag == NSMAP % 'comment': + if line.tag == NSMAP.format(tag='comment'): continue if text: text += '\n' diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 5682fb13d..2367e828c 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -58,7 +58,7 @@ __default_settings__ = { 'songs/db password': '', 'songs/db hostname': '', 'songs/db database': '', - 'songs/last search type': SongSearch.Entire, + 'songs/last used search type': SongSearch.Entire, 'songs/last import type': SongFormat.OpenLyrics, 'songs/update service on edit': False, 'songs/add song from service': True, diff --git a/openlp/plugins/songusage/forms/songusagedeleteform.py b/openlp/plugins/songusage/forms/songusagedeleteform.py index 108ccb438..37664b06a 100644 --- a/openlp/plugins/songusage/forms/songusagedeleteform.py +++ b/openlp/plugins/songusage/forms/songusagedeleteform.py @@ -37,7 +37,7 @@ class SongUsageDeleteForm(QtWidgets.QDialog, Ui_SongUsageDeleteDialog, RegistryP """ self.manager = manager super(SongUsageDeleteForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | - QtCore.Qt.WindowTitleHint) + QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.setupUi(self) self.button_box.clicked.connect(self.on_button_box_clicked) @@ -53,9 +53,7 @@ class SongUsageDeleteForm(QtWidgets.QDialog, Ui_SongUsageDeleteDialog, RegistryP 'Delete Selected Song Usage Events?'), translate('SongUsagePlugin.SongUsageDeleteForm', 'Are you sure you want to delete selected Song Usage data?'), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.No) + defaultButton=QtWidgets.QMessageBox.No) if ret == QtWidgets.QMessageBox.Yes: delete_date = self.delete_calendar.selectedDate().toPyDate() self.manager.delete_all_objects(SongUsageItem, SongUsageItem.usagedate <= delete_date) diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index 8a270598e..172cca6b1 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -44,7 +44,8 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP """ Initialise the form """ - super(SongUsageDetailForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) + super(SongUsageDetailForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | + QtCore.Qt.WindowCloseButtonHint) self.plugin = plugin self.setupUi(self) diff --git a/tests/functional/openlp_core_lib/test_projector_constants.py b/tests/functional/openlp_core_lib/test_projector_constants.py index 4a8e7a9e1..019c18888 100644 --- a/tests/functional/openlp_core_lib/test_projector_constants.py +++ b/tests/functional/openlp_core_lib/test_projector_constants.py @@ -29,7 +29,7 @@ class TestProjectorConstants(TestCase): """ Test specific functions in the projector constants module. """ - def build_pjlink_video_label_test(self): + def test_build_pjlink_video_label(self): """ Test building PJLINK_DEFAULT_CODES dictionary """ diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink1.py b/tests/functional/openlp_core_lib/test_projector_pjlink1.py index cd62da060..e5fb7566f 100644 --- a/tests/functional/openlp_core_lib/test_projector_pjlink1.py +++ b/tests/functional/openlp_core_lib/test_projector_pjlink1.py @@ -384,21 +384,6 @@ class TestPJLink(TestCase): self.assertEquals("{test}".format(test=mock_send_command.call_args), "call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH)) - @patch.object(pjlink_test, '_not_implemented') - def not_implemented_test(self, mock_not_implemented): - """ - Test PJLink._not_implemented method being called - """ - # GIVEN: test object - pjlink = pjlink_test - test_cmd = 'TESTMEONLY' - - # WHEN: A future command is called that is not implemented yet - pjlink.process_command(test_cmd, "Garbage data for test only") - - # THEN: PJLink.__not_implemented should have been called with test_cmd - mock_not_implemented.assert_called_with(test_cmd) - @patch.object(pjlink_test, 'disconnect_from_host') def socket_abort_test(self, mock_disconnect): """ diff --git a/tests/functional/openlp_core_lib/test_projectordb.py b/tests/functional/openlp_core_lib/test_projectordb.py index 6b63ad493..d4ff4e75c 100644 --- a/tests/functional/openlp_core_lib/test_projectordb.py +++ b/tests/functional/openlp_core_lib/test_projectordb.py @@ -27,11 +27,15 @@ PREREQUISITE: add_record() and get_all() functions validated. """ import os import shutil -from unittest import TestCase +from tempfile import mkdtemp + +from unittest import TestCase, skip from unittest.mock import MagicMock, patch -from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source +from openlp.core.lib.projector import upgrade +from openlp.core.lib.db import upgrade_db from openlp.core.lib.projector.constants import PJLINK_PORT +from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source from tests.resources.projector.data import TEST_DB_PJLINK1, TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA from tests.utils.constants import TEST_RESOURCES_PATH @@ -85,6 +89,42 @@ def add_records(projector_db, test): return added +class TestProjectorDBUpdate(TestCase): + """ + Test case for upgrading Projector DB. + NOTE: Separate class so I don't have to look for upgrade tests. + """ + def setUp(self): + """ + Setup for tests + """ + self.tmp_folder = mkdtemp(prefix='openlp_') + + def tearDown(self): + """ + Clean up after tests + """ + # Ignore errors since windows can have problems with locked files + shutil.rmtree(self.tmp_folder, ignore_errors=True) + + def test_upgrade_old_projector_db(self): + """ + Test that we can upgrade an old song db to the current schema + """ + # GIVEN: An old song db + old_db = os.path.join(TEST_RESOURCES_PATH, "projector", TEST_DB_PJLINK1) + tmp_db = os.path.join(self.tmp_folder, TEST_DB) + shutil.copyfile(old_db, tmp_db) + db_url = 'sqlite:///{db}'.format(db=tmp_db) + + # WHEN: upgrading the db + updated_to_version, latest_version = upgrade_db(db_url, upgrade) + + # THEN: the song db should have been upgraded to the latest version + self.assertEqual(updated_to_version, latest_version, + 'The projector DB should have been upgrade to the latest version') + + class TestProjectorDB(TestCase): """ Test case for ProjectorDB @@ -94,7 +134,9 @@ class TestProjectorDB(TestCase): """ Set up anything necessary for all tests """ - mocked_init_url.return_value = 'sqlite:///{db}'.format(db=TEST_DB) + self.tmp_folder = mkdtemp(prefix='openlp_') + tmpdb_url = 'sqlite:///{db}'.format(db=os.path.join(self.tmp_folder, TEST_DB)) + mocked_init_url.return_value = tmpdb_url self.projector = ProjectorDB() def tearDown(self): @@ -103,15 +145,8 @@ class TestProjectorDB(TestCase): """ self.projector.session.close() self.projector = None - retries = 0 - while retries < 5: - try: - if os.path.exists(TEST_DB): - os.unlink(TEST_DB) - break - except: - time.sleep(1) - retries += 1 + # Ignore errors since windows can have problems with locked files + shutil.rmtree(self.tmp_folder, ignore_errors=True) def test_find_record_by_ip(self): """ @@ -271,10 +306,10 @@ class TestProjectorDB(TestCase): # THEN: __repr__ should return a proper string self.assertEqual(str(projector), - '< Projector(id="0", ip="127.0.0.1", port="4352", pin="None", name="Test One", ' - 'location="Somewhere over the rainbow", notes="Not again", pjlink_name="TEST", ' - 'manufacturer="IN YOUR DREAMS", model="OpenLP", serial_no="None", other="None", ' - 'sources="None", source_list="[]", model_filter="None", model_lamp="None", ' + '< Projector(id="0", ip="127.0.0.1", port="4352", mac_adx="None", pin="None", ' + 'name="Test One", location="Somewhere over the rainbow", notes="Not again", ' + 'pjlink_name="TEST", manufacturer="IN YOUR DREAMS", model="OpenLP", serial_no="None", ' + 'other="None", sources="None", source_list="[]", model_filter="None", model_lamp="None", ' 'sw_version="None") >', 'Projector.__repr__() should have returned a proper representation string') diff --git a/tests/functional/openlp_core_ui/test_formattingtagsform.py b/tests/functional/openlp_core_ui/test_formattingtagsform.py index bb9534439..c4069a553 100644 --- a/tests/functional/openlp_core_ui/test_formattingtagsform.py +++ b/tests/functional/openlp_core_ui/test_formattingtagsform.py @@ -27,12 +27,6 @@ from unittest.mock import MagicMock, patch, call from openlp.core.ui.formattingtagform import FormattingTagForm -# TODO: Tests Still TODO -# __init__ -# exec -# on_saved_clicked -# _reloadTable - class TestFormattingTagForm(TestCase): diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index 86aaf5ea7..e9ec825d2 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -29,7 +29,7 @@ from PyQt5 import QtCore from openlp.core.common import Registry, is_macosx, Settings from openlp.core.lib import ScreenList, PluginManager -from openlp.core.ui import MainDisplay +from openlp.core.ui import MainDisplay, AudioPlayer from openlp.core.ui.media import MediaController from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET @@ -283,3 +283,18 @@ class TestMainDisplay(TestCase, TestMixin): self.assertEquals(main_display.web_view.setHtml.call_count, 1, 'setHTML should be called once') self.assertEquals(main_display.media_controller.video.call_count, 1, 'Media Controller video should have been called once') + + +def test_calling_next_item_in_playlist(): + """ + Test the AudioPlayer.next() method + """ + # GIVEN: An instance of AudioPlayer with a mocked out playlist + audio_player = AudioPlayer(None) + + # WHEN: next is called. + with patch.object(audio_player, 'playlist') as mocked_playlist: + audio_player.next() + + # THEN: playlist.next should had been called once. + mocked_playlist.next.assert_called_once_with() diff --git a/tests/functional/openlp_core_ui/test_thememanager.py b/tests/functional/openlp_core_ui/test_thememanager.py index a30eda7fa..45209ebeb 100644 --- a/tests/functional/openlp_core_ui/test_thememanager.py +++ b/tests/functional/openlp_core_ui/test_thememanager.py @@ -176,7 +176,7 @@ class TestThemeManager(TestCase): self.assertTrue(result) mocked_qmessagebox_question.assert_called_once_with( theme_manager, 'Theme Already Exists', 'Theme Theme Name already exists. Do you want to replace it?', - ANY, ANY) + defaultButton=ANY) def test_over_write_message_box_no(self): """ @@ -196,7 +196,7 @@ class TestThemeManager(TestCase): self.assertFalse(result) mocked_qmessagebox_question.assert_called_once_with( theme_manager, 'Theme Already Exists', 'Theme Theme Name already exists. Do you want to replace it?', - ANY, ANY) + defaultButton=ANY) def test_unzip_theme(self): """ diff --git a/tests/functional/openlp_plugins/bibles/test_bibleserver.py b/tests/functional/openlp_plugins/bibles/test_bibleserver.py index a66f8b28e..bc05a601f 100644 --- a/tests/functional/openlp_plugins/bibles/test_bibleserver.py +++ b/tests/functional/openlp_plugins/bibles/test_bibleserver.py @@ -29,45 +29,11 @@ from bs4 import BeautifulSoup from openlp.plugins.bibles.lib.importers.http import BSExtract -# TODO: Items left to test -# BGExtract -# __init__ -# _remove_elements -# _extract_verse -# _clean_soup -# _extract_verses -# _extract_verses_old -# get_bible_chapter -# get_books_from_http -# _get_application -# CWExtract -# __init__ -# get_bible_chapter -# get_books_from_http -# _get_application -# HTTPBible -# __init__ -# do_import -# get_verses -# get_chapter -# get_books -# get_chapter_count -# get_verse_count -# _get_application -# get_soup_for_bible_ref -# send_error_message - class TestBSExtract(TestCase): """ Test the BSExtractClass """ - # TODO: Items left to test - # BSExtract - # __init__ - # get_bible_chapter - # get_books_from_http - # _get_application def setUp(self): self.get_soup_for_bible_ref_patcher = patch('openlp.plugins.bibles.lib.importers.http.get_soup_for_bible_ref') self.log_patcher = patch('openlp.plugins.bibles.lib.importers.http.log') diff --git a/tests/functional/openlp_plugins/bibles/test_lib.py b/tests/functional/openlp_plugins/bibles/test_lib.py index 2c2615a7a..5ae8ae85d 100644 --- a/tests/functional/openlp_plugins/bibles/test_lib.py +++ b/tests/functional/openlp_plugins/bibles/test_lib.py @@ -68,7 +68,8 @@ class TestLib(TestCase, TestMixin): """ # GIVEN: Some test data which contains different references to parse, with the expected results. with patch('openlp.plugins.bibles.lib.Settings', return_value=MagicMock(**{'value.return_value': ''})): - # The following test data tests with 222 variants when using the default 'separators' + # The following test data tests with about 240 variants when using the default 'separators' + # The amount is exactly 222 without '1. John 23' and'1. John. 23' test_data = [ # Input reference, book name, chapter + verse reference ('Psalm 23', 'Psalm', '23'), @@ -84,6 +85,8 @@ class TestLib(TestCase, TestMixin): ('Psalm 23{_and}24', 'Psalm', '23,24'), ('1 John 23', '1 John', '23'), ('1 John. 23', '1 John', '23'), + ('1. John 23', '1. John', '23'), + ('1. John. 23', '1. John', '23'), ('1 John 23{to}24', '1 John', '23-24'), ('1 John 23{verse}1{to}2', '1 John', '23:1-2'), ('1 John 23{verse}1{to}{end}', '1 John', '23:1-end'), diff --git a/tests/functional/openlp_plugins/bibles/test_mediaitem.py b/tests/functional/openlp_plugins/bibles/test_mediaitem.py index f9c5f64d0..a26802be7 100755 --- a/tests/functional/openlp_plugins/bibles/test_mediaitem.py +++ b/tests/functional/openlp_plugins/bibles/test_mediaitem.py @@ -199,9 +199,6 @@ class TestMediaItem(TestCase, TestMixin): self.assertTrue(self.media_item.has_delete_icon, 'Check that the icon is called as True.') self.assertFalse(self.media_item.add_to_service_item, 'Check that the icon is called as False') - # TODO: Test add_end_header_bar - # TODO: Test setupUi - def test_on_focus_search_tab_visible(self): """ Test the correct widget gets focus when the BibleMediaItem receives focus @@ -480,9 +477,9 @@ class TestMediaItem(TestCase, TestMixin): # WHEN: Calling update_auto_completer self.media_item.update_auto_completer() - # THEN: set_case_insensitive_completer should have been called with the names of the books in order + # THEN: set_case_insensitive_completer should have been called with the names of the books + space in order mocked_set_case_insensitive_completer.assert_called_once_with( - ['Book 1', 'Book 2', 'Book 3'], mocked_search_edit) + ['Book 1 ', 'Book 2 ', 'Book 3 '], mocked_search_edit) def test_update_auto_completer_search_combined_type(self): """ @@ -500,11 +497,11 @@ class TestMediaItem(TestCase, TestMixin): # WHEN: Calling update_auto_completer self.media_item.update_auto_completer() - # THEN: set_case_insensitive_completer should have been called with the names of the books in order + # THEN: set_case_insensitive_completer should have been called with the names of the books + space in order mocked_set_case_insensitive_completer.assert_called_once_with( - ['Book 1', 'Book 2', 'Book 3'], mocked_search_edit) + ['Book 1 ', 'Book 2 ', 'Book 3 '], mocked_search_edit) - def test_on_import_click_no_import_wizzard_attr(self): + def test_on_import_click_no_import_wizard_attr(self): """ Test on_import_click when media_item does not have the `import_wizard` attribute. And the wizard was canceled. """ @@ -521,9 +518,9 @@ class TestMediaItem(TestCase, TestMixin): self.assertTrue(mocked_bible_import_form.called) self.assertFalse(mocked_reload_bibles.called) - def test_on_import_click_wizzard_not_canceled(self): + def test_on_import_click_wizard_not_canceled(self): """ - Test on_import_click when the media item has the import_wizzard attr set and wizard completes sucessfully. + Test on_import_click when the media item has the import_wizard attr set and wizard completes sucessfully. """ # GIVEN: An instance of :class:`MediaManagerItem` and a mocked import_wizard mocked_import_wizard = MagicMock(**{'exec.return_value': True}) @@ -1381,8 +1378,6 @@ class TestMediaItem(TestCase, TestMixin): self.assertTrue(self.mocked_main_window.information_message.called) mocked_display_results.assert_called_once_with() - # TODO: Test text_search - def test_on_search_edit_text_changed_search_while_typing_disabled(self): """ Test on_search_edit_text_changed when 'search while typing' is disabled diff --git a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py index 88544f0e9..3c08d226a 100644 --- a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py @@ -42,11 +42,6 @@ class TestPptviewController(TestCase, TestMixin): """ Test the PptviewController Class """ -# TODO: Items left to test -# PptviewController -# start_process(self) -# kill - def setUp(self): """ Set up the patches and mocks need for all tests. @@ -103,24 +98,6 @@ class TestPptviewDocument(TestCase): """ Test the PptviewDocument Class """ - # TODO: Items left to test - # PptviewDocument - # __init__ - # create_thumbnails - # close_presentation - # is_loaded - # is_active - # blank_screen - # unblank_screen - # is_blank - # stop_presentation - # start_presentation - # get_slide_number - # get_slide_count - # goto_slide - # next_step - # previous_step - def setUp(self): """ Set up the patches and mocks need for all tests. diff --git a/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py b/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py index 0254f3ce5..9f02fb9c8 100644 --- a/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py @@ -36,19 +36,6 @@ class TestPresentationController(TestCase): """ Test the PresentationController. """ - # TODO: Items left to test - # PresentationController - # __init__ - # enabled - # is_available - # check_available - # start_process - # kill - # add_document - # remove_doc - # close_presentation - # _get_plugin_manager - def setUp(self): self.get_thumbnail_folder_patcher = \ patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') @@ -183,32 +170,6 @@ class TestPresentationDocument(TestCase): """ Test the PresentationDocument Class """ - # TODO: Items left to test - # PresentationDocument - # __init__ - # presentation_deleted - # get_thumbnail_folder - # get_temp_folder - # check_thumbnails - # close_presentation - # is_active - # is_loaded - # blank_screen - # unblank_screen - # is_blank - # stop_presentation - # start_presentation - # get_slide_number - # get_slide_count - # goto_slide - # next_step - # previous_step - # convert_thumbnail - # get_thumbnail_path - # poll_slidenumber - # get_slide_text - # get_slide_notes - def setUp(self): """ Set up the patches and mocks need for all tests. diff --git a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py index 1a8b1e38b..d3d705722 100644 --- a/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py +++ b/tests/functional/openlp_plugins/songs/test_foilpresenterimport.py @@ -36,18 +36,6 @@ class TestFoilPresenter(TestCase): """ Test the functions in the :mod:`foilpresenterimport` module. """ - # TODO: The following modules still need tests written for - # xml_to_song - # to_str - # _process_authors - # _process_cclinumber - # _process_comments - # _process_copyright - # _process_lyrics - # _process_songbooks - # _process_titles - # _process_topics - def setUp(self): self.to_str_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.to_str') self.clean_song_patcher = patch('openlp.plugins.songs.lib.importers.foilpresenter.clean_song') diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index d5b06b7dc..15c8e5ef0 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -46,9 +46,10 @@ class TestMediaItem(TestCase, TestMixin): Registry.create() Registry().register('service_list', MagicMock()) Registry().register('main_window', MagicMock()) + self.mocked_plugin = MagicMock() with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \ patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'): - self.media_item = SongMediaItem(None, MagicMock()) + self.media_item = SongMediaItem(None, self.mocked_plugin) self.media_item.save_auto_select_id = MagicMock() self.media_item.list_view = MagicMock() self.media_item.list_view.save_auto_select_id = MagicMock() @@ -558,3 +559,35 @@ class TestMediaItem(TestCase, TestMixin): # THEN: The correct formatted results are returned self.assertEqual(search_results, [[123, 'My Song', 'My alternative']]) + + @patch('openlp.plugins.songs.lib.mediaitem.Book') + @patch('openlp.plugins.songs.lib.mediaitem.SongBookEntry') + @patch('openlp.plugins.songs.lib.mediaitem.Song') + @patch('openlp.plugins.songs.lib.mediaitem.or_') + def test_entire_song_search(self, mocked_or, MockedSong, MockedSongBookEntry, MockedBook): + """ + Test that searching the entire song does the right queries + """ + # GIVEN: A song media item, a keyword and some mocks + keyword = 'Jesus' + mocked_song = MagicMock() + mocked_or.side_effect = lambda a, b, c, d, e: ' '.join([a, b, c, d, e]) + MockedSong.search_title.like.side_effect = lambda a: a + MockedSong.search_lyrics.like.side_effect = lambda a: a + MockedSong.comments.like.side_effect = lambda a: a + MockedSongBookEntry.entry.like.side_effect = lambda a: a + MockedBook.name.like.side_effect = lambda a: a + + # WHEN: search_entire_song() is called with the keyword + self.media_item.search_entire(keyword) + + # THEN: The correct calls were made + MockedSong.search_title.like.assert_called_once_with('%jesus%') + MockedSong.search_lyrics.like.assert_called_once_with('%jesus%') + MockedSong.comments.like.assert_called_once_with('%jesus%') + MockedSongBookEntry.entry.like.assert_called_once_with('%jesus%') + MockedBook.name.like.assert_called_once_with('%jesus%') + mocked_or.assert_called_once_with('%jesus%', '%jesus%', '%jesus%', '%jesus%', '%jesus%') + self.mocked_plugin.manager.session.query.assert_called_once_with(MockedSong) + + self.assertEqual(self.mocked_plugin.manager.session.query.mock_calls[4][0], '().join().join().filter().all') diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index b367b7c41..8583e0a17 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -612,8 +612,7 @@ class TestSongSelectForm(TestCase, TestMixin): mocked_song_select_importer.save_song.assert_called_with(None) mocked_question.assert_called_with(ssform, 'Song Imported', 'Your song has been imported, would you like to import more songs?', - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.Yes) + defaultButton=QtWidgets.QMessageBox.Yes) mocked_on_back_button_clicked.assert_called_with() self.assertIsNone(ssform.song) @@ -639,8 +638,7 @@ class TestSongSelectForm(TestCase, TestMixin): mocked_song_select_importer.save_song.assert_called_with(None) mocked_question.assert_called_with(ssform, 'Song Imported', 'Your song has been imported, would you like to import more songs?', - QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, - QtWidgets.QMessageBox.Yes) + defaultButton=QtWidgets.QMessageBox.Yes) mocked_done.assert_called_with(QtWidgets.QDialog.Accepted) self.assertIsNone(ssform.song) diff --git a/tests/interfaces/openlp_core_lib/test_searchedit.py b/tests/interfaces/openlp_core_lib/test_searchedit.py index e2a01a450..4f4c4d68c 100644 --- a/tests/interfaces/openlp_core_lib/test_searchedit.py +++ b/tests/interfaces/openlp_core_lib/test_searchedit.py @@ -88,7 +88,7 @@ class TestSearchEdit(TestCase, TestMixin): # settings self.assertEqual(self.search_edit.current_search_type(), SearchTypes.First, "The first search type should be selected.") - self.mocked_settings().setValue.assert_called_once_with('settings_section/last search type', 0) + self.mocked_settings().setValue.assert_called_once_with('settings_section/last used search type', 0) def test_set_current_search_type(self): """ @@ -105,7 +105,7 @@ class TestSearchEdit(TestCase, TestMixin): self.assertEqual(self.search_edit.placeholderText(), SECOND_PLACEHOLDER_TEXT, "The correct placeholder text should be 'Second Placeholder Text'.") self.mocked_settings().setValue.assert_has_calls( - [call('settings_section/last search type', 0), call('settings_section/last search type', 1)]) + [call('settings_section/last used search type', 0), call('settings_section/last used search type', 1)]) def test_clear_button_visibility(self): """ diff --git a/tests/resources/projector/data.py b/tests/resources/projector/data.py index 358719015..d9baa80d0 100644 --- a/tests/resources/projector/data.py +++ b/tests/resources/projector/data.py @@ -29,7 +29,7 @@ from tempfile import gettempdir # Test data TEST_DB_PJLINK1 = 'projector_pjlink1.sqlite' -TEST_DB = os.path.join(gettempdir(), 'openlp-test-projectordb.sql') +TEST_DB = 'openlp-test-projectordb.sqlite' TEST_SALT = '498e4a67' @@ -39,8 +39,6 @@ TEST_HASH = '5d8409bc1c3fa39749434aa3a5c38682' TEST_CONNECT_AUTHENTICATE = 'PJLink 1 {salt}'.format(salt=TEST_SALT) -TEST_DB = os.path.join(gettempdir(), 'openlp-test-projectordb.sql') - TEST1_DATA = dict(ip='111.111.111.111', port='1111', pin='1111',