merge trunk

This commit is contained in:
Jonathan Springer 2017-07-08 09:12:31 -04:00
commit a7daffe54f
107 changed files with 981 additions and 510 deletions

View File

@ -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")

View File

@ -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():

View File

@ -219,7 +219,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

View File

@ -154,8 +154,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')

View File

@ -25,12 +25,15 @@ The :mod:`db` module provides the core database functionality for OpenLP
"""
import logging
import os
from copy import copy
from urllib.parse import quote_plus as urlquote
from sqlalchemy import Table, MetaData, Column, types, create_engine
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError, OperationalError
from sqlalchemy.engine.url import make_url
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError, OperationalError, ProgrammingError
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
from sqlalchemy.pool import NullPool
from alembic.migration import MigrationContext
from alembic.operations import Operations
@ -40,6 +43,66 @@ from openlp.core.lib.ui import critical_error_message_box
log = logging.getLogger(__name__)
def database_exists(url):
"""Check if a database exists.
:param url: A SQLAlchemy engine URL.
Performs backend-specific testing to quickly determine if a database
exists on the server. ::
database_exists('postgres://postgres@localhost/name') #=> False
create_database('postgres://postgres@localhost/name')
database_exists('postgres://postgres@localhost/name') #=> True
Supports checking against a constructed URL as well. ::
engine = create_engine('postgres://postgres@localhost/name')
database_exists(engine.url) #=> False
create_database(engine.url)
database_exists(engine.url) #=> True
Borrowed from SQLAlchemy_Utils (v0.32.14 )since we only need this one function.
"""
url = copy(make_url(url))
database = url.database
if url.drivername.startswith('postgresql'):
url.database = 'template1'
else:
url.database = None
engine = create_engine(url)
if engine.dialect.name == 'postgresql':
text = "SELECT 1 FROM pg_database WHERE datname='{db}'".format(db=database)
return bool(engine.execute(text).scalar())
elif engine.dialect.name == 'mysql':
text = ("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA "
"WHERE SCHEMA_NAME = '{db}'".format(db=database))
return bool(engine.execute(text).scalar())
elif engine.dialect.name == 'sqlite':
if database:
return database == ':memory:' or os.path.exists(database)
else:
# The default SQLAlchemy database is in memory,
# and :memory is not required, thus we should support that use-case
return True
else:
text = 'SELECT 1'
try:
url.database = database
engine = create_engine(url)
engine.execute(text)
return True
except (ProgrammingError, OperationalError):
return False
def init_db(url, auto_flush=True, auto_commit=False, base=None):
"""
Initialise and return the session and metadata for a database
@ -144,6 +207,12 @@ def upgrade_db(url, upgrade):
:param url: The url of the database to upgrade.
:param upgrade: The python module that contains the upgrade instructions.
"""
if not database_exists(url):
log.warn("Database {db} doesn't exist - skipping upgrade checks".format(db=url))
return (0, 0)
log.debug('Checking upgrades for DB {db}'.format(db=url))
session, metadata = init_db(url)
class Metadata(BaseModel):
@ -160,17 +229,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
if version_meta:
version = int(version_meta.value)
else:
version = upgrade.__version__
# 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__

View File

@ -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

View File

@ -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,8 +121,6 @@ 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.secondary_priority = Image.secondary_priority
Image.secondary_priority += 1

View File

@ -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 - Projector is now available on network. 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.

View File

@ -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))

View File

@ -80,25 +80,8 @@ class PJLink(QtNetwork.QTcpSocket):
projectorNoAuthentication = QtCore.pyqtSignal(str) # PIN set and no authentication needed
projectorReceivedData = QtCore.pyqtSignal() # Notify when received data finished processing
projectorUpdateIcons = QtCore.pyqtSignal() # Update the status icons on toolbar
# New commands available in PJLink Class 2
pjlink_future = [
'ACKN', # UDP Reply to 'SRCH'
'FILT', # Get current filter usage time
'FREZ', # Set freeze/unfreeze picture being projected
'INNM', # Get Video source input terminal name
'IRES', # Get Video source resolution
'LKUP', # UPD Linkup status notification
'MVOL', # Set microphone volume
'RFIL', # Get replacement air filter model number
'RLMP', # Get lamp replacement model number
'RRES', # Get projector recommended video resolution
'SNUM', # Get projector serial number
'SRCH', # UDP broadcast search for available projectors on local network
'SVER', # Get projector software version
'SVOL', # Set speaker volume
'TESTMEONLY' # For testing when other commands have been implemented
]
# New commands available in PJLink Class 2
pjlink_udp_commands = [
'ACKN',
'ERST', # Class 1 or 2
@ -129,13 +112,14 @@ class PJLink(QtNetwork.QTcpSocket):
self.ip = ip
self.port = port
self.pin = pin
super(PJLink, self).__init__()
super().__init__()
self.mac_adx = kwargs.get('mac_adx')
self.dbid = None
self.location = None
self.notes = None
self.dbid = None if 'dbid' not in kwargs else kwargs['dbid']
self.location = None if 'location' not in kwargs else kwargs['location']
self.notes = None if 'notes' not in kwargs else kwargs['notes']
self.dbid = kwargs.get('dbid')
self.location = kwargs.get('location')
self.notes = kwargs.get('notes')
# Poll time 20 seconds unless called with something else
self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000
# Timeout 5 seconds unless called with something else
@ -186,10 +170,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 +440,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 +496,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
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 +589,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 +627,11 @@ class PJLink(QtNetwork.QTcpSocket):
self.send_busy = False
self.projectorReceivedData.emit()
return
if cmd in self.pjlink_functions:
# Command checks already passed
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))
self.send_busy = False
self.projectorReceivedData.emit()
self.pjlink_functions[cmd](data)
def process_lamp(self, data):
"""

View File

@ -0,0 +1,85 @@
# -*- 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 #
###############################################################################
"""
:mod:`openlp.core.lib.projector.pjlink2` module provides the PJLink Class 2
updates from PJLink Class 1.
This module only handles the UDP socket functionality. Command/query/status
change messages will still be processed by the PJLink 1 module.
Currently, the only variance is the addition of a UDP "search" command to
query the local network for Class 2 capable projectors,
and UDP "notify" messages from projectors to connected software of status
changes (i.e., power change, input change, error changes).
Differences between Class 1 and Class 2 PJLink specifications are as follows.
New Functionality:
* Search - UDP Query local network for Class 2 capabable projector(s).
* Status - UDP Status change with connected projector(s). Status change
messages consist of:
* Initial projector power up when network communication becomes available
* Lamp off/standby to warmup or on
* Lamp on to cooldown or off/standby
* Input source select change completed
* Error status change (i.e., fan/lamp/temp/cover open/filter/other error(s))
New Commands:
* Query serial number of projector
* Query version number of projector software
* Query model number of replacement lamp
* Query model number of replacement air filter
* Query current projector screen resolution
* Query recommended screen resolution
* Query name of specific input terminal (video source)
* Adjust projector microphone in 1-step increments
* Adjust projector speacker in 1-step increments
Extended Commands:
* Addition of INTERNAL terminal (video source) for a total of 6 types of terminals.
* Number of terminals (video source) has been expanded from [1-9]
to [1-9a-z] (Addition of 26 terminals for each type of input).
See PJLink Class 2 Specifications for details.
http://pjlink.jbmia.or.jp/english/dl_class2.html
Section 5-1 PJLink Specifications
Section 5-5 Guidelines for Input Terminals
"""
import logging
log = logging.getLogger(__name__)
log.debug('pjlink2 loaded')
from PyQt5 import QtCore, QtNetwork
class PJLinkUDP(QtNetwork.QTcpSocket):
"""
Socket service for handling datagram (UDP) sockets.
"""
log.debug('PJLinkUDP loaded')
# Class varialbe for projector list. Should be replaced by ProjectorManager's
# projector list after being loaded there.
projector_list = None
projectors_found = None # UDP search found list

View File

@ -0,0 +1,76 @@
# -*- 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 Table, Column, types, inspect
from sqlalchemy.exc import NoSuchTableError
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.
"""
log.debug('Skipping upgrade_1 of projector DB - not used')
def upgrade_2(session, metadata):
"""
Version 2 upgrade.
Update Projector() table to include new data defined in PJLink version 2 changes
mac_adx: Column(String(18))
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
"""
projector_table = Table('projector', metadata, autoload=True)
if 'mac_adx' not in [col.name for col in projector_table.c.values()]:
log.debug("Upgrading projector DB to version '2'")
new_op = get_upgrade_op(session)
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")

View File

@ -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()

View File

@ -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)

View File

@ -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']

View File

@ -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):

View File

@ -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

View File

@ -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):

View File

@ -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())
@ -563,7 +562,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))
@ -576,7 +574,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),
@ -589,7 +586,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),

View File

@ -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')

View File

@ -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: '</%s>' % tag, reversed(end_tags)))
return ''.join(map(lambda tag: '</{tag}>'.format(tag=tag), reversed(end_tags)))
def start_tag_changed(self, start_html, end_html):
"""

View File

@ -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()

View File

@ -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

View File

@ -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 = '<span style="font-size:14pt; font-weight:600;">%s</span>'
HeaderStyle = '<span style="font-size:14pt; font-weight:600;">{text}</span>'
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)

View File

@ -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):
"""

View File

@ -920,8 +920,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()
@ -1316,7 +1315,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]),

View File

@ -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

View File

@ -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)')

View File

@ -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()

View File

@ -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)

View File

@ -39,6 +39,7 @@ from openlp.core.lib.projector.constants import ERROR_MSG, ERROR_STRING, E_AUTHE
S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP
from openlp.core.lib.projector.db import ProjectorDB
from openlp.core.lib.projector.pjlink1 import PJLink
from openlp.core.lib.projector.pjlink2 import PJLinkUDP
from openlp.core.ui.projector.editform import ProjectorEditForm
from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle
@ -290,6 +291,8 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjecto
self.settings_section = 'projector'
self.projectordb = projectordb
self.projector_list = []
self.pjlink_udp = PJLinkUDP()
self.pjlink_udp.projector_list = self.projector_list
self.source_select_form = None
def bootstrap_initialise(self):
@ -662,6 +665,20 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjecto
message = '%s<b>%s</b>: %s<br />' % (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 += '<b>{title}</b>: {data}<br /><br />'.format(title=translate('OpenLP.ProjectorManager',
'Serial Number'),
data=projector.serial_no)
message += '<b>{title}</b>: {data}<br /><br />'.format(title=translate('OpenLP.ProjectorManager',
'Software Version'),
data=projector.sw_version)
message += '<b>{title}</b>: {data}<br /><br />'.format(title=translate('OpenLP.ProjectorManager',
'Lamp type'),
data=projector.model_lamp)
message += '<b>{title}</b>: {data}<br /><br />'.format(title=translate('OpenLP.ProjectorManager',
'Filter type'),
data=projector.model_filter)
count = 1
for item in projector.link.lamp:
message += '<b>{title} {count}</b> {status} '.format(title=translate('OpenLP.ProjectorManager',
@ -973,7 +990,7 @@ class ProjectorItem(QtCore.QObject):
self.poll_time = None
self.socket_timeout = None
self.status = S_NOT_CONNECTED
super(ProjectorItem, self).__init__()
super().__init__()
def not_implemented(function):

View File

@ -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')

View File

@ -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)

View File

@ -66,6 +66,12 @@ class ServiceManagerList(QtWidgets.QTreeWidget):
elif event.key() == QtCore.Qt.Key_Down:
self.service_manager.on_move_selection_down()
event.accept()
elif event.key() == QtCore.Qt.Key_Right:
self.service_manager.on_expand_selection()
event.accept()
elif event.key() == QtCore.Qt.Key_Left:
self.service_manager.on_collapse_selection()
event.accept()
elif event.key() == QtCore.Qt.Key_Delete:
self.service_manager.on_delete_from_service()
event.accept()
@ -1119,6 +1125,35 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
return
self.service_manager_list.setCurrentItem(item_after)
def on_expand_selection(self):
"""
Expands cursor selection on the window. Called by the right arrow
"""
item = self.service_manager_list.currentItem()
# Since we only have 2 levels we find them by checking for children
if item.childCount():
if not self.service_manager_list.isExpanded(self.service_manager_list.currentIndex()):
self.service_manager_list.expandItem(item)
self.service_manager.expanded(item)
# If not expanded, Expand it
self.service_manager_list.setCurrentItem(self.service_manager_list.itemBelow(item))
# Then move selection down to child whether it needed to be expanded or not
def on_collapse_selection(self):
"""
Collapses cursor selection on the window Called by the left arrow
"""
item = self.service_manager_list.currentItem()
# Since we only have 2 levels we find them by checking for children
if item.childCount():
if self.service_manager_list.isExpanded(self.service_manager_list.currentIndex()):
self.service_manager_list.collapseItem(item)
self.service_manager.collapsed(item)
else: # If selection is lower level
self.service_manager_list.collapseItem(item.parent())
self.service_manager.collapsed(item.parent())
self.service_manager_list.setCurrentItem(item.parent())
def on_collapse_all(self, field=None):
"""
Collapse all the service items.

View File

@ -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()

View File

@ -46,7 +46,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)

View File

@ -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='')

View File

@ -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):

View File

@ -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

View File

@ -88,21 +88,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 = """
@ -228,8 +227,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():

View File

@ -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

View File

@ -38,7 +38,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,

View File

@ -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 '

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

@ -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: (<chapter>:)?<verse>(-((<chapter>:)?<verse>|end)?)?
# TODO: Check before converting this string
range_regex = '(?:(?P<from_chapter>[0-9]+)%(sep_v)s)?' \
'(?P<from_verse>[0-9]+)(?P<range_to>%(sep_r)s(?:(?:(?P<to_chapter>' \
'[0-9]+)%(sep_v)s)?(?P<to_verse>[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<from_chapter>[0-9]+){sep_v})?' \
'(?P<from_verse>[0-9]+)(?P<range_to>{sep_r}(?:(?:(?P<to_chapter>' \
'[0-9]+){sep_v})?(?P<to_verse>[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: <book>(<range>(,(?!$)|(?=$)))+
REFERENCE_MATCHES['full'] = \
re.compile('^\s*(?!\s)(?P<book>[\d]*[^\d\.]+)\.*(?<!\s)\s*'
'(?P<ranges>(?:%(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<book>[\d]*[.]?[^\d\.]+)\.*(?<!\s)\s*'
r'(?P<ranges>(?:{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<book>[\d]*[^\d]+)(?<!\s)\s*``
The ``book`` group starts with the first non-whitespace character. There are optional leading digits followed by
non-digits. The group ends before the whitspace, or a full stop in front of the next digit.
non-digits. The group ends before the whitespace, or a full stop in front of the next digit.
``(?P<ranges>(?:%(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

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -40,7 +40,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
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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.

View File

@ -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

View File

@ -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'),

View File

@ -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

View File

@ -122,5 +122,4 @@ class RemotesPlugin(Plugin):
translate('RemotePlugin', 'Server Config Change'),
translate('RemotePlugin',
'Server configuration changes will require a restart '
'to take effect.'),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
'to take effect.'))

View File

@ -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)

View File

@ -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):
"""

View File

@ -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())

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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 '
'<strong>OpenLyrics </strong> worship song format.'))

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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):

View File

@ -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',

View File

@ -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()

View File

@ -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()

View File

@ -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).

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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 = ''

View File

@ -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))

View File

@ -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))

View File

@ -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 <comment> 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 <br/> 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'

View File

@ -52,6 +52,7 @@ def upgrade_1(session, metadata):
:param metadata:
"""
op = get_upgrade_op(session)
metadata.reflect()
if 'media_files_songs' in [t.name for t in metadata.tables.values()]:
op.drop_table('media_files_songs')
op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
@ -122,6 +123,7 @@ def upgrade_6(session, metadata):
This version corrects the errors in upgrades 4 and 5
"""
op = get_upgrade_op(session)
metadata.reflect()
# Move upgrade 4 to here and correct it (authors_songs table, not songs table)
authors_songs = Table('authors_songs', metadata, autoload=True)
if 'author_type' not in [col.name for col in authors_songs.c.values()]:

View File

@ -54,7 +54,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,

View File

@ -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)

View File

@ -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)

View File

@ -25,17 +25,26 @@ backend for the SongsUsage plugin
"""
import logging
from sqlalchemy import Column, types
from sqlalchemy import Table, Column, types
from openlp.core.lib.db import get_upgrade_op
log = logging.getLogger(__name__)
__version__ = 1
__version__ = 2
def upgrade_1(session, metadata):
"""
Version 1 upgrade.
Version 1 upgrade
Skip due to possible missed update from a 2.4-2.6 upgrade
"""
pass
def upgrade_2(session, metadata):
"""
Version 2 upgrade.
This upgrade adds two new fields to the songusage database
@ -43,5 +52,7 @@ def upgrade_1(session, metadata):
:param metadata: SQLAlchemy MetaData object
"""
op = get_upgrade_op(session)
songusage_table = Table('songusage_data', metadata, autoload=True)
if 'plugin_name' not in [col.name for col in songusage_table.c.values()]:
op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default=''))
op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))

View File

@ -23,6 +23,9 @@
Package to test the openlp.core.lib package.
"""
import os
import shutil
from tempfile import mkdtemp
from unittest import TestCase
from unittest.mock import patch, MagicMock
@ -30,13 +33,27 @@ from sqlalchemy.pool import NullPool
from sqlalchemy.orm.scoping import ScopedSession
from sqlalchemy import MetaData
from openlp.core.lib.db import init_db, get_upgrade_op, delete_database
from openlp.core.lib.db import init_db, get_upgrade_op, delete_database, upgrade_db
from openlp.core.lib.projector import upgrade as pjlink_upgrade
class TestDB(TestCase):
"""
A test case for all the tests for the :mod:`~openlp.core.lib.db` module.
"""
def setUp(self):
"""
Set up anything necessary for all tests
"""
self.tmp_folder = mkdtemp(prefix='openlp_')
def tearDown(self):
"""
Clean up
"""
# Ignore errors since windows can have problems with locked files
shutil.rmtree(self.tmp_folder, ignore_errors=True)
def test_init_db_calls_correct_functions(self):
"""
Test that the init_db function makes the correct function calls
@ -145,3 +162,17 @@ class TestDB(TestCase):
MockedAppLocation.get_section_data_path.assert_called_with(test_plugin)
mocked_delete_file.assert_called_with(test_location)
self.assertFalse(result, 'The result of delete_file should be False (was rigged that way)')
@patch('tests.functional.openlp_core_lib.test_db.pjlink_upgrade')
def test_skip_db_upgrade_with_no_database(self, mocked_upgrade):
"""
Test the upgrade_db function does not try to update a missing database
"""
# GIVEN: Database URL that does not (yet) exist
url = 'sqlite:///{tmp}/test_db.sqlite'.format(tmp=self.tmp_folder)
# WHEN: We attempt to upgrade a non-existant database
upgrade_db(url, pjlink_upgrade)
# THEN: upgrade should NOT have been called
self.assertFalse(mocked_upgrade.called, 'Database upgrade function should NOT have been called')

View File

@ -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
"""

View File

@ -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):
"""

View File

@ -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')

View File

@ -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):

View File

@ -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()

View File

@ -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):
"""

View File

@ -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')

View File

@ -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'),

View File

@ -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,7 +477,7 @@ 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)
@ -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)
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

View File

@ -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.

Some files were not shown because too many files have changed in this diff Show More