forked from openlp/openlp
merge trunk
This commit is contained in:
commit
a7daffe54f
@ -251,8 +251,7 @@ class OpenLP(OpenLPMixin, QtWidgets.QApplication):
|
|||||||
if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'),
|
if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'),
|
||||||
translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n'
|
translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n'
|
||||||
'a backup of the old data folder?'),
|
'a backup of the old data folder?'),
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
||||||
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
|
||||||
# Create copy of data folder
|
# Create copy of data folder
|
||||||
data_folder_path = AppLocation.get_data_path()
|
data_folder_path = AppLocation.get_data_path()
|
||||||
timestamp = time.strftime("%Y%m%d-%H%M%S")
|
timestamp = time.strftime("%Y%m%d-%H%M%S")
|
||||||
|
@ -140,8 +140,8 @@ class LanguageManager(object):
|
|||||||
reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm")
|
reg_ex = QtCore.QRegExp("^.*i18n/(.*).qm")
|
||||||
if reg_ex.exactMatch(qmf):
|
if reg_ex.exactMatch(qmf):
|
||||||
name = '{regex}'.format(regex=reg_ex.cap(1))
|
name = '{regex}'.format(regex=reg_ex.cap(1))
|
||||||
# TODO: Test before converting to python3 string format
|
LanguageManager.__qm_list__[
|
||||||
LanguageManager.__qm_list__['%#2i %s' % (counter + 1, LanguageManager.language_name(qmf))] = name
|
'{count:>2i} {name}'.format(count=counter + 1, name=LanguageManager.language_name(qmf))] = name
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_qm_list():
|
def get_qm_list():
|
||||||
|
@ -219,7 +219,11 @@ class Settings(QtCore.QSettings):
|
|||||||
('shortcuts/offlineHelpItem', 'shortcuts/userManualItem', []), # Online and Offline help were combined in 2.6.
|
('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.
|
('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/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
|
@staticmethod
|
||||||
|
@ -154,8 +154,6 @@ class UiStrings(object):
|
|||||||
self.Split = translate('OpenLP.Ui', 'Optional &Split')
|
self.Split = translate('OpenLP.Ui', 'Optional &Split')
|
||||||
self.SplitToolTip = translate('OpenLP.Ui',
|
self.SplitToolTip = translate('OpenLP.Ui',
|
||||||
'Split a slide into two only if it does not fit on the screen as one slide.')
|
'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.StopPlaySlidesInLoop = translate('OpenLP.Ui', 'Stop Play Slides in Loop')
|
||||||
self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End')
|
self.StopPlaySlidesToEnd = translate('OpenLP.Ui', 'Stop Play Slides to End')
|
||||||
self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular')
|
self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular')
|
||||||
|
@ -25,12 +25,15 @@ The :mod:`db` module provides the core database functionality for OpenLP
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from copy import copy
|
||||||
from urllib.parse import quote_plus as urlquote
|
from urllib.parse import quote_plus as urlquote
|
||||||
|
|
||||||
from sqlalchemy import Table, MetaData, Column, types, create_engine
|
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.orm import scoped_session, sessionmaker, mapper
|
||||||
from sqlalchemy.pool import NullPool
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
from alembic.migration import MigrationContext
|
from alembic.migration import MigrationContext
|
||||||
from alembic.operations import Operations
|
from alembic.operations import Operations
|
||||||
|
|
||||||
@ -40,6 +43,66 @@ from openlp.core.lib.ui import critical_error_message_box
|
|||||||
log = logging.getLogger(__name__)
|
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):
|
def init_db(url, auto_flush=True, auto_commit=False, base=None):
|
||||||
"""
|
"""
|
||||||
Initialise and return the session and metadata for a database
|
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 url: The url of the database to upgrade.
|
||||||
:param upgrade: The python module that contains the upgrade instructions.
|
: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)
|
session, metadata = init_db(url)
|
||||||
|
|
||||||
class Metadata(BaseModel):
|
class Metadata(BaseModel):
|
||||||
@ -160,17 +229,15 @@ def upgrade_db(url, upgrade):
|
|||||||
metadata_table.create(checkfirst=True)
|
metadata_table.create(checkfirst=True)
|
||||||
mapper(Metadata, metadata_table)
|
mapper(Metadata, metadata_table)
|
||||||
version_meta = session.query(Metadata).get('version')
|
version_meta = session.query(Metadata).get('version')
|
||||||
if version_meta is None:
|
if version_meta:
|
||||||
# Tables have just been created - fill the version field with the most recent version
|
version = int(version_meta.value)
|
||||||
if session.query(Metadata).get('dbversion'):
|
|
||||||
version = 0
|
|
||||||
else:
|
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)
|
version_meta = Metadata.populate(key='version', value=version)
|
||||||
session.add(version_meta)
|
session.add(version_meta)
|
||||||
session.commit()
|
session.commit()
|
||||||
else:
|
|
||||||
version = int(version_meta.value)
|
|
||||||
if version > upgrade.__version__:
|
if version > upgrade.__version__:
|
||||||
session.remove()
|
session.remove()
|
||||||
return version, upgrade.__version__
|
return version, upgrade.__version__
|
||||||
|
@ -24,7 +24,6 @@ The :mod:`~openlp.core.lib.exceptions` module contains custom exceptions
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# TODO: Test __init__ & __str__
|
|
||||||
class ValidationError(Exception):
|
class ValidationError(Exception):
|
||||||
"""
|
"""
|
||||||
The :class:`~openlp.core.lib.exceptions.ValidationError` exception provides a custom exception for validating
|
The :class:`~openlp.core.lib.exceptions.ValidationError` exception provides a custom exception for validating
|
||||||
|
@ -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 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.
|
: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.path = path
|
||||||
self.image = None
|
self.image = None
|
||||||
self.image_bytes = None
|
self.image_bytes = None
|
||||||
@ -119,8 +121,6 @@ class Image(object):
|
|||||||
self.timestamp = 0
|
self.timestamp = 0
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
# FIXME: We assume that the path exist. The caller has to take care that it exists!
|
|
||||||
if os.path.exists(path):
|
|
||||||
self.timestamp = os.stat(path).st_mtime
|
self.timestamp = os.stat(path).st_mtime
|
||||||
self.secondary_priority = Image.secondary_priority
|
self.secondary_priority = Image.secondary_priority
|
||||||
Image.secondary_priority += 1
|
Image.secondary_priority += 1
|
||||||
|
@ -57,35 +57,115 @@ LF = chr(0x0A) # \n
|
|||||||
PJLINK_PORT = 4352
|
PJLINK_PORT = 4352
|
||||||
TIMEOUT = 30.0
|
TIMEOUT = 30.0
|
||||||
PJLINK_MAX_PACKET = 136
|
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 = {
|
PJLINK_VALID_CMD = {
|
||||||
'ACKN': ['2', ], # UDP Reply to 'SRCH'
|
'ACKN': {'version': ['2', ],
|
||||||
'AVMT': ['1', ], # Shutter option
|
'description': translate('OpenLP.PJLinkConstants',
|
||||||
'CLSS': ['1', ], # PJLink class support query
|
'Acknowledge a PJLink SRCH command - returns MAC address.')
|
||||||
'ERST': ['1', '2'], # Error status option
|
},
|
||||||
'FILT': ['2', ], # Get current filter usage time
|
'AVMT': {'version': ['1', ],
|
||||||
'FREZ': ['2', ], # Set freeze/unfreeze picture being projected
|
'description': translate('OpenLP.PJLinkConstants',
|
||||||
'INF1': ['1', ], # Manufacturer name query
|
'Blank/unblank video and/or mute audio.')
|
||||||
'INF2': ['1', ], # Product name query
|
},
|
||||||
'INFO': ['1', ], # Other information query
|
'CLSS': {'version': ['1', ],
|
||||||
'INNM': ['2', ], # Get Video source input terminal name
|
'description': translate('OpenLP.PJLinkConstants',
|
||||||
'INPT': ['1', ], # Video sources option
|
'Query projector PJLink class support.')
|
||||||
'INST': ['1', ], # Input sources available query
|
},
|
||||||
'IRES': ['2', ], # Get Video source resolution
|
'ERST': {'version': ['1', '2'],
|
||||||
'LAMP': ['1', ], # Lamp(s) query (Includes fans)
|
'description': translate('OpenLP.PJLinkConstants',
|
||||||
'LKUP': ['2', ], # UPD Linkup status notification
|
'Query error status from projector. '
|
||||||
'MVOL': ['2', ], # Set microphone volume
|
'Returns fan/lamp/temp/cover/filter/other error status.')
|
||||||
'NAME': ['1', ], # Projector name query
|
},
|
||||||
'PJLINK': ['1', ], # Initial connection
|
'FILT': {'version': ['2', ],
|
||||||
'POWR': ['1', ], # Power option
|
'description': translate('OpenLP.PJLinkConstants',
|
||||||
'RFIL': ['2', ], # Get replacement air filter model number
|
'Query number of hours on filter.')
|
||||||
'RLMP': ['2', ], # Get lamp replacement model number
|
},
|
||||||
'RRES': ['2', ], # Get projector recommended video resolution
|
'FREZ': {'version': ['2', ],
|
||||||
'SNUM': ['2', ], # Get projector serial number
|
'description': translate('OpenLP.PJLinkConstants',
|
||||||
'SRCH': ['2', ], # UDP broadcast search for available projectors on local network
|
'Freeze or unfreeze current image being projected.')
|
||||||
'SVER': ['2', ], # Get projector software version
|
},
|
||||||
'SVOL': ['2', ] # Set speaker volume
|
'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
|
# Error and status codes
|
||||||
S_OK = E_OK = 0 # E_OK included since I sometimes forget
|
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.
|
# Error codes. Start at 200 so we don't duplicate system error codes.
|
||||||
|
@ -44,6 +44,7 @@ from sqlalchemy.orm import relationship
|
|||||||
|
|
||||||
from openlp.core.lib.db import Manager, init_db, init_url
|
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.constants import PJLINK_DEFAULT_CODES
|
||||||
|
from openlp.core.lib.projector import upgrade
|
||||||
|
|
||||||
Base = declarative_base(MetaData())
|
Base = declarative_base(MetaData())
|
||||||
|
|
||||||
@ -166,13 +167,14 @@ class Projector(CommonBase, Base):
|
|||||||
"""
|
"""
|
||||||
Return basic representation of Source table entry.
|
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}", ' \
|
'location="{location}", notes="{notes}", pjlink_name="{pjlink_name}", ' \
|
||||||
'manufacturer="{manufacturer}", model="{model}", serial_no="{serial}", other="{other}", ' \
|
'manufacturer="{manufacturer}", model="{model}", serial_no="{serial}", other="{other}", ' \
|
||||||
'sources="{sources}", source_list="{source_list}", model_filter="{mfilter}", ' \
|
'sources="{sources}", source_list="{source_list}", model_filter="{mfilter}", ' \
|
||||||
'model_lamp="{mlamp}", sw_version="{sw_ver}") >'.format(data=self.id,
|
'model_lamp="{mlamp}", sw_version="{sw_ver}") >'.format(data=self.id,
|
||||||
ip=self.ip,
|
ip=self.ip,
|
||||||
port=self.port,
|
port=self.port,
|
||||||
|
mac=self.mac_adx,
|
||||||
pin=self.pin,
|
pin=self.pin,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
location=self.location,
|
location=self.location,
|
||||||
@ -189,6 +191,7 @@ class Projector(CommonBase, Base):
|
|||||||
sw_ver=self.sw_version)
|
sw_ver=self.sw_version)
|
||||||
ip = Column(String(100))
|
ip = Column(String(100))
|
||||||
port = Column(String(8))
|
port = Column(String(8))
|
||||||
|
mac_adx = Column(String(18))
|
||||||
pin = Column(String(20))
|
pin = Column(String(20))
|
||||||
name = Column(String(20))
|
name = Column(String(20))
|
||||||
location = Column(String(30))
|
location = Column(String(30))
|
||||||
@ -243,7 +246,9 @@ class ProjectorDB(Manager):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
log.debug('ProjectorDB().__init__(args="{arg}", kwargs="{kwarg}")'.format(arg=args, kwarg=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('ProjectorDB() Initialized using db url {db}'.format(db=self.db_url))
|
||||||
log.debug('Session: {session}'.format(session=self.session))
|
log.debug('Session: {session}'.format(session=self.session))
|
||||||
|
|
||||||
|
@ -80,25 +80,8 @@ class PJLink(QtNetwork.QTcpSocket):
|
|||||||
projectorNoAuthentication = QtCore.pyqtSignal(str) # PIN set and no authentication needed
|
projectorNoAuthentication = QtCore.pyqtSignal(str) # PIN set and no authentication needed
|
||||||
projectorReceivedData = QtCore.pyqtSignal() # Notify when received data finished processing
|
projectorReceivedData = QtCore.pyqtSignal() # Notify when received data finished processing
|
||||||
projectorUpdateIcons = QtCore.pyqtSignal() # Update the status icons on toolbar
|
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 = [
|
pjlink_udp_commands = [
|
||||||
'ACKN',
|
'ACKN',
|
||||||
'ERST', # Class 1 or 2
|
'ERST', # Class 1 or 2
|
||||||
@ -129,13 +112,14 @@ class PJLink(QtNetwork.QTcpSocket):
|
|||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.port = port
|
self.port = port
|
||||||
self.pin = pin
|
self.pin = pin
|
||||||
super(PJLink, self).__init__()
|
super().__init__()
|
||||||
|
self.mac_adx = kwargs.get('mac_adx')
|
||||||
self.dbid = None
|
self.dbid = None
|
||||||
self.location = None
|
self.location = None
|
||||||
self.notes = None
|
self.notes = None
|
||||||
self.dbid = None if 'dbid' not in kwargs else kwargs['dbid']
|
self.dbid = kwargs.get('dbid')
|
||||||
self.location = None if 'location' not in kwargs else kwargs['location']
|
self.location = kwargs.get('location')
|
||||||
self.notes = None if 'notes' not in kwargs else kwargs['notes']
|
self.notes = kwargs.get('notes')
|
||||||
# Poll time 20 seconds unless called with something else
|
# Poll time 20 seconds unless called with something else
|
||||||
self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000
|
self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000
|
||||||
# Timeout 5 seconds unless called with something else
|
# Timeout 5 seconds unless called with something else
|
||||||
@ -186,10 +170,15 @@ class PJLink(QtNetwork.QTcpSocket):
|
|||||||
self.pjlink_name = None
|
self.pjlink_name = None
|
||||||
self.manufacturer = None
|
self.manufacturer = None
|
||||||
self.model = None
|
self.model = None
|
||||||
|
self.serial_no = None
|
||||||
|
self.sw_version = None
|
||||||
self.shutter = None
|
self.shutter = None
|
||||||
self.mute = None
|
self.mute = None
|
||||||
self.lamp = None
|
self.lamp = None
|
||||||
|
self.model_lamp = None
|
||||||
self.fan = None
|
self.fan = None
|
||||||
|
self.filter_time = None
|
||||||
|
self.model_filter = None
|
||||||
self.source_available = None
|
self.source_available = None
|
||||||
self.source = None
|
self.source = None
|
||||||
self.other_info = None
|
self.other_info = None
|
||||||
@ -451,18 +440,18 @@ class PJLink(QtNetwork.QTcpSocket):
|
|||||||
return
|
return
|
||||||
data_split = data.split('=')
|
data_split = data.split('=')
|
||||||
try:
|
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:
|
except ValueError as e:
|
||||||
log.warning('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip))
|
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()))
|
log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip()))
|
||||||
self.change_status(E_INVALID_DATA)
|
self.change_status(E_INVALID_DATA)
|
||||||
self.receive_data_signal()
|
self.receive_data_signal()
|
||||||
return
|
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))
|
log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
|
||||||
self.receive_data_signal()
|
self.receive_data_signal()
|
||||||
return
|
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 '
|
log.warn('({ip}) get_data(): Projector returned class reply higher '
|
||||||
'than projector stated class'.format(ip=self.ip))
|
'than projector stated class'.format(ip=self.ip))
|
||||||
return self.process_command(cmd, data)
|
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))
|
log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
|
||||||
self.send_queue = []
|
self.send_queue = []
|
||||||
return
|
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)
|
self.projectorNetwork.emit(S_NETWORK_SENDING)
|
||||||
log.debug('({ip}) send_command(): Building cmd="{command}" opts="{data}"{salt}'.format(ip=self.ip,
|
log.debug('({ip}) send_command(): Building cmd="{command}" opts="{data}"{salt}'.format(ip=self.ip,
|
||||||
command=cmd,
|
command=cmd,
|
||||||
data=opts,
|
data=opts,
|
||||||
salt='' if salt is None
|
salt='' if salt is None
|
||||||
else ' with hash'))
|
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)
|
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,
|
out = '{salt}{header}{command} {options}{suffix}'.format(salt="" if salt is None else salt,
|
||||||
header=header,
|
header=header,
|
||||||
command=cmd,
|
command=cmd,
|
||||||
@ -589,10 +589,13 @@ class PJLink(QtNetwork.QTcpSocket):
|
|||||||
cmd=cmd,
|
cmd=cmd,
|
||||||
data=data))
|
data=data))
|
||||||
# Check if we have a future command not available yet
|
# Check if we have a future command not available yet
|
||||||
if cmd in self.pjlink_future:
|
if cmd not in PJLINK_VALID_CMD:
|
||||||
self._not_implemented(cmd)
|
log.error('({ip}) Unknown command received - ignoring'.format(ip=self.ip))
|
||||||
return
|
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
|
# Oops - projector error
|
||||||
log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data))
|
log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data))
|
||||||
if data.upper() == 'ERRA':
|
if data.upper() == 'ERRA':
|
||||||
@ -624,14 +627,11 @@ class PJLink(QtNetwork.QTcpSocket):
|
|||||||
self.send_busy = False
|
self.send_busy = False
|
||||||
self.projectorReceivedData.emit()
|
self.projectorReceivedData.emit()
|
||||||
return
|
return
|
||||||
|
# Command checks already passed
|
||||||
if cmd in self.pjlink_functions:
|
|
||||||
log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd))
|
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.send_busy = False
|
||||||
self.projectorReceivedData.emit()
|
self.projectorReceivedData.emit()
|
||||||
|
self.pjlink_functions[cmd](data)
|
||||||
|
|
||||||
def process_lamp(self, data):
|
def process_lamp(self, data):
|
||||||
"""
|
"""
|
||||||
|
85
openlp/core/lib/projector/pjlink2.py
Normal file
85
openlp/core/lib/projector/pjlink2.py
Normal 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
|
76
openlp/core/lib/projector/upgrade.py
Normal file
76
openlp/core/lib/projector/upgrade.py
Normal 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")
|
@ -105,7 +105,7 @@ class SearchEdit(QtWidgets.QLineEdit):
|
|||||||
self.setPlaceholderText(action.placeholder_text)
|
self.setPlaceholderText(action.placeholder_text)
|
||||||
self.menu_button.setDefaultAction(action)
|
self.menu_button.setDefaultAction(action)
|
||||||
self._current_search_type = identifier
|
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)
|
self.searchTypeChanged.emit(identifier)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ class SearchEdit(QtWidgets.QLineEdit):
|
|||||||
self.menu_button.resize(QtCore.QSize(28, 18))
|
self.menu_button.resize(QtCore.QSize(28, 18))
|
||||||
self.menu_button.setMenu(menu)
|
self.menu_button.setMenu(menu)
|
||||||
self.set_current_search_type(
|
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.menu_button.show()
|
||||||
self._update_style_sheet()
|
self._update_style_sheet()
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ def add_welcome_page(parent, image):
|
|||||||
parent.title_label = QtWidgets.QLabel(parent.welcome_page)
|
parent.title_label = QtWidgets.QLabel(parent.welcome_page)
|
||||||
parent.title_label.setObjectName('title_label')
|
parent.title_label.setObjectName('title_label')
|
||||||
parent.welcome_layout.addWidget(parent.title_label)
|
parent.welcome_layout.addWidget(parent.title_label)
|
||||||
|
parent.title_label.setWordWrap(True)
|
||||||
parent.welcome_layout.addSpacing(40)
|
parent.welcome_layout.addSpacing(40)
|
||||||
parent.information_label = QtWidgets.QLabel(parent.welcome_page)
|
parent.information_label = QtWidgets.QLabel(parent.welcome_page)
|
||||||
parent.information_label.setWordWrap(True)
|
parent.information_label.setWordWrap(True)
|
||||||
|
@ -99,7 +99,7 @@ from .themelayoutform import ThemeLayoutForm
|
|||||||
from .themeform import ThemeForm
|
from .themeform import ThemeForm
|
||||||
from .filerenameform import FileRenameForm
|
from .filerenameform import FileRenameForm
|
||||||
from .starttimeform import StartTimeForm
|
from .starttimeform import StartTimeForm
|
||||||
from .maindisplay import MainDisplay, Display
|
from .maindisplay import MainDisplay, Display, AudioPlayer
|
||||||
from .servicenoteform import ServiceNoteForm
|
from .servicenoteform import ServiceNoteForm
|
||||||
from .serviceitemeditform import ServiceItemEditForm
|
from .serviceitemeditform import ServiceItemEditForm
|
||||||
from .slidecontroller import SlideController, DisplayController, PreviewController, LiveController
|
from .slidecontroller import SlideController, DisplayController, PreviewController, LiveController
|
||||||
@ -120,8 +120,8 @@ from .projector.tab import ProjectorTab
|
|||||||
from .projector.editform import ProjectorEditForm
|
from .projector.editform import ProjectorEditForm
|
||||||
|
|
||||||
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeForm',
|
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeForm',
|
||||||
'ThemeManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm',
|
'ThemeManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'Display', 'AudioPlayer',
|
||||||
'Display', 'ServiceNoteForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay',
|
'ServiceNoteForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay',
|
||||||
'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
|
'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
|
||||||
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'SingleColumnTableWidget',
|
'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'SingleColumnTableWidget',
|
||||||
'ProjectorManager', 'ProjectorTab', 'ProjectorEditForm']
|
'ProjectorManager', 'ProjectorTab', 'ProjectorEditForm']
|
||||||
|
@ -40,7 +40,8 @@ class AboutForm(QtWidgets.QDialog, UiAboutDialog):
|
|||||||
"""
|
"""
|
||||||
Do some initialisation stuff
|
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()
|
self._setup()
|
||||||
|
|
||||||
def _setup(self):
|
def _setup(self):
|
||||||
|
@ -495,9 +495,7 @@ class AdvancedTab(SettingsTab):
|
|||||||
'location of the OpenLP data directory to:\n\n{path}'
|
'location of the OpenLP data directory to:\n\n{path}'
|
||||||
'\n\nThe data directory will be changed when OpenLP is '
|
'\n\nThe data directory will be changed when OpenLP is '
|
||||||
'closed.').format(path=new_data_path),
|
'closed.').format(path=new_data_path),
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
|
defaultButton=QtWidgets.QMessageBox.No)
|
||||||
QtWidgets.QMessageBox.No),
|
|
||||||
QtWidgets.QMessageBox.No)
|
|
||||||
if answer != QtWidgets.QMessageBox.Yes:
|
if answer != QtWidgets.QMessageBox.Yes:
|
||||||
self.data_directory_path_edit.path = AppLocation.get_data_path()
|
self.data_directory_path_edit.path = AppLocation.get_data_path()
|
||||||
return
|
return
|
||||||
|
@ -38,8 +38,8 @@ class FileRenameForm(QtWidgets.QDialog, Ui_FileRenameDialog, RegistryProperties)
|
|||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
"""
|
"""
|
||||||
super(FileRenameForm, self).__init__(Registry().get('main_window'),
|
super(FileRenameForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint |
|
||||||
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
|
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
|
||||||
self._setup()
|
self._setup()
|
||||||
|
|
||||||
def _setup(self):
|
def _setup(self):
|
||||||
|
@ -206,7 +206,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.update_screen_list_combo()
|
self.update_screen_list_combo()
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
# TODO: Tested at home
|
|
||||||
self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading {name}...')
|
self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading {name}...')
|
||||||
if self.has_run_wizard:
|
if self.has_run_wizard:
|
||||||
self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active())
|
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)
|
item = self.songs_list_widget.item(i)
|
||||||
if item.checkState() == QtCore.Qt.Checked:
|
if item.checkState() == QtCore.Qt.Checked:
|
||||||
filename, sha256 = item.data(QtCore.Qt.UserRole)
|
filename, sha256 = item.data(QtCore.Qt.UserRole)
|
||||||
# TODO: Tested at home
|
|
||||||
self._increment_progress_bar(self.downloading.format(name=filename), 0)
|
self._increment_progress_bar(self.downloading.format(name=filename), 0)
|
||||||
self.previous_size = 0
|
self.previous_size = 0
|
||||||
destination = os.path.join(songs_destination, str(filename))
|
destination = os.path.join(songs_destination, str(filename))
|
||||||
@ -576,7 +574,6 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
item = bibles_iterator.value()
|
item = bibles_iterator.value()
|
||||||
if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
|
if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
|
||||||
bible, sha256 = item.data(0, QtCore.Qt.UserRole)
|
bible, sha256 = item.data(0, QtCore.Qt.UserRole)
|
||||||
# TODO: Tested at home
|
|
||||||
self._increment_progress_bar(self.downloading.format(name=bible), 0)
|
self._increment_progress_bar(self.downloading.format(name=bible), 0)
|
||||||
self.previous_size = 0
|
self.previous_size = 0
|
||||||
if not url_get_file(self, '{path}{name}'.format(path=self.bibles_url, name=bible),
|
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)
|
item = self.themes_list_widget.item(i)
|
||||||
if item.checkState() == QtCore.Qt.Checked:
|
if item.checkState() == QtCore.Qt.Checked:
|
||||||
theme, sha256 = item.data(QtCore.Qt.UserRole)
|
theme, sha256 = item.data(QtCore.Qt.UserRole)
|
||||||
# TODO: Tested at home
|
|
||||||
self._increment_progress_bar(self.downloading.format(name=theme), 0)
|
self._increment_progress_bar(self.downloading.format(name=theme), 0)
|
||||||
self.previous_size = 0
|
self.previous_size = 0
|
||||||
if not url_get_file(self, '{path}{name}'.format(path=self.themes_url, name=theme),
|
if not url_get_file(self, '{path}{name}'.format(path=self.themes_url, name=theme),
|
||||||
|
@ -37,7 +37,8 @@ class FirstTimeLanguageForm(QtWidgets.QDialog, Ui_FirstTimeLanguageDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.setupUi(self)
|
||||||
self.qm_list = LanguageManager.get_qm_list()
|
self.qm_list = LanguageManager.get_qm_list()
|
||||||
self.language_combo_box.addItem('Autodetect')
|
self.language_combo_box.addItem('Autodetect')
|
||||||
|
@ -130,8 +130,7 @@ class FormattingTagController(object):
|
|||||||
elif not match.group('empty'):
|
elif not match.group('empty'):
|
||||||
end_tags.append(tag)
|
end_tags.append(tag)
|
||||||
match = self.html_tag_regex.search(start_html, match.end())
|
match = self.html_tag_regex.search(start_html, match.end())
|
||||||
# TODO: Verify format() works with lambda
|
return ''.join(map(lambda tag: '</{tag}>'.format(tag=tag), reversed(end_tags)))
|
||||||
return ''.join(map(lambda tag: '</%s>' % tag, reversed(end_tags)))
|
|
||||||
|
|
||||||
def start_tag_changed(self, start_html, end_html):
|
def start_tag_changed(self, start_html, end_html):
|
||||||
"""
|
"""
|
||||||
|
@ -51,7 +51,8 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.setupUi(self)
|
||||||
self._setup()
|
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, 2).text(),
|
||||||
self.tag_table_widget.item(count, 3).text())
|
self.tag_table_widget.item(count, 3).text())
|
||||||
if error:
|
if error:
|
||||||
QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), error,
|
QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), error)
|
||||||
QtWidgets.QMessageBox.Ok)
|
|
||||||
self.tag_table_widget.selectRow(count)
|
self.tag_table_widget.selectRow(count)
|
||||||
return
|
return
|
||||||
count += 1
|
count += 1
|
||||||
@ -198,6 +198,5 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag
|
|||||||
if tag:
|
if tag:
|
||||||
self.tag_table_widget.setItem(pre_row, 3, QtWidgets.QTableWidgetItem(tag))
|
self.tag_table_widget.setItem(pre_row, 3, QtWidgets.QTableWidgetItem(tag))
|
||||||
if errors:
|
if errors:
|
||||||
QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), errors,
|
QtWidgets.QMessageBox.warning(self, translate('OpenLP.FormattingTagForm', 'Validation Error'), errors)
|
||||||
QtWidgets.QMessageBox.Ok)
|
|
||||||
self.tag_table_widget.resizeRowsToContents()
|
self.tag_table_widget.resizeRowsToContents()
|
||||||
|
@ -48,7 +48,7 @@ class PathEdit(QtWidgets.QWidget):
|
|||||||
:type parent: QWidget or None
|
:type parent: QWidget or None
|
||||||
|
|
||||||
:param dialog_caption: Used to customise the caption in the QFileDialog.
|
: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
|
:param default_path: The default path. This is set as the path when the revert button is clicked
|
||||||
:type default_path: str
|
:type default_path: str
|
||||||
|
@ -25,7 +25,7 @@ The :mod:``wizard`` module provides generic wizard tools for OpenLP.
|
|||||||
import logging
|
import logging
|
||||||
import os
|
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.common import Registry, RegistryProperties, Settings, UiStrings, translate, is_macosx
|
||||||
from openlp.core.lib import build_icon
|
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.
|
# These strings should need a good reason to be retranslated elsewhere.
|
||||||
FinishedImport = translate('OpenLP.Ui', 'Finished import.')
|
FinishedImport = translate('OpenLP.Ui', 'Finished import.')
|
||||||
FormatLabel = translate('OpenLP.Ui', 'Format:')
|
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')
|
Importing = translate('OpenLP.Ui', 'Importing')
|
||||||
ImportingType = translate('OpenLP.Ui', 'Importing "%s"...')
|
ImportingType = translate('OpenLP.Ui', 'Importing "{source}"...')
|
||||||
ImportSelect = translate('OpenLP.Ui', 'Select Import Source')
|
ImportSelect = translate('OpenLP.Ui', 'Select Import Source')
|
||||||
ImportSelectLong = translate('OpenLP.Ui', 'Select the import format and the location to import from.')
|
ImportSelectLong = translate('OpenLP.Ui', 'Select the import format and the location to import from.')
|
||||||
OpenTypeFile = translate('OpenLP.Ui', 'Open %s File')
|
OpenTypeFile = translate('OpenLP.Ui', 'Open {file_type} File')
|
||||||
OpenTypeFolder = translate('OpenLP.Ui', 'Open %s Folder')
|
OpenTypeFolder = translate('OpenLP.Ui', 'Open {folder_name} Folder')
|
||||||
PercentSymbolFormat = translate('OpenLP.Ui', '%p%')
|
PercentSymbolFormat = translate('OpenLP.Ui', '%p%')
|
||||||
Ready = translate('OpenLP.Ui', 'Ready.')
|
Ready = translate('OpenLP.Ui', 'Ready.')
|
||||||
StartingImport = translate('OpenLP.Ui', 'Starting import...')
|
StartingImport = translate('OpenLP.Ui', 'Starting import...')
|
||||||
@ -93,7 +93,10 @@ class OpenLPWizard(QtWidgets.QWizard, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.plugin = plugin
|
||||||
self.with_progress_page = add_progress_page
|
self.with_progress_page = add_progress_page
|
||||||
self.setFixedWidth(640)
|
self.setFixedWidth(640)
|
||||||
|
@ -689,7 +689,7 @@ class AudioPlayer(OpenLPMixin, QtCore.QObject):
|
|||||||
"""
|
"""
|
||||||
Skip forward to the next track in the list
|
Skip forward to the next track in the list
|
||||||
"""
|
"""
|
||||||
self.playerlist.next()
|
self.playlist.next()
|
||||||
|
|
||||||
def go_to(self, index):
|
def go_to(self, index):
|
||||||
"""
|
"""
|
||||||
|
@ -920,8 +920,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
|||||||
QtWidgets.QMessageBox.information(self, translate('OpenLP.MainWindow', 'Import settings'),
|
QtWidgets.QMessageBox.information(self, translate('OpenLP.MainWindow', 'Import settings'),
|
||||||
translate('OpenLP.MainWindow',
|
translate('OpenLP.MainWindow',
|
||||||
'OpenLP will now close. Imported settings will '
|
'OpenLP will now close. Imported settings will '
|
||||||
'be applied the next time you start OpenLP.'),
|
'be applied the next time you start OpenLP.'))
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
|
|
||||||
self.settings_imported = True
|
self.settings_imported = True
|
||||||
self.clean_up()
|
self.clean_up()
|
||||||
QtCore.QCoreApplication.exit()
|
QtCore.QCoreApplication.exit()
|
||||||
@ -1316,7 +1315,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
|||||||
self.recent_files_menu.clear()
|
self.recent_files_menu.clear()
|
||||||
for file_id, filename in enumerate(recent_files_to_display):
|
for file_id, filename in enumerate(recent_files_to_display):
|
||||||
log.debug('Recent file name: {name}'.format(name=filename))
|
log.debug('Recent file name: {name}'.format(name=filename))
|
||||||
# TODO: Should be good
|
|
||||||
action = create_action(self, '',
|
action = create_action(self, '',
|
||||||
text='&{n} {name}'.format(n=file_id + 1,
|
text='&{n} {name}'.format(n=file_id + 1,
|
||||||
name=os.path.splitext(os.path.basename(str(filename)))[0]),
|
name=os.path.splitext(os.path.basename(str(filename)))[0]),
|
||||||
|
@ -468,9 +468,10 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
|
|||||||
player = self.media_players[used_players[0]]
|
player = self.media_players[used_players[0]]
|
||||||
if suffix not in player.video_extensions_list and suffix not in player.audio_extensions_list:
|
if suffix not in player.video_extensions_list and suffix not in player.audio_extensions_list:
|
||||||
# Media could not be loaded correctly
|
# Media could not be loaded correctly
|
||||||
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported Media File'),
|
critical_error_message_box(
|
||||||
translate('MediaPlugin.MediaItem', 'File %s not supported using player %s') %
|
translate('MediaPlugin.MediaItem', 'Unsupported Media File'),
|
||||||
(service_item.get_frame_path(), used_players[0]))
|
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
|
return False
|
||||||
media_data = MediaInfoWrapper.parse(service_item.get_frame_path())
|
media_data = MediaInfoWrapper.parse(service_item.get_frame_path())
|
||||||
# duration returns in milli seconds
|
# duration returns in milli seconds
|
||||||
|
@ -41,7 +41,8 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.active_plugin = None
|
||||||
self.programatic_change = False
|
self.programatic_change = False
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@ -60,7 +61,6 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
|
|||||||
self._clear_details()
|
self._clear_details()
|
||||||
self.programatic_change = True
|
self.programatic_change = True
|
||||||
plugin_list_width = 0
|
plugin_list_width = 0
|
||||||
# TODO: Tested at home
|
|
||||||
for plugin in self.plugin_manager.plugins:
|
for plugin in self.plugin_manager.plugins:
|
||||||
item = QtWidgets.QListWidgetItem(self.plugin_list_widget)
|
item = QtWidgets.QListWidgetItem(self.plugin_list_widget)
|
||||||
# We do this just to make 100% sure the status is an integer as
|
# 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()
|
self.active_plugin.app_startup()
|
||||||
else:
|
else:
|
||||||
self.active_plugin.toggle_status(PluginStatus.Inactive)
|
self.active_plugin.toggle_status(PluginStatus.Inactive)
|
||||||
# TODO: Tested at home
|
|
||||||
status_text = translate('OpenLP.PluginForm', '{name} (Inactive)')
|
status_text = translate('OpenLP.PluginForm', '{name} (Inactive)')
|
||||||
if self.active_plugin.status == PluginStatus.Active:
|
if self.active_plugin.status == PluginStatus.Active:
|
||||||
status_text = translate('OpenLP.PluginForm', '{name} (Active)')
|
status_text = translate('OpenLP.PluginForm', '{name} (Active)')
|
||||||
|
@ -125,8 +125,8 @@ class PrintServiceForm(QtWidgets.QDialog, Ui_PrintServiceDialog, RegistryPropert
|
|||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
"""
|
"""
|
||||||
super(PrintServiceForm, self).__init__(Registry().get('main_window'),
|
super(PrintServiceForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint |
|
||||||
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
|
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
|
||||||
self.printer = QtPrintSupport.QPrinter()
|
self.printer = QtPrintSupport.QPrinter()
|
||||||
self.print_dialog = QtPrintSupport.QPrintDialog(self.printer, self)
|
self.print_dialog = QtPrintSupport.QPrintDialog(self.printer, self)
|
||||||
self.document = QtGui.QTextDocument()
|
self.document = QtGui.QTextDocument()
|
||||||
|
@ -142,7 +142,8 @@ class ProjectorEditForm(QtWidgets.QDialog, Ui_ProjectorEditForm):
|
|||||||
editProjector = QtCore.pyqtSignal(object)
|
editProjector = QtCore.pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, parent=None, projectordb=None):
|
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.projectordb = projectordb
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.button_box.accepted.connect(self.accept_me)
|
self.button_box.accepted.connect(self.accept_me)
|
||||||
|
@ -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
|
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.db import ProjectorDB
|
||||||
from openlp.core.lib.projector.pjlink1 import PJLink
|
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.editform import ProjectorEditForm
|
||||||
from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle
|
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.settings_section = 'projector'
|
||||||
self.projectordb = projectordb
|
self.projectordb = projectordb
|
||||||
self.projector_list = []
|
self.projector_list = []
|
||||||
|
self.pjlink_udp = PJLinkUDP()
|
||||||
|
self.pjlink_udp.projector_list = self.projector_list
|
||||||
self.source_select_form = None
|
self.source_select_form = None
|
||||||
|
|
||||||
def bootstrap_initialise(self):
|
def bootstrap_initialise(self):
|
||||||
@ -662,6 +665,20 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjecto
|
|||||||
message = '%s<b>%s</b>: %s<br />' % (message,
|
message = '%s<b>%s</b>: %s<br />' % (message,
|
||||||
translate('OpenLP.ProjectorManager', 'Current source input is'),
|
translate('OpenLP.ProjectorManager', 'Current source input is'),
|
||||||
projector.link.source)
|
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
|
count = 1
|
||||||
for item in projector.link.lamp:
|
for item in projector.link.lamp:
|
||||||
message += '<b>{title} {count}</b> {status} '.format(title=translate('OpenLP.ProjectorManager',
|
message += '<b>{title} {count}</b> {status} '.format(title=translate('OpenLP.ProjectorManager',
|
||||||
@ -973,7 +990,7 @@ class ProjectorItem(QtCore.QObject):
|
|||||||
self.poll_time = None
|
self.poll_time = None
|
||||||
self.socket_timeout = None
|
self.socket_timeout = None
|
||||||
self.status = S_NOT_CONNECTED
|
self.status = S_NOT_CONNECTED
|
||||||
super(ProjectorItem, self).__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|
||||||
def not_implemented(function):
|
def not_implemented(function):
|
||||||
|
@ -233,7 +233,8 @@ class SourceSelectTabs(QtWidgets.QDialog):
|
|||||||
:param projectordb: ProjectorDB session to use
|
:param projectordb: ProjectorDB session to use
|
||||||
"""
|
"""
|
||||||
log.debug('Initializing SourceSelectTabs()')
|
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.setMinimumWidth(350)
|
||||||
self.projectordb = projectordb
|
self.projectordb = projectordb
|
||||||
self.edit = edit
|
self.edit = edit
|
||||||
@ -388,7 +389,8 @@ class SourceSelectSingle(QtWidgets.QDialog):
|
|||||||
"""
|
"""
|
||||||
log.debug('Initializing SourceSelectSingle()')
|
log.debug('Initializing SourceSelectSingle()')
|
||||||
self.projectordb = projectordb
|
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
|
self.edit = edit
|
||||||
if self.edit:
|
if self.edit:
|
||||||
title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text')
|
title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text')
|
||||||
|
@ -37,8 +37,8 @@ class ServiceItemEditForm(QtWidgets.QDialog, Ui_ServiceItemEditDialog, RegistryP
|
|||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
"""
|
"""
|
||||||
super(ServiceItemEditForm, self).__init__(Registry().get('main_window'),
|
super(ServiceItemEditForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint |
|
||||||
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
|
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.item_list = []
|
self.item_list = []
|
||||||
self.list_widget.currentRowChanged.connect(self.on_current_row_changed)
|
self.list_widget.currentRowChanged.connect(self.on_current_row_changed)
|
||||||
|
@ -66,6 +66,12 @@ class ServiceManagerList(QtWidgets.QTreeWidget):
|
|||||||
elif event.key() == QtCore.Qt.Key_Down:
|
elif event.key() == QtCore.Qt.Key_Down:
|
||||||
self.service_manager.on_move_selection_down()
|
self.service_manager.on_move_selection_down()
|
||||||
event.accept()
|
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:
|
elif event.key() == QtCore.Qt.Key_Delete:
|
||||||
self.service_manager.on_delete_from_service()
|
self.service_manager.on_delete_from_service()
|
||||||
event.accept()
|
event.accept()
|
||||||
@ -1119,6 +1125,35 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
|
|||||||
return
|
return
|
||||||
self.service_manager_list.setCurrentItem(item_after)
|
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):
|
def on_collapse_all(self, field=None):
|
||||||
"""
|
"""
|
||||||
Collapse all the service items.
|
Collapse all the service items.
|
||||||
|
@ -37,8 +37,8 @@ class ServiceNoteForm(QtWidgets.QDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
"""
|
"""
|
||||||
super(ServiceNoteForm, self).__init__(Registry().get('main_window'),
|
super(ServiceNoteForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint |
|
||||||
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
|
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
|
||||||
self.setupUi()
|
self.setupUi()
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
|
|
||||||
|
@ -46,7 +46,8 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Registry().register('settings_form', self)
|
Registry().register('settings_form', self)
|
||||||
Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up)
|
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.processes = []
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.setting_list_widget.currentRowChanged.connect(self.list_item_changed)
|
self.setting_list_widget.currentRowChanged.connect(self.list_item_changed)
|
||||||
|
@ -44,7 +44,8 @@ class ShortcutListForm(QtWidgets.QDialog, Ui_ShortcutListDialog, RegistryPropert
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.setupUi(self)
|
||||||
self.changed_actions = {}
|
self.changed_actions = {}
|
||||||
self.action_list = ActionList.get_instance()
|
self.action_list = ActionList.get_instance()
|
||||||
@ -279,9 +280,7 @@ class ShortcutListForm(QtWidgets.QDialog, Ui_ShortcutListDialog, RegistryPropert
|
|||||||
return
|
return
|
||||||
if QtWidgets.QMessageBox.question(self, translate('OpenLP.ShortcutListDialog', 'Restore Default Shortcuts'),
|
if QtWidgets.QMessageBox.question(self, translate('OpenLP.ShortcutListDialog', 'Restore Default Shortcuts'),
|
||||||
translate('OpenLP.ShortcutListDialog', 'Do you want to restore all '
|
translate('OpenLP.ShortcutListDialog', 'Do you want to restore all '
|
||||||
'shortcuts to their defaults?'),
|
'shortcuts to their defaults?')
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
|
|
||||||
QtWidgets.QMessageBox.No)
|
|
||||||
) == QtWidgets.QMessageBox.No:
|
) == QtWidgets.QMessageBox.No:
|
||||||
return
|
return
|
||||||
self._adjust_button(self.primary_push_button, False, text='')
|
self._adjust_button(self.primary_push_button, False, text='')
|
||||||
|
@ -38,8 +38,8 @@ class StartTimeForm(QtWidgets.QDialog, Ui_StartTimeDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
Constructor
|
||||||
"""
|
"""
|
||||||
super(StartTimeForm, self).__init__(Registry().get('main_window'),
|
super(StartTimeForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint |
|
||||||
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
|
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
def exec(self):
|
def exec(self):
|
||||||
|
@ -257,10 +257,9 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
|
|||||||
Renames an existing theme to a new name
|
Renames an existing theme to a new name
|
||||||
:param field:
|
:param field:
|
||||||
"""
|
"""
|
||||||
# TODO: Check for delayed format() conversions
|
|
||||||
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'),
|
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'),
|
||||||
translate('OpenLP.ThemeManager', 'Rename Confirmation'),
|
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()
|
item = self.theme_list_widget.currentItem()
|
||||||
old_theme_name = item.data(QtCore.Qt.UserRole)
|
old_theme_name = item.data(QtCore.Qt.UserRole)
|
||||||
self.file_rename_form.file_name_edit.setText(old_theme_name)
|
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.
|
Delete a theme triggered by the UI.
|
||||||
:param field:
|
:param field:
|
||||||
"""
|
"""
|
||||||
# TODO: Verify delayed format() conversions
|
|
||||||
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to delete.'),
|
if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to delete.'),
|
||||||
translate('OpenLP.ThemeManager', 'Delete Confirmation'),
|
translate('OpenLP.ThemeManager', 'Delete Confirmation'),
|
||||||
translate('OpenLP.ThemeManager', 'Delete %s theme?')):
|
translate('OpenLP.ThemeManager', 'Delete {theme_name} theme?')):
|
||||||
item = self.theme_list_widget.currentItem()
|
item = self.theme_list_widget.currentItem()
|
||||||
theme = item.text()
|
theme = item.text()
|
||||||
row = self.theme_list_widget.row(item)
|
row = self.theme_list_widget.row(item)
|
||||||
@ -539,9 +537,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
|
|||||||
translate('OpenLP.ThemeManager',
|
translate('OpenLP.ThemeManager',
|
||||||
'Theme {name} already exists. '
|
'Theme {name} already exists. '
|
||||||
'Do you want to replace it?').format(name=theme_name),
|
'Do you want to replace it?').format(name=theme_name),
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
|
defaultButton=QtWidgets.QMessageBox.No)
|
||||||
QtWidgets.QMessageBox.No),
|
|
||||||
QtWidgets.QMessageBox.No)
|
|
||||||
return ret == QtWidgets.QMessageBox.Yes
|
return ret == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
def unzip_theme(self, file_name, directory):
|
def unzip_theme(self, file_name, directory):
|
||||||
@ -785,9 +781,8 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage
|
|||||||
# confirm deletion
|
# confirm deletion
|
||||||
if confirm:
|
if confirm:
|
||||||
answer = QtWidgets.QMessageBox.question(
|
answer = QtWidgets.QMessageBox.question(
|
||||||
self, confirm_title, confirm_text % theme,
|
self, confirm_title, confirm_text.format(theme_name=theme),
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
|
defaultButton=QtWidgets.QMessageBox.No)
|
||||||
QtWidgets.QMessageBox.No)
|
|
||||||
if answer == QtWidgets.QMessageBox.No:
|
if answer == QtWidgets.QMessageBox.No:
|
||||||
return False
|
return False
|
||||||
# should be the same unless default
|
# should be the same unless default
|
||||||
|
@ -88,21 +88,20 @@ JAVASCRIPT = """
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# TODO: Verify format() with variable templates
|
|
||||||
CSS = """
|
CSS = """
|
||||||
#alert {
|
#alert {{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
width: 100%%;
|
width: 100%;
|
||||||
vertical-align: %s;
|
vertical-align: {vertical_align};
|
||||||
font-family: %s;
|
font-family: {font_family};
|
||||||
font-size: %spt;
|
font-size: {font_size:d}pt;
|
||||||
color: %s;
|
color: {color};
|
||||||
background-color: %s;
|
background-color: {background_color};
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
HTML = """
|
HTML = """
|
||||||
@ -228,8 +227,11 @@ class AlertsPlugin(Plugin):
|
|||||||
Add CSS to the main display.
|
Add CSS to the main display.
|
||||||
"""
|
"""
|
||||||
align = VerticalType.Names[self.settings_tab.location]
|
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,
|
return CSS.format(vertical_align=align,
|
||||||
self.settings_tab.background_color)
|
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
|
@staticmethod
|
||||||
def get_display_html():
|
def get_display_html():
|
||||||
|
@ -36,8 +36,8 @@ class AlertForm(QtWidgets.QDialog, Ui_AlertDialog):
|
|||||||
"""
|
"""
|
||||||
Initialise the alert form
|
Initialise the alert form
|
||||||
"""
|
"""
|
||||||
super(AlertForm, self).__init__(Registry().get('main_window'),
|
super(AlertForm, self).__init__(Registry().get('main_window'), QtCore.Qt.WindowSystemMenuHint |
|
||||||
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
|
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
|
||||||
self.manager = plugin.manager
|
self.manager = plugin.manager
|
||||||
self.plugin = plugin
|
self.plugin = plugin
|
||||||
self.item_id = None
|
self.item_id = None
|
||||||
@ -180,9 +180,7 @@ class AlertForm(QtWidgets.QDialog, Ui_AlertDialog):
|
|||||||
translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
|
translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
|
||||||
translate('AlertsPlugin.AlertForm',
|
translate('AlertsPlugin.AlertForm',
|
||||||
'You have not entered a parameter to be replaced.\n'
|
'You have not entered a parameter to be replaced.\n'
|
||||||
'Do you want to continue anyway?'),
|
'Do you want to continue anyway?')
|
||||||
QtWidgets.QMessageBox.StandardButtons(
|
|
||||||
QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes)
|
|
||||||
) == QtWidgets.QMessageBox.No:
|
) == QtWidgets.QMessageBox.No:
|
||||||
self.parameter_edit.setFocus()
|
self.parameter_edit.setFocus()
|
||||||
return False
|
return False
|
||||||
@ -193,9 +191,7 @@ class AlertForm(QtWidgets.QDialog, Ui_AlertDialog):
|
|||||||
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
|
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
|
||||||
translate('AlertsPlugin.AlertForm',
|
translate('AlertsPlugin.AlertForm',
|
||||||
'The alert text does not contain \'<>\'.\n'
|
'The alert text does not contain \'<>\'.\n'
|
||||||
'Do you want to continue anyway?'),
|
'Do you want to continue anyway?')
|
||||||
QtWidgets.QMessageBox.StandardButtons(
|
|
||||||
QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes)
|
|
||||||
) == QtWidgets.QMessageBox.No:
|
) == QtWidgets.QMessageBox.No:
|
||||||
self.parameter_edit.setFocus()
|
self.parameter_edit.setFocus()
|
||||||
return False
|
return False
|
||||||
|
@ -38,7 +38,7 @@ __default_settings__ = {
|
|||||||
'bibles/db password': '',
|
'bibles/db password': '',
|
||||||
'bibles/db hostname': '',
|
'bibles/db hostname': '',
|
||||||
'bibles/db database': '',
|
'bibles/db database': '',
|
||||||
'bibles/last search type': BibleSearch.Combined,
|
'bibles/last used search type': BibleSearch.Combined,
|
||||||
'bibles/reset to combined quick search': True,
|
'bibles/reset to combined quick search': True,
|
||||||
'bibles/verse layout style': LayoutStyle.VersePerSlide,
|
'bibles/verse layout style': LayoutStyle.VersePerSlide,
|
||||||
'bibles/book name language': LanguageSelection.Bible,
|
'bibles/book name language': LanguageSelection.Bible,
|
||||||
|
@ -421,8 +421,8 @@ class BibleImportForm(OpenLPWizard):
|
|||||||
Allow for localisation of the bible import wizard.
|
Allow for localisation of the bible import wizard.
|
||||||
"""
|
"""
|
||||||
self.setWindowTitle(translate('BiblesPlugin.ImportWizardForm', 'Bible Import Wizard'))
|
self.setWindowTitle(translate('BiblesPlugin.ImportWizardForm', 'Bible Import Wizard'))
|
||||||
self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui',
|
self.title_label.setText(WizardStrings.HeaderStyle.format(text=translate('OpenLP.Ui',
|
||||||
'Welcome to the Bible Import Wizard'))
|
'Welcome to the Bible Import Wizard')))
|
||||||
self.information_label.setText(
|
self.information_label.setText(
|
||||||
translate('BiblesPlugin.ImportWizardForm',
|
translate('BiblesPlugin.ImportWizardForm',
|
||||||
'This wizard will help you to import Bibles from a variety of '
|
'This wizard will help you to import Bibles from a variety of '
|
||||||
|
@ -49,7 +49,8 @@ class BookNameForm(QDialog, Ui_BookNameDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.setupUi(self)
|
||||||
self.custom_signals()
|
self.custom_signals()
|
||||||
self.book_names = BibleStrings().BookNames
|
self.book_names = BibleStrings().BookNames
|
||||||
|
@ -45,7 +45,8 @@ class EditBibleForm(QtWidgets.QDialog, Ui_EditBibleDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.media_item = media_item
|
||||||
self.book_names = BibleStrings().BookNames
|
self.book_names = BibleStrings().BookNames
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
@ -47,7 +47,8 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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)
|
self.setupUi(self)
|
||||||
|
|
||||||
def exec(self, bible_name):
|
def exec(self, bible_name):
|
||||||
|
@ -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}'.format(role=role)] = '\s*(?:{source})\s*'.format(source=source_string)
|
||||||
REFERENCE_SEPARATORS['sep_{role}_default'.format(role=role)] = default_separators[index]
|
REFERENCE_SEPARATORS['sep_{role}_default'.format(role=role)] = default_separators[index]
|
||||||
# verse range match: (<chapter>:)?<verse>(-((<chapter>:)?<verse>|end)?)?
|
# verse range match: (<chapter>:)?<verse>(-((<chapter>:)?<verse>|end)?)?
|
||||||
# TODO: Check before converting this string
|
range_regex = '(?:(?P<from_chapter>[0-9]+){sep_v})?' \
|
||||||
range_regex = '(?:(?P<from_chapter>[0-9]+)%(sep_v)s)?' \
|
'(?P<from_verse>[0-9]+)(?P<range_to>{sep_r}(?:(?:(?P<to_chapter>' \
|
||||||
'(?P<from_verse>[0-9]+)(?P<range_to>%(sep_r)s(?:(?:(?P<to_chapter>' \
|
'[0-9]+){sep_v})?(?P<to_verse>[0-9]+)|{sep_e})?)?'.format_map(REFERENCE_SEPARATORS)
|
||||||
'[0-9]+)%(sep_v)s)?(?P<to_verse>[0-9]+)|%(sep_e)s)?)?' % REFERENCE_SEPARATORS
|
REFERENCE_MATCHES['range'] = re.compile(r'^\s*{range}\s*$'.format(range=range_regex), re.UNICODE)
|
||||||
# TODO: Test before converting re.compile strings
|
|
||||||
REFERENCE_MATCHES['range'] = re.compile('^\s*%s\s*$' % range_regex, re.UNICODE)
|
|
||||||
REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE)
|
REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE)
|
||||||
# full reference match: <book>(<range>(,(?!$)|(?=$)))+
|
# full reference match: <book>(<range>(,(?!$)|(?=$)))+
|
||||||
REFERENCE_MATCHES['full'] = \
|
REFERENCE_MATCHES['full'] = \
|
||||||
re.compile('^\s*(?!\s)(?P<book>[\d]*[^\d\.]+)\.*(?<!\s)\s*'
|
re.compile(r'^\s*(?!\s)(?P<book>[\d]*[.]?[^\d\.]+)\.*(?<!\s)\s*'
|
||||||
'(?P<ranges>(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$'
|
r'(?P<ranges>(?:{range_regex}(?:{sep_l}(?!\s*$)|(?=\s*$)))+)\s*$'.format(
|
||||||
% dict(list(REFERENCE_SEPARATORS.items()) + [('range_regex', range_regex)]), re.UNICODE)
|
range_regex=range_regex, sep_l=REFERENCE_SEPARATORS['sep_l']), re.UNICODE)
|
||||||
|
|
||||||
|
|
||||||
def get_reference_separator(separator_type):
|
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*``
|
``^\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
|
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*$``
|
``(?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
|
The second group contains all ``ranges``. This can be multiple declarations of range_regex separated by a list
|
||||||
|
@ -306,9 +306,8 @@ class BibleDB(Manager):
|
|||||||
book_escaped = book
|
book_escaped = book
|
||||||
for character in RESERVED_CHARACTERS:
|
for character in RESERVED_CHARACTERS:
|
||||||
book_escaped = book_escaped.replace(character, '\\' + character)
|
book_escaped = book_escaped.replace(character, '\\' + character)
|
||||||
# TODO: Verify regex patters before using format()
|
regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())),
|
||||||
regex_book = re.compile('\s*%s\s*' % '\s*'.join(
|
re.UNICODE | re.IGNORECASE)
|
||||||
book_escaped.split()), re.UNICODE | re.IGNORECASE)
|
|
||||||
if language_selection == LanguageSelection.Bible:
|
if language_selection == LanguageSelection.Bible:
|
||||||
db_book = self.get_book(book)
|
db_book = self.get_book(book)
|
||||||
if db_book:
|
if db_book:
|
||||||
|
@ -90,6 +90,8 @@ class BGExtract(RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Extract verses from BibleGateway
|
Extract verses from BibleGateway
|
||||||
"""
|
"""
|
||||||
|
NAME = 'BibleGateway'
|
||||||
|
|
||||||
def __init__(self, proxy_url=None):
|
def __init__(self, proxy_url=None):
|
||||||
log.debug('BGExtract.init("{url}")'.format(url=proxy_url))
|
log.debug('BGExtract.init("{url}")'.format(url=proxy_url))
|
||||||
self.proxy_url = proxy_url
|
self.proxy_url = proxy_url
|
||||||
@ -357,6 +359,8 @@ class BSExtract(RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Extract verses from Bibleserver.com
|
Extract verses from Bibleserver.com
|
||||||
"""
|
"""
|
||||||
|
NAME = 'BibleServer'
|
||||||
|
|
||||||
def __init__(self, proxy_url=None):
|
def __init__(self, proxy_url=None):
|
||||||
log.debug('BSExtract.init("{url}")'.format(url=proxy_url))
|
log.debug('BSExtract.init("{url}")'.format(url=proxy_url))
|
||||||
self.proxy_url = proxy_url
|
self.proxy_url = proxy_url
|
||||||
@ -458,6 +462,8 @@ class CWExtract(RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Extract verses from CrossWalk/BibleStudyTools
|
Extract verses from CrossWalk/BibleStudyTools
|
||||||
"""
|
"""
|
||||||
|
NAME = 'Crosswalk'
|
||||||
|
|
||||||
def __init__(self, proxy_url=None):
|
def __init__(self, proxy_url=None):
|
||||||
log.debug('CWExtract.init("{url}")'.format(url=proxy_url))
|
log.debug('CWExtract.init("{url}")'.format(url=proxy_url))
|
||||||
self.proxy_url = proxy_url
|
self.proxy_url = proxy_url
|
||||||
|
@ -414,7 +414,9 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
if self.bible:
|
if self.bible:
|
||||||
book_data = self.get_common_books(self.bible, self.second_bible)
|
book_data = self.get_common_books(self.bible, self.second_bible)
|
||||||
language_selection = self.plugin.manager.get_language_selection(self.bible.name)
|
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)
|
books.sort(key=get_locale_key)
|
||||||
set_case_insensitive_completer(books, self.search_edit)
|
set_case_insensitive_completer(books, self.search_edit)
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ __default_settings__ = {
|
|||||||
'custom/db password': '',
|
'custom/db password': '',
|
||||||
'custom/db hostname': '',
|
'custom/db hostname': '',
|
||||||
'custom/db database': '',
|
'custom/db database': '',
|
||||||
'custom/last search type': CustomSearch.Titles,
|
'custom/last used search type': CustomSearch.Titles,
|
||||||
'custom/display footer': True,
|
'custom/display footer': True,
|
||||||
'custom/add custom from service': True
|
'custom/add custom from service': True
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,8 @@ class EditCustomForm(QtWidgets.QDialog, Ui_CustomEditDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.manager = manager
|
||||||
self.media_item = media_item
|
self.media_item = media_item
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
@ -39,7 +39,8 @@ class EditCustomSlideForm(QtWidgets.QDialog, Ui_CustomSlideEditDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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)
|
self.setupUi(self)
|
||||||
# Connecting signals and slots
|
# Connecting signals and slots
|
||||||
self.insert_button.clicked.connect(self.on_insert_button_clicked)
|
self.insert_button.clicked.connect(self.on_insert_button_clicked)
|
||||||
|
@ -190,9 +190,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
translate('CustomPlugin.MediaItem',
|
translate('CustomPlugin.MediaItem',
|
||||||
'Are you sure you want to delete the "{items:d}" '
|
'Are you sure you want to delete the "{items:d}" '
|
||||||
'selected custom slide(s)?').format(items=len(items)),
|
'selected custom slide(s)?').format(items=len(items)),
|
||||||
QtWidgets.QMessageBox.StandardButtons(
|
defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
|
|
||||||
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
|
|
||||||
return
|
return
|
||||||
row_list = [item.row() for item in self.list_view.selectedIndexes()]
|
row_list = [item.row() for item in self.list_view.selectedIndexes()]
|
||||||
row_list.sort(reverse=True)
|
row_list.sort(reverse=True)
|
||||||
|
@ -35,7 +35,8 @@ class AddGroupForm(QtWidgets.QDialog, Ui_AddGroupDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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)
|
self.setupUi(self)
|
||||||
|
|
||||||
def exec(self, clear=True, show_top_level_group=False, selected_group=None):
|
def exec(self, clear=True, show_top_level_group=False, selected_group=None):
|
||||||
|
@ -33,7 +33,8 @@ class ChooseGroupForm(QtWidgets.QDialog, Ui_ChooseGroupDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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)
|
self.setupUi(self)
|
||||||
|
|
||||||
def exec(self, selected_group=None):
|
def exec(self, selected_group=None):
|
||||||
|
@ -246,9 +246,7 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
translate('ImagePlugin.MediaItem', 'Remove group'),
|
translate('ImagePlugin.MediaItem', 'Remove group'),
|
||||||
translate('ImagePlugin.MediaItem',
|
translate('ImagePlugin.MediaItem',
|
||||||
'Are you sure you want to remove "{name}" and everything in it?'
|
'Are you sure you want to remove "{name}" and everything in it?'
|
||||||
).format(name=item_data.group_name),
|
).format(name=item_data.group_name)
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
|
|
||||||
QtWidgets.QMessageBox.No)
|
|
||||||
) == QtWidgets.QMessageBox.Yes:
|
) == QtWidgets.QMessageBox.Yes:
|
||||||
self.recursively_delete_group(item_data)
|
self.recursively_delete_group(item_data)
|
||||||
self.manager.delete_object(ImageGroups, row_item.data(0, QtCore.Qt.UserRole).id)
|
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)'),
|
self, translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
||||||
translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: {names}\n'
|
translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: {names}\n'
|
||||||
'Do you want to add the other images anyway?'
|
'Do you want to add the other images anyway?'
|
||||||
).format(names='\n'.join(missing_items_file_names)),
|
).format(names='\n'.join(missing_items_file_names))) == \
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Yes)) == \
|
|
||||||
QtWidgets.QMessageBox.No:
|
QtWidgets.QMessageBox.No:
|
||||||
return False
|
return False
|
||||||
# Continue with the existing images.
|
# Continue with the existing images.
|
||||||
|
@ -52,7 +52,8 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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_instance = None
|
||||||
self.vlc_media_player = None
|
self.vlc_media_player = None
|
||||||
self.vlc_media = None
|
self.vlc_media = None
|
||||||
|
@ -253,15 +253,14 @@ class PdfDocument(PresentationDocument):
|
|||||||
try:
|
try:
|
||||||
if not os.path.isdir(self.get_temp_folder()):
|
if not os.path.isdir(self.get_temp_folder()):
|
||||||
os.makedirs(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:
|
if self.controller.mudrawbin:
|
||||||
log.debug('loading presentation using mudraw')
|
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()),
|
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],
|
'-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path],
|
||||||
startupinfo=self.startupinfo)
|
startupinfo=self.startupinfo)
|
||||||
elif self.controller.mutoolbin:
|
elif self.controller.mutoolbin:
|
||||||
log.debug('loading presentation using mutool')
|
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',
|
runlog = check_output([self.controller.mutoolbin, 'draw', '-w', str(size.width()), '-h',
|
||||||
str(size.height()),
|
str(size.height()),
|
||||||
'-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path],
|
'-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:
|
elif self.controller.gsbin:
|
||||||
log.debug('loading presentation using gs')
|
log.debug('loading presentation using gs')
|
||||||
resolution = self.gs_get_resolution(size)
|
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',
|
runlog = check_output([self.controller.gsbin, '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m',
|
||||||
'-r' + str(resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4',
|
'-r' + str(resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4',
|
||||||
'-sOutputFile=' + os.path.join(self.get_temp_folder(), 'mainslide%03d.png'),
|
'-sOutputFile=' + os.path.join(self.get_temp_folder(), 'mainslide%03d.png'),
|
||||||
|
@ -81,7 +81,7 @@ class PowerpointController(PresentationController):
|
|||||||
if app_version >= 12:
|
if app_version >= 12:
|
||||||
self.also_supports = ['odp']
|
self.also_supports = ['odp']
|
||||||
except (OSError, ValueError):
|
except (OSError, ValueError):
|
||||||
log.warning('Detection of powerpoint version using registry failed.')
|
log.exception('Detection of powerpoint version using registry failed.')
|
||||||
return True
|
return True
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
@ -109,9 +109,8 @@ class PowerpointController(PresentationController):
|
|||||||
if self.process.Presentations.Count > 0:
|
if self.process.Presentations.Count > 0:
|
||||||
return
|
return
|
||||||
self.process.Quit()
|
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('Exception caught while killing powerpoint process')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.process = None
|
self.process = None
|
||||||
|
|
||||||
@ -154,9 +153,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
if len(ScreenList().screen_list) > 1:
|
if len(ScreenList().screen_list) > 1:
|
||||||
Registry().get('main_window').activateWindow()
|
Registry().get('main_window').activateWindow()
|
||||||
return True
|
return True
|
||||||
except (AttributeError, pywintypes.com_error) as e:
|
except (AttributeError, pywintypes.com_error):
|
||||||
log.exception('Exception caught while loading Powerpoint presentation')
|
log.exception('Exception caught while loading Powerpoint presentation')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -192,9 +190,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
if self.presentation:
|
if self.presentation:
|
||||||
try:
|
try:
|
||||||
self.presentation.Close()
|
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('Caught exception while closing powerpoint presentation')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.presentation = None
|
self.presentation = None
|
||||||
self.controller.remove_doc(self)
|
self.controller.remove_doc(self)
|
||||||
@ -210,9 +207,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
try:
|
try:
|
||||||
if self.controller.process.Presentations.Count == 0:
|
if self.controller.process.Presentations.Count == 0:
|
||||||
return False
|
return False
|
||||||
except (AttributeError, pywintypes.com_error) as e:
|
except (AttributeError, pywintypes.com_error):
|
||||||
log.exception('Caught exception while in is_loaded')
|
log.exception('Caught exception while in is_loaded')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@ -229,9 +225,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
return False
|
return False
|
||||||
if self.presentation.SlideShowWindow.View is None:
|
if self.presentation.SlideShowWindow.View is None:
|
||||||
return False
|
return False
|
||||||
except (AttributeError, pywintypes.com_error) as e:
|
except (AttributeError, pywintypes.com_error):
|
||||||
log.exception('Caught exception while in is_active')
|
log.exception('Caught exception while in is_active')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@ -249,9 +244,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[self.blank_slide], False)
|
self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[self.blank_slide], False)
|
||||||
if self.blank_click:
|
if self.blank_click:
|
||||||
self.presentation.SlideShowWindow.View.GotoClick(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('Caught exception while in unblank_screen')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
# Stop powerpoint from flashing in the taskbar
|
# Stop powerpoint from flashing in the taskbar
|
||||||
@ -273,9 +267,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex()
|
self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex()
|
||||||
# ppSlideShowBlackScreen = 3
|
# ppSlideShowBlackScreen = 3
|
||||||
self.presentation.SlideShowWindow.View.State = 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('Caught exception while in blank_screen')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
|
|
||||||
@ -288,9 +281,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
try:
|
try:
|
||||||
# ppSlideShowBlackScreen = 3
|
# ppSlideShowBlackScreen = 3
|
||||||
return self.presentation.SlideShowWindow.View.State == 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('Caught exception while in is_blank')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
else:
|
else:
|
||||||
@ -303,9 +295,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
log.debug('stop_presentation')
|
log.debug('stop_presentation')
|
||||||
try:
|
try:
|
||||||
self.presentation.SlideShowWindow.View.Exit()
|
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('Caught exception while in stop_presentation')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
|
|
||||||
@ -328,9 +319,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
ppt_window = None
|
ppt_window = None
|
||||||
try:
|
try:
|
||||||
ppt_window = self.presentation.SlideShowSettings.Run()
|
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('Caught exception while in start_presentation')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
if ppt_window and not Settings().value('presentations/powerpoint control window'):
|
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.Height = size.height() * 72 / dpi
|
||||||
ppt_window.Left = size.x() * 72 / dpi
|
ppt_window.Left = size.x() * 72 / dpi
|
||||||
ppt_window.Width = size.width() * 72 / dpi
|
ppt_window.Width = size.width() * 72 / dpi
|
||||||
except AttributeError as e:
|
except AttributeError:
|
||||||
log.exception('AttributeError while in start_presentation')
|
log.exception('AttributeError while in start_presentation')
|
||||||
log.exception(e)
|
|
||||||
# Find the presentation window and save the handle for later
|
# Find the presentation window and save the handle for later
|
||||||
self.presentation_hwnd = None
|
self.presentation_hwnd = None
|
||||||
if ppt_window:
|
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)
|
ret = next((key for key, slidenum in self.index_map.items() if slidenum == ret), None)
|
||||||
else:
|
else:
|
||||||
ret = self.presentation.SlideShowWindow.View.CurrentShowPosition
|
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('Caught exception while in get_slide_number')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
return ret
|
return ret
|
||||||
@ -431,9 +419,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
self.next_step()
|
self.next_step()
|
||||||
else:
|
else:
|
||||||
self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[slide_no])
|
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('Caught exception while in goto_slide')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
|
|
||||||
@ -445,9 +432,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
try:
|
try:
|
||||||
self.presentation.SlideShowWindow.Activate()
|
self.presentation.SlideShowWindow.Activate()
|
||||||
self.presentation.SlideShowWindow.View.Next()
|
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('Caught exception while in next_step')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
return
|
return
|
||||||
@ -468,9 +454,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
log.debug('previous_step')
|
log.debug('previous_step')
|
||||||
try:
|
try:
|
||||||
self.presentation.SlideShowWindow.View.Previous()
|
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('Caught exception while in previous_step')
|
||||||
log.exception(e)
|
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
self.show_error_msg()
|
self.show_error_msg()
|
||||||
|
|
||||||
@ -503,8 +488,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
slide = self.presentation.Slides(self.index_map[num + 1])
|
slide = self.presentation.Slides(self.index_map[num + 1])
|
||||||
try:
|
try:
|
||||||
text = slide.Shapes.Title.TextFrame.TextRange.Text
|
text = slide.Shapes.Title.TextFrame.TextRange.Text
|
||||||
except Exception as e:
|
except Exception:
|
||||||
log.exception(e)
|
log.exception('Exception raised when getting title text')
|
||||||
text = ''
|
text = ''
|
||||||
titles.append(text.replace('\n', ' ').replace('\x0b', ' ') + '\n')
|
titles.append(text.replace('\n', ' ').replace('\x0b', ' ') + '\n')
|
||||||
note = _get_text_from_shapes(slide.NotesPage.Shapes)
|
note = _get_text_from_shapes(slide.NotesPage.Shapes)
|
||||||
@ -519,9 +504,8 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.presentation.SlideShowWindow.View.Exit()
|
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('Failed to exit Powerpoint presentation after error')
|
||||||
log.exception(e)
|
|
||||||
critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument',
|
critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument',
|
||||||
'An error occurred in the PowerPoint integration '
|
'An error occurred in the PowerPoint integration '
|
||||||
'and the presentation will be stopped. '
|
'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.PlaceholderFormat.Type == 2: # 2 from is enum PpPlaceholderType.ppPlaceholderBody
|
||||||
if shape.HasTextFrame and shape.TextFrame.HasText:
|
if shape.HasTextFrame and shape.TextFrame.HasText:
|
||||||
text += shape.TextFrame.TextRange.Text + '\n'
|
text += shape.TextFrame.TextRange.Text + '\n'
|
||||||
except pywintypes.com_error as e:
|
except pywintypes.com_error:
|
||||||
log.warning('Failed to extract text from powerpoint slide')
|
log.exception('Failed to extract text from powerpoint slide')
|
||||||
log.warning(e)
|
|
||||||
return text
|
return text
|
||||||
|
@ -122,5 +122,4 @@ class RemotesPlugin(Plugin):
|
|||||||
translate('RemotePlugin', 'Server Config Change'),
|
translate('RemotePlugin', 'Server Config Change'),
|
||||||
translate('RemotePlugin',
|
translate('RemotePlugin',
|
||||||
'Server configuration changes will require a restart '
|
'Server configuration changes will require a restart '
|
||||||
'to take effect.'),
|
'to take effect.'))
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
|
|
||||||
|
@ -35,7 +35,8 @@ class AuthorsForm(QtWidgets.QDialog, Ui_AuthorsDialog):
|
|||||||
"""
|
"""
|
||||||
Set up the screen and common data
|
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.setupUi(self)
|
||||||
self.auto_display_name = False
|
self.auto_display_name = False
|
||||||
self.first_name_edit.textEdited.connect(self.on_first_name_edited)
|
self.first_name_edit.textEdited.connect(self.on_first_name_edited)
|
||||||
|
@ -82,6 +82,9 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
|
|||||||
self.finish_button.clicked.connect(self.on_wizard_exit)
|
self.finish_button.clicked.connect(self.on_wizard_exit)
|
||||||
self.cancel_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):
|
def add_custom_pages(self):
|
||||||
"""
|
"""
|
||||||
Add song wizard specific pages.
|
Add song wizard specific pages.
|
||||||
@ -130,9 +133,9 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
|
|||||||
Song wizard localisation.
|
Song wizard localisation.
|
||||||
"""
|
"""
|
||||||
self.setWindowTitle(translate('Wizard', 'Wizard'))
|
self.setWindowTitle(translate('Wizard', 'Wizard'))
|
||||||
# TODO: Check format() using template strings
|
self.title_label.setText(
|
||||||
self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui',
|
WizardStrings.HeaderStyle.format(text=translate('OpenLP.Ui',
|
||||||
'Welcome to the Duplicate Song Removal Wizard'))
|
'Welcome to the Duplicate Song Removal Wizard')))
|
||||||
self.information_label.setText(
|
self.information_label.setText(
|
||||||
translate("Wizard",
|
translate("Wizard",
|
||||||
'This wizard will help you to remove duplicate songs from the song database. You will have a '
|
'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()
|
self.button(QtWidgets.QWizard.CancelButton).hide()
|
||||||
QtWidgets.QMessageBox.information(
|
QtWidgets.QMessageBox.information(
|
||||||
self, translate('Wizard', 'Information'),
|
self, translate('Wizard', 'Information'),
|
||||||
translate('Wizard', 'No duplicate songs have been found in the database.'),
|
translate('Wizard', 'No duplicate songs have been found in the database.'))
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
|
|
||||||
|
|
||||||
def add_duplicates_to_song_list(self, search_song, duplicate_song):
|
def add_duplicates_to_song_list(self, search_song, duplicate_song):
|
||||||
"""
|
"""
|
||||||
|
@ -56,7 +56,8 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.media_item = media_item
|
||||||
self.song = None
|
self.song = None
|
||||||
# can this be automated?
|
# 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'
|
'There is no verse corresponding to "{invalid}". Valid entries are {valid}.\n'
|
||||||
'Please enter the verses separated by spaces.').format(invalid=invalid_verses[0],
|
'Please enter the verses separated by spaces.').format(invalid=invalid_verses[0],
|
||||||
valid=valid)
|
valid=valid)
|
||||||
critical_error_message_box(title=translate('SongsPlugin.EditSongForm', 'Invalid Verse Order'),
|
critical_error_message_box(title=translate('SongsPlugin.EditSongForm', 'Invalid Verse Order'), message=msg)
|
||||||
message=msg)
|
|
||||||
return len(invalid_verses) == 0
|
return len(invalid_verses) == 0
|
||||||
|
|
||||||
def _validate_song(self):
|
def _validate_song(self):
|
||||||
@ -579,8 +579,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
self,
|
self,
|
||||||
translate('SongsPlugin.EditSongForm', 'Add Author'),
|
translate('SongsPlugin.EditSongForm', 'Add Author'),
|
||||||
translate('SongsPlugin.EditSongForm', 'This author does not exist, do you want to add them?'),
|
translate('SongsPlugin.EditSongForm', 'This author does not exist, do you want to add them?'),
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
||||||
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
|
||||||
if text.find(' ') == -1:
|
if text.find(' ') == -1:
|
||||||
author = Author.populate(first_name='', last_name='', display_name=text)
|
author = Author.populate(first_name='', last_name='', display_name=text)
|
||||||
else:
|
else:
|
||||||
@ -658,8 +657,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
if QtWidgets.QMessageBox.question(
|
if QtWidgets.QMessageBox.question(
|
||||||
self, translate('SongsPlugin.EditSongForm', 'Add Topic'),
|
self, translate('SongsPlugin.EditSongForm', 'Add Topic'),
|
||||||
translate('SongsPlugin.EditSongForm', 'This topic does not exist, do you want to add it?'),
|
translate('SongsPlugin.EditSongForm', 'This topic does not exist, do you want to add it?'),
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
||||||
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
|
||||||
topic = Topic.populate(name=text)
|
topic = Topic.populate(name=text)
|
||||||
self.manager.save_object(topic)
|
self.manager.save_object(topic)
|
||||||
topic_item = QtWidgets.QListWidgetItem(str(topic.name))
|
topic_item = QtWidgets.QListWidgetItem(str(topic.name))
|
||||||
@ -705,8 +703,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||||||
if QtWidgets.QMessageBox.question(
|
if QtWidgets.QMessageBox.question(
|
||||||
self, translate('SongsPlugin.EditSongForm', 'Add Songbook'),
|
self, translate('SongsPlugin.EditSongForm', 'Add Songbook'),
|
||||||
translate('SongsPlugin.EditSongForm', 'This Songbook does not exist, do you want to add it?'),
|
translate('SongsPlugin.EditSongForm', 'This Songbook does not exist, do you want to add it?'),
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
||||||
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
|
||||||
songbook = Book.populate(name=text)
|
songbook = Book.populate(name=text)
|
||||||
self.manager.save_object(songbook)
|
self.manager.save_object(songbook)
|
||||||
self.add_songbook_entry_to_list(songbook.id, songbook.name, self.songbook_entry_edit.text())
|
self.add_songbook_entry_to_list(songbook.id, songbook.name, self.songbook_entry_edit.text())
|
||||||
|
@ -43,7 +43,8 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.setupUi(self)
|
||||||
self.has_single_verse = False
|
self.has_single_verse = False
|
||||||
self.insert_button.clicked.connect(self.on_insert_button_clicked)
|
self.insert_button.clicked.connect(self.on_insert_button_clicked)
|
||||||
|
@ -37,7 +37,8 @@ class MediaFilesForm(QtWidgets.QDialog, Ui_MediaFilesDialog):
|
|||||||
log.info('{name} MediaFilesForm loaded'.format(name=__name__))
|
log.info('{name} MediaFilesForm loaded'.format(name=__name__))
|
||||||
|
|
||||||
def __init__(self, parent):
|
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)
|
self.setupUi(self)
|
||||||
|
|
||||||
def populate_files(self, files):
|
def populate_files(self, files):
|
||||||
|
@ -38,7 +38,8 @@ class SongBookForm(QtWidgets.QDialog, Ui_SongBookDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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)
|
self.setupUi(self)
|
||||||
|
|
||||||
def exec(self, clear=True):
|
def exec(self, clear=True):
|
||||||
|
@ -121,7 +121,7 @@ class SongExportForm(OpenLPWizard):
|
|||||||
self.selected_list_widget = QtWidgets.QListWidget(self.export_song_page)
|
self.selected_list_widget = QtWidgets.QListWidget(self.export_song_page)
|
||||||
self.selected_list_widget.setObjectName('selected_list_widget')
|
self.selected_list_widget.setObjectName('selected_list_widget')
|
||||||
self.grid_layout.addWidget(self.selected_list_widget, 1, 0, 1, 1)
|
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 = QtWidgets.QHBoxLayout()
|
||||||
self.horizontal_layout.setObjectName('horizontal_layout')
|
self.horizontal_layout.setObjectName('horizontal_layout')
|
||||||
self.directory_label = QtWidgets.QLabel(self.export_song_page)
|
self.directory_label = QtWidgets.QLabel(self.export_song_page)
|
||||||
@ -143,9 +143,8 @@ class SongExportForm(OpenLPWizard):
|
|||||||
Song wizard localisation.
|
Song wizard localisation.
|
||||||
"""
|
"""
|
||||||
self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard'))
|
self.setWindowTitle(translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard'))
|
||||||
# TODO: Verify format() with template variables
|
self.title_label.setText(
|
||||||
self.title_label.setText(WizardStrings.HeaderStyle %
|
WizardStrings.HeaderStyle.format(text=translate('OpenLP.Ui', 'Welcome to the Song Export Wizard')))
|
||||||
translate('OpenLP.Ui', 'Welcome to the Song Export Wizard'))
|
|
||||||
self.information_label.setText(
|
self.information_label.setText(
|
||||||
translate('SongsPlugin.ExportWizardForm', 'This wizard will help to export your songs to the open and free '
|
translate('SongsPlugin.ExportWizardForm', 'This wizard will help to export your songs to the open and free '
|
||||||
'<strong>OpenLyrics </strong> worship song format.'))
|
'<strong>OpenLyrics </strong> worship song format.'))
|
||||||
|
@ -132,9 +132,8 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
|
|||||||
Song wizard localisation.
|
Song wizard localisation.
|
||||||
"""
|
"""
|
||||||
self.setWindowTitle(translate('SongsPlugin.ImportWizardForm', 'Song Import Wizard'))
|
self.setWindowTitle(translate('SongsPlugin.ImportWizardForm', 'Song Import Wizard'))
|
||||||
# TODO: Verify format() with template variables
|
self.title_label.setText(
|
||||||
self.title_label.setText(WizardStrings.HeaderStyle % translate('OpenLP.Ui',
|
WizardStrings.HeaderStyle.format(text=translate('OpenLP.Ui', 'Welcome to the Song Import Wizard')))
|
||||||
'Welcome to the Song Import Wizard'))
|
|
||||||
self.information_label.setText(
|
self.information_label.setText(
|
||||||
translate('SongsPlugin.ImportWizardForm',
|
translate('SongsPlugin.ImportWizardForm',
|
||||||
'This wizard will help you to import songs from a variety of formats. Click the next button '
|
'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')
|
select_mode, format_name, ext_filter = SongFormat.get(this_format, 'selectMode', 'name', 'filter')
|
||||||
file_path_edit = self.format_widgets[this_format]['file_path_edit']
|
file_path_edit = self.format_widgets[this_format]['file_path_edit']
|
||||||
if select_mode == SongFormatSelect.SingleFile:
|
if select_mode == SongFormatSelect.SingleFile:
|
||||||
# TODO: Verify format() with template variables
|
self.get_file_name(WizardStrings.OpenTypeFile.format(file_type=format_name),
|
||||||
self.get_file_name(
|
file_path_edit, 'last directory import', ext_filter)
|
||||||
WizardStrings.OpenTypeFile % format_name, file_path_edit, 'last directory import', ext_filter)
|
|
||||||
elif select_mode == SongFormatSelect.SingleFolder:
|
elif select_mode == SongFormatSelect.SingleFolder:
|
||||||
# TODO: Verify format() with template variables
|
self.get_folder(
|
||||||
self.get_folder(WizardStrings.OpenTypeFolder % format_name, file_path_edit, 'last directory import')
|
WizardStrings.OpenTypeFolder.format(folder_name=format_name), file_path_edit, 'last directory import')
|
||||||
|
|
||||||
def on_add_button_clicked(self):
|
def on_add_button_clicked(self):
|
||||||
"""
|
"""
|
||||||
@ -286,8 +284,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties):
|
|||||||
this_format = self.current_format
|
this_format = self.current_format
|
||||||
select_mode, format_name, ext_filter, custom_title = \
|
select_mode, format_name, ext_filter, custom_title = \
|
||||||
SongFormat.get(this_format, 'selectMode', 'name', 'filter', 'getFilesTitle')
|
SongFormat.get(this_format, 'selectMode', 'name', 'filter', 'getFilesTitle')
|
||||||
# TODO: Verify format() with template variables
|
title = custom_title if custom_title else WizardStrings.OpenTypeFile.format(file_type=format_name)
|
||||||
title = custom_title if custom_title else WizardStrings.OpenTypeFile % format_name
|
|
||||||
if select_mode == SongFormatSelect.MultipleFiles:
|
if select_mode == SongFormatSelect.MultipleFiles:
|
||||||
self.get_files(title, self.format_widgets[this_format]['file_list_widget'], ext_filter)
|
self.get_files(title, self.format_widgets[this_format]['file_list_widget'], ext_filter)
|
||||||
self.source_page.completeChanged.emit()
|
self.source_page.completeChanged.emit()
|
||||||
|
@ -39,7 +39,7 @@ class Ui_SongMaintenanceDialog(object):
|
|||||||
song_maintenance_dialog.setObjectName('song_maintenance_dialog')
|
song_maintenance_dialog.setObjectName('song_maintenance_dialog')
|
||||||
song_maintenance_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
|
song_maintenance_dialog.setWindowIcon(build_icon(':/icon/openlp-logo.svg'))
|
||||||
song_maintenance_dialog.setWindowModality(QtCore.Qt.ApplicationModal)
|
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 = QtWidgets.QGridLayout(song_maintenance_dialog)
|
||||||
self.dialog_layout.setObjectName('dialog_layout')
|
self.dialog_layout.setObjectName('dialog_layout')
|
||||||
self.type_list_widget = QtWidgets.QListWidget(song_maintenance_dialog)
|
self.type_list_widget = QtWidgets.QListWidget(song_maintenance_dialog)
|
||||||
|
@ -44,7 +44,8 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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.setupUi(self)
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.author_form = AuthorsForm(self)
|
self.author_form = AuthorsForm(self)
|
||||||
|
@ -81,7 +81,8 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent=None, plugin=None, db_manager=None):
|
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.plugin = plugin
|
||||||
self.db_manager = db_manager
|
self.db_manager = db_manager
|
||||||
self.setup_ui(self)
|
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 '
|
translate('SongsPlugin.SongSelectForm', 'WARNING: Saving your username and password is INSECURE, your '
|
||||||
'password is stored in PLAIN TEXT. Click Yes to save your '
|
'password is stored in PLAIN TEXT. Click Yes to save your '
|
||||||
'password or No to cancel this.'),
|
'password or No to cancel this.'),
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
|
defaultButton=QtWidgets.QMessageBox.No)
|
||||||
QtWidgets.QMessageBox.No)
|
|
||||||
if answer == QtWidgets.QMessageBox.No:
|
if answer == QtWidgets.QMessageBox.No:
|
||||||
self.save_password_checkbox.setChecked(False)
|
self.save_password_checkbox.setChecked(False)
|
||||||
|
|
||||||
@ -397,8 +397,7 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog):
|
|||||||
translate('SongsPlugin.SongSelectForm',
|
translate('SongsPlugin.SongSelectForm',
|
||||||
'Your song has been imported, would you '
|
'Your song has been imported, would you '
|
||||||
'like to import more songs?'),
|
'like to import more songs?'),
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
|
defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
||||||
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
|
|
||||||
self.on_back_button_clicked()
|
self.on_back_button_clicked()
|
||||||
else:
|
else:
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
|
@ -38,7 +38,8 @@ class TopicsForm(QtWidgets.QDialog, Ui_TopicsDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor
|
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)
|
self.setupUi(self)
|
||||||
|
|
||||||
def exec(self, clear=True):
|
def exec(self, clear=True):
|
||||||
|
@ -265,7 +265,7 @@ class SongFormat(object):
|
|||||||
},
|
},
|
||||||
EasyWorshipService: {
|
EasyWorshipService: {
|
||||||
'class': EasyWorshipSongImport,
|
'class': EasyWorshipSongImport,
|
||||||
'name': 'EasyWorship Service File',
|
'name': 'EasyWorship Service',
|
||||||
'prefix': 'ew',
|
'prefix': 'ew',
|
||||||
'selectMode': SongFormatSelect.SingleFile,
|
'selectMode': SongFormatSelect.SingleFile,
|
||||||
'filter': '{text} (*.ews)'.format(text=translate('SongsPlugin.ImportWizardForm',
|
'filter': '{text} (*.ews)'.format(text=translate('SongsPlugin.ImportWizardForm',
|
||||||
|
@ -121,8 +121,8 @@ class FoilPresenterImport(SongImport):
|
|||||||
for file_path in self.import_source:
|
for file_path in self.import_source:
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
# TODO: Verify format() with template strings
|
self.import_wizard.increment_progress_bar(
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
|
WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
|
||||||
try:
|
try:
|
||||||
parsed_file = etree.parse(file_path, parser)
|
parsed_file = etree.parse(file_path, parser)
|
||||||
xml = etree.tostring(parsed_file).decode()
|
xml = etree.tostring(parsed_file).decode()
|
||||||
|
@ -275,11 +275,9 @@ class OpenLPSongImport(SongImport):
|
|||||||
self.manager.save_object(new_song)
|
self.manager.save_object(new_song)
|
||||||
if progress_dialog:
|
if progress_dialog:
|
||||||
progress_dialog.setValue(progress_dialog.value() + 1)
|
progress_dialog.setValue(progress_dialog.value() + 1)
|
||||||
# TODO: Verify format() with template strings
|
progress_dialog.setLabelText(WizardStrings.ImportingType.format(source=new_song.title))
|
||||||
progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title)
|
|
||||||
else:
|
else:
|
||||||
# TODO: Verify format() with template strings
|
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=new_song.title))
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title)
|
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
break
|
break
|
||||||
self.source_session.close()
|
self.source_session.close()
|
||||||
|
@ -58,8 +58,8 @@ class OpenLyricsImport(SongImport):
|
|||||||
for file_path in self.import_source:
|
for file_path in self.import_source:
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
# TODO: Verify format() with template strings
|
self.import_wizard.increment_progress_bar(
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
|
WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
|
||||||
try:
|
try:
|
||||||
# Pass a file object, because lxml does not cope with some
|
# Pass a file object, because lxml does not cope with some
|
||||||
# special characters in the path (see lp:757673 and lp:744337).
|
# special characters in the path (see lp:757673 and lp:744337).
|
||||||
|
@ -41,8 +41,8 @@ class PowerPraiseImport(SongImport):
|
|||||||
for file_path in self.import_source:
|
for file_path in self.import_source:
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
# TODO: Verify format() with template strings
|
self.import_wizard.increment_progress_bar(
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
|
WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
|
||||||
root = objectify.parse(open(file_path, 'rb')).getroot()
|
root = objectify.parse(open(file_path, 'rb')).getroot()
|
||||||
self.process_song(root)
|
self.process_song(root)
|
||||||
|
|
||||||
|
@ -44,8 +44,8 @@ class PresentationManagerImport(SongImport):
|
|||||||
for file_path in self.import_source:
|
for file_path in self.import_source:
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
# TODO: Verify format() with template strings
|
self.import_wizard.increment_progress_bar(
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
|
WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
|
||||||
try:
|
try:
|
||||||
tree = etree.parse(file_path, parser=etree.XMLParser(recover=True))
|
tree = etree.parse(file_path, parser=etree.XMLParser(recover=True))
|
||||||
except etree.XMLSyntaxError:
|
except etree.XMLSyntaxError:
|
||||||
|
@ -46,8 +46,8 @@ class ProPresenterImport(SongImport):
|
|||||||
for file_path in self.import_source:
|
for file_path in self.import_source:
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
# TODO: Verify format() with template strings
|
self.import_wizard.increment_progress_bar(
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
|
WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
|
||||||
root = objectify.parse(open(file_path, 'rb')).getroot()
|
root = objectify.parse(open(file_path, 'rb')).getroot()
|
||||||
self.process_song(root, file_path)
|
self.process_song(root, file_path)
|
||||||
|
|
||||||
|
@ -347,8 +347,7 @@ class SongImport(QtCore.QObject):
|
|||||||
song = Song()
|
song = Song()
|
||||||
song.title = self.title
|
song.title = self.title
|
||||||
if self.import_wizard is not None:
|
if self.import_wizard is not None:
|
||||||
# TODO: Verify format() with template variables
|
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=song.title))
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % song.title)
|
|
||||||
song.alternate_title = self.alternate_title
|
song.alternate_title = self.alternate_title
|
||||||
# Values will be set when cleaning the song.
|
# Values will be set when cleaning the song.
|
||||||
song.search_title = ''
|
song.search_title = ''
|
||||||
|
@ -100,8 +100,7 @@ class SongShowPlusImport(SongImport):
|
|||||||
self.other_count = 0
|
self.other_count = 0
|
||||||
self.other_list = {}
|
self.other_list = {}
|
||||||
file_name = os.path.split(file)[1]
|
file_name = os.path.split(file)[1]
|
||||||
# TODO: Verify format() with template variables
|
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_name), 0)
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % file_name, 0)
|
|
||||||
song_data = open(file, 'rb')
|
song_data = open(file, 'rb')
|
||||||
while True:
|
while True:
|
||||||
block_key, = struct.unpack("I", song_data.read(4))
|
block_key, = struct.unpack("I", song_data.read(4))
|
||||||
|
@ -231,9 +231,14 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def search_entire(self, search_keywords):
|
def search_entire(self, search_keywords):
|
||||||
search_string = '%{text}%'.format(text=clean_string(search_keywords))
|
search_string = '%{text}%'.format(text=clean_string(search_keywords))
|
||||||
return self.plugin.manager.get_all_objects(
|
return self.plugin.manager.session.query(Song) \
|
||||||
Song, or_(Song.search_title.like(search_string), Song.search_lyrics.like(search_string),
|
.join(SongBookEntry, isouter=True) \
|
||||||
Song.comments.like(search_string)))
|
.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):
|
def on_song_list_load(self):
|
||||||
"""
|
"""
|
||||||
@ -500,8 +505,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
translate('SongsPlugin.MediaItem',
|
translate('SongsPlugin.MediaItem',
|
||||||
'Are you sure you want to delete the "{items:d}" '
|
'Are you sure you want to delete the "{items:d}" '
|
||||||
'selected song(s)?').format(items=len(items)),
|
'selected song(s)?').format(items=len(items)),
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
|
defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
|
||||||
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
|
|
||||||
return
|
return
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
self.main_window.display_progress_bar(len(items))
|
self.main_window.display_progress_bar(len(items))
|
||||||
|
@ -70,8 +70,7 @@ from openlp.plugins.songs.lib.db import Author, AuthorType, Book, Song, Topic
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
NAMESPACE = 'http://openlyrics.info/namespace/2009/song'
|
NAMESPACE = 'http://openlyrics.info/namespace/2009/song'
|
||||||
# TODO: Verify format() with template variable
|
NSMAP = '{{' + NAMESPACE + '}}{tag}'
|
||||||
NSMAP = '{' + NAMESPACE + '}' + '%s'
|
|
||||||
|
|
||||||
|
|
||||||
class SongXML(object):
|
class SongXML(object):
|
||||||
@ -616,15 +615,13 @@ class OpenLyrics(object):
|
|||||||
text = ''
|
text = ''
|
||||||
use_endtag = True
|
use_endtag = True
|
||||||
# Skip <comment> elements - not yet supported.
|
# Skip <comment> elements - not yet supported.
|
||||||
# TODO: Verify format() with template variables
|
if element.tag == NSMAP.format(tag='comment'):
|
||||||
if element.tag == NSMAP % 'comment':
|
|
||||||
if element.tail:
|
if element.tail:
|
||||||
# Append tail text at comment element.
|
# Append tail text at comment element.
|
||||||
text += element.tail
|
text += element.tail
|
||||||
return text
|
return text
|
||||||
# Convert chords to ChordPro format which OpenLP uses internally
|
# Convert chords to ChordPro format which OpenLP uses internally
|
||||||
# TODO: Verify format() with template variables
|
elif element.tag == NSMAP.format(tag='chord'):
|
||||||
elif element.tag == NSMAP % 'chord':
|
|
||||||
if Settings().value('songs/enable chords') and not Settings().value('songs/disable chords import'):
|
if Settings().value('songs/enable chords') and not Settings().value('songs/disable chords import'):
|
||||||
text += '[{chord}]'.format(chord=element.get('name'))
|
text += '[{chord}]'.format(chord=element.get('name'))
|
||||||
if element.tail:
|
if element.tail:
|
||||||
@ -632,15 +629,13 @@ class OpenLyrics(object):
|
|||||||
text += element.tail
|
text += element.tail
|
||||||
return text
|
return text
|
||||||
# Convert line breaks <br/> to \n.
|
# Convert line breaks <br/> to \n.
|
||||||
# TODO: Verify format() with template variables
|
elif newlines and element.tag == NSMAP.format(tag='br'):
|
||||||
elif newlines and element.tag == NSMAP % 'br':
|
|
||||||
text += '\n'
|
text += '\n'
|
||||||
if element.tail:
|
if element.tail:
|
||||||
text += element.tail
|
text += element.tail
|
||||||
return text
|
return text
|
||||||
# Start formatting tag.
|
# Start formatting tag.
|
||||||
# TODO: Verify format() with template variables
|
if element.tag == NSMAP.format(tag='tag'):
|
||||||
if element.tag == NSMAP % 'tag':
|
|
||||||
text += '{{{name}}}'.format(name=element.get('name'))
|
text += '{{{name}}}'.format(name=element.get('name'))
|
||||||
# Some formattings may have only start tag.
|
# Some formattings may have only start tag.
|
||||||
# Handle this case if element has no children and contains no text.
|
# 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.
|
# Use recursion since nested formatting tags are allowed.
|
||||||
text += self._process_lines_mixed_content(child, newlines)
|
text += self._process_lines_mixed_content(child, newlines)
|
||||||
# Append text from tail and add formatting end tag.
|
# Append text from tail and add formatting end tag.
|
||||||
# TODO: Verify format() with template variables
|
if element.tag == NSMAP.format(tag='tag') and use_endtag:
|
||||||
if element.tag == NSMAP % 'tag' and use_endtag:
|
|
||||||
text += '{{/{name}}}'.format(name=element.get('name'))
|
text += '{{/{name}}}'.format(name=element.get('name'))
|
||||||
# Append text from tail.
|
# Append text from tail.
|
||||||
if element.tail:
|
if element.tail:
|
||||||
@ -682,8 +676,7 @@ class OpenLyrics(object):
|
|||||||
# Loop over the "line" elements removing comments
|
# Loop over the "line" elements removing comments
|
||||||
for line in element:
|
for line in element:
|
||||||
# Skip comment lines.
|
# Skip comment lines.
|
||||||
# TODO: Verify format() with template variables
|
if line.tag == NSMAP.format(tag='comment'):
|
||||||
if line.tag == NSMAP % 'comment':
|
|
||||||
continue
|
continue
|
||||||
if text:
|
if text:
|
||||||
text += '\n'
|
text += '\n'
|
||||||
|
@ -52,6 +52,7 @@ def upgrade_1(session, metadata):
|
|||||||
:param metadata:
|
:param metadata:
|
||||||
"""
|
"""
|
||||||
op = get_upgrade_op(session)
|
op = get_upgrade_op(session)
|
||||||
|
metadata.reflect()
|
||||||
if 'media_files_songs' in [t.name for t in metadata.tables.values()]:
|
if 'media_files_songs' in [t.name for t in metadata.tables.values()]:
|
||||||
op.drop_table('media_files_songs')
|
op.drop_table('media_files_songs')
|
||||||
op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
|
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
|
This version corrects the errors in upgrades 4 and 5
|
||||||
"""
|
"""
|
||||||
op = get_upgrade_op(session)
|
op = get_upgrade_op(session)
|
||||||
|
metadata.reflect()
|
||||||
# Move upgrade 4 to here and correct it (authors_songs table, not songs table)
|
# Move upgrade 4 to here and correct it (authors_songs table, not songs table)
|
||||||
authors_songs = Table('authors_songs', metadata, autoload=True)
|
authors_songs = Table('authors_songs', metadata, autoload=True)
|
||||||
if 'author_type' not in [col.name for col in authors_songs.c.values()]:
|
if 'author_type' not in [col.name for col in authors_songs.c.values()]:
|
||||||
|
@ -54,7 +54,7 @@ __default_settings__ = {
|
|||||||
'songs/db password': '',
|
'songs/db password': '',
|
||||||
'songs/db hostname': '',
|
'songs/db hostname': '',
|
||||||
'songs/db database': '',
|
'songs/db database': '',
|
||||||
'songs/last search type': SongSearch.Entire,
|
'songs/last used search type': SongSearch.Entire,
|
||||||
'songs/last import type': SongFormat.OpenLyrics,
|
'songs/last import type': SongFormat.OpenLyrics,
|
||||||
'songs/update service on edit': False,
|
'songs/update service on edit': False,
|
||||||
'songs/add song from service': True,
|
'songs/add song from service': True,
|
||||||
|
@ -37,7 +37,7 @@ class SongUsageDeleteForm(QtWidgets.QDialog, Ui_SongUsageDeleteDialog, RegistryP
|
|||||||
"""
|
"""
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
super(SongUsageDeleteForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint |
|
super(SongUsageDeleteForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint |
|
||||||
QtCore.Qt.WindowTitleHint)
|
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.button_box.clicked.connect(self.on_button_box_clicked)
|
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?'),
|
'Delete Selected Song Usage Events?'),
|
||||||
translate('SongUsagePlugin.SongUsageDeleteForm',
|
translate('SongUsagePlugin.SongUsageDeleteForm',
|
||||||
'Are you sure you want to delete selected Song Usage data?'),
|
'Are you sure you want to delete selected Song Usage data?'),
|
||||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
|
defaultButton=QtWidgets.QMessageBox.No)
|
||||||
QtWidgets.QMessageBox.No),
|
|
||||||
QtWidgets.QMessageBox.No)
|
|
||||||
if ret == QtWidgets.QMessageBox.Yes:
|
if ret == QtWidgets.QMessageBox.Yes:
|
||||||
delete_date = self.delete_calendar.selectedDate().toPyDate()
|
delete_date = self.delete_calendar.selectedDate().toPyDate()
|
||||||
self.manager.delete_all_objects(SongUsageItem, SongUsageItem.usagedate <= delete_date)
|
self.manager.delete_all_objects(SongUsageItem, SongUsageItem.usagedate <= delete_date)
|
||||||
|
@ -44,7 +44,8 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP
|
|||||||
"""
|
"""
|
||||||
Initialise the form
|
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.plugin = plugin
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
|
@ -25,17 +25,26 @@ backend for the SongsUsage plugin
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from sqlalchemy import Column, types
|
from sqlalchemy import Table, Column, types
|
||||||
|
|
||||||
from openlp.core.lib.db import get_upgrade_op
|
from openlp.core.lib.db import get_upgrade_op
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
__version__ = 1
|
__version__ = 2
|
||||||
|
|
||||||
|
|
||||||
def upgrade_1(session, metadata):
|
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
|
This upgrade adds two new fields to the songusage database
|
||||||
|
|
||||||
@ -43,5 +52,7 @@ def upgrade_1(session, metadata):
|
|||||||
:param metadata: SQLAlchemy MetaData object
|
:param metadata: SQLAlchemy MetaData object
|
||||||
"""
|
"""
|
||||||
op = get_upgrade_op(session)
|
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('plugin_name', types.Unicode(20), server_default=''))
|
||||||
op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))
|
op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
Package to test the openlp.core.lib package.
|
Package to test the openlp.core.lib package.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from tempfile import mkdtemp
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import patch, MagicMock
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
@ -30,13 +33,27 @@ from sqlalchemy.pool import NullPool
|
|||||||
from sqlalchemy.orm.scoping import ScopedSession
|
from sqlalchemy.orm.scoping import ScopedSession
|
||||||
from sqlalchemy import MetaData
|
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):
|
class TestDB(TestCase):
|
||||||
"""
|
"""
|
||||||
A test case for all the tests for the :mod:`~openlp.core.lib.db` module.
|
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):
|
def test_init_db_calls_correct_functions(self):
|
||||||
"""
|
"""
|
||||||
Test that the init_db function makes the correct function calls
|
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)
|
MockedAppLocation.get_section_data_path.assert_called_with(test_plugin)
|
||||||
mocked_delete_file.assert_called_with(test_location)
|
mocked_delete_file.assert_called_with(test_location)
|
||||||
self.assertFalse(result, 'The result of delete_file should be False (was rigged that way)')
|
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')
|
||||||
|
@ -29,7 +29,7 @@ class TestProjectorConstants(TestCase):
|
|||||||
"""
|
"""
|
||||||
Test specific functions in the projector constants module.
|
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
|
Test building PJLINK_DEFAULT_CODES dictionary
|
||||||
"""
|
"""
|
||||||
|
@ -384,21 +384,6 @@ class TestPJLink(TestCase):
|
|||||||
self.assertEquals("{test}".format(test=mock_send_command.call_args),
|
self.assertEquals("{test}".format(test=mock_send_command.call_args),
|
||||||
"call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH))
|
"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')
|
@patch.object(pjlink_test, 'disconnect_from_host')
|
||||||
def socket_abort_test(self, mock_disconnect):
|
def socket_abort_test(self, mock_disconnect):
|
||||||
"""
|
"""
|
||||||
|
@ -27,11 +27,15 @@ PREREQUISITE: add_record() and get_all() functions validated.
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from unittest import TestCase
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
|
from unittest import TestCase, skip
|
||||||
from unittest.mock import MagicMock, patch
|
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.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.resources.projector.data import TEST_DB_PJLINK1, TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
|
||||||
from tests.utils.constants import TEST_RESOURCES_PATH
|
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||||
@ -85,6 +89,42 @@ def add_records(projector_db, test):
|
|||||||
return added
|
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):
|
class TestProjectorDB(TestCase):
|
||||||
"""
|
"""
|
||||||
Test case for ProjectorDB
|
Test case for ProjectorDB
|
||||||
@ -94,7 +134,9 @@ class TestProjectorDB(TestCase):
|
|||||||
"""
|
"""
|
||||||
Set up anything necessary for all tests
|
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()
|
self.projector = ProjectorDB()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@ -103,15 +145,8 @@ class TestProjectorDB(TestCase):
|
|||||||
"""
|
"""
|
||||||
self.projector.session.close()
|
self.projector.session.close()
|
||||||
self.projector = None
|
self.projector = None
|
||||||
retries = 0
|
# Ignore errors since windows can have problems with locked files
|
||||||
while retries < 5:
|
shutil.rmtree(self.tmp_folder, ignore_errors=True)
|
||||||
try:
|
|
||||||
if os.path.exists(TEST_DB):
|
|
||||||
os.unlink(TEST_DB)
|
|
||||||
break
|
|
||||||
except:
|
|
||||||
time.sleep(1)
|
|
||||||
retries += 1
|
|
||||||
|
|
||||||
def test_find_record_by_ip(self):
|
def test_find_record_by_ip(self):
|
||||||
"""
|
"""
|
||||||
@ -271,10 +306,10 @@ class TestProjectorDB(TestCase):
|
|||||||
|
|
||||||
# THEN: __repr__ should return a proper string
|
# THEN: __repr__ should return a proper string
|
||||||
self.assertEqual(str(projector),
|
self.assertEqual(str(projector),
|
||||||
'< Projector(id="0", ip="127.0.0.1", port="4352", pin="None", name="Test One", '
|
'< Projector(id="0", ip="127.0.0.1", port="4352", mac_adx="None", pin="None", '
|
||||||
'location="Somewhere over the rainbow", notes="Not again", pjlink_name="TEST", '
|
'name="Test One", location="Somewhere over the rainbow", notes="Not again", '
|
||||||
'manufacturer="IN YOUR DREAMS", model="OpenLP", serial_no="None", other="None", '
|
'pjlink_name="TEST", manufacturer="IN YOUR DREAMS", model="OpenLP", serial_no="None", '
|
||||||
'sources="None", source_list="[]", model_filter="None", model_lamp="None", '
|
'other="None", sources="None", source_list="[]", model_filter="None", model_lamp="None", '
|
||||||
'sw_version="None") >',
|
'sw_version="None") >',
|
||||||
'Projector.__repr__() should have returned a proper representation string')
|
'Projector.__repr__() should have returned a proper representation string')
|
||||||
|
|
||||||
|
@ -27,12 +27,6 @@ from unittest.mock import MagicMock, patch, call
|
|||||||
|
|
||||||
from openlp.core.ui.formattingtagform import FormattingTagForm
|
from openlp.core.ui.formattingtagform import FormattingTagForm
|
||||||
|
|
||||||
# TODO: Tests Still TODO
|
|
||||||
# __init__
|
|
||||||
# exec
|
|
||||||
# on_saved_clicked
|
|
||||||
# _reloadTable
|
|
||||||
|
|
||||||
|
|
||||||
class TestFormattingTagForm(TestCase):
|
class TestFormattingTagForm(TestCase):
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ from PyQt5 import QtCore
|
|||||||
|
|
||||||
from openlp.core.common import Registry, is_macosx, Settings
|
from openlp.core.common import Registry, is_macosx, Settings
|
||||||
from openlp.core.lib import ScreenList, PluginManager
|
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.media import MediaController
|
||||||
from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
|
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.web_view.setHtml.call_count, 1, 'setHTML should be called once')
|
||||||
self.assertEquals(main_display.media_controller.video.call_count, 1,
|
self.assertEquals(main_display.media_controller.video.call_count, 1,
|
||||||
'Media Controller video should have been called once')
|
'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()
|
||||||
|
@ -176,7 +176,7 @@ class TestThemeManager(TestCase):
|
|||||||
self.assertTrue(result)
|
self.assertTrue(result)
|
||||||
mocked_qmessagebox_question.assert_called_once_with(
|
mocked_qmessagebox_question.assert_called_once_with(
|
||||||
theme_manager, 'Theme Already Exists', 'Theme Theme Name already exists. Do you want to replace it?',
|
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):
|
def test_over_write_message_box_no(self):
|
||||||
"""
|
"""
|
||||||
@ -196,7 +196,7 @@ class TestThemeManager(TestCase):
|
|||||||
self.assertFalse(result)
|
self.assertFalse(result)
|
||||||
mocked_qmessagebox_question.assert_called_once_with(
|
mocked_qmessagebox_question.assert_called_once_with(
|
||||||
theme_manager, 'Theme Already Exists', 'Theme Theme Name already exists. Do you want to replace it?',
|
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):
|
def test_unzip_theme(self):
|
||||||
"""
|
"""
|
||||||
|
@ -29,45 +29,11 @@ from bs4 import BeautifulSoup
|
|||||||
|
|
||||||
from openlp.plugins.bibles.lib.importers.http import BSExtract
|
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):
|
class TestBSExtract(TestCase):
|
||||||
"""
|
"""
|
||||||
Test the BSExtractClass
|
Test the BSExtractClass
|
||||||
"""
|
"""
|
||||||
# TODO: Items left to test
|
|
||||||
# BSExtract
|
|
||||||
# __init__
|
|
||||||
# get_bible_chapter
|
|
||||||
# get_books_from_http
|
|
||||||
# _get_application
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.get_soup_for_bible_ref_patcher = patch('openlp.plugins.bibles.lib.importers.http.get_soup_for_bible_ref')
|
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')
|
self.log_patcher = patch('openlp.plugins.bibles.lib.importers.http.log')
|
||||||
|
@ -68,7 +68,8 @@ class TestLib(TestCase, TestMixin):
|
|||||||
"""
|
"""
|
||||||
# GIVEN: Some test data which contains different references to parse, with the expected results.
|
# 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': ''})):
|
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 = [
|
test_data = [
|
||||||
# Input reference, book name, chapter + verse reference
|
# Input reference, book name, chapter + verse reference
|
||||||
('Psalm 23', 'Psalm', '23'),
|
('Psalm 23', 'Psalm', '23'),
|
||||||
@ -84,6 +85,8 @@ class TestLib(TestCase, TestMixin):
|
|||||||
('Psalm 23{_and}24', 'Psalm', '23,24'),
|
('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', '1. John', '23'),
|
||||||
|
('1. John. 23', '1. John', '23'),
|
||||||
('1 John 23{to}24', '1 John', '23-24'),
|
('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}2', '1 John', '23:1-2'),
|
||||||
('1 John 23{verse}1{to}{end}', '1 John', '23:1-end'),
|
('1 John 23{verse}1{to}{end}', '1 John', '23:1-end'),
|
||||||
|
@ -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.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')
|
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):
|
def test_on_focus_search_tab_visible(self):
|
||||||
"""
|
"""
|
||||||
Test the correct widget gets focus when the BibleMediaItem receives focus
|
Test the correct widget gets focus when the BibleMediaItem receives focus
|
||||||
@ -480,7 +477,7 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
# WHEN: Calling update_auto_completer
|
# WHEN: Calling update_auto_completer
|
||||||
self.media_item.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(
|
mocked_set_case_insensitive_completer.assert_called_once_with(
|
||||||
['Book 1 ', 'Book 2 ', 'Book 3 '], mocked_search_edit)
|
['Book 1 ', 'Book 2 ', 'Book 3 '], mocked_search_edit)
|
||||||
|
|
||||||
@ -500,11 +497,11 @@ class TestMediaItem(TestCase, TestMixin):
|
|||||||
# WHEN: Calling update_auto_completer
|
# WHEN: Calling update_auto_completer
|
||||||
self.media_item.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(
|
mocked_set_case_insensitive_completer.assert_called_once_with(
|
||||||
['Book 1 ', 'Book 2 ', 'Book 3 '], mocked_search_edit)
|
['Book 1 ', 'Book 2 ', 'Book 3 '], mocked_search_edit)
|
||||||
|
|
||||||
def test_on_import_click_no_import_wizzard_attr(self):
|
def test_on_import_click_no_import_wizard_attr(self):
|
||||||
"""
|
"""
|
||||||
Test on_import_click when media_item does not have the `import_wizard` attribute. And the wizard was canceled.
|
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.assertTrue(mocked_bible_import_form.called)
|
||||||
self.assertFalse(mocked_reload_bibles.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
|
# GIVEN: An instance of :class:`MediaManagerItem` and a mocked import_wizard
|
||||||
mocked_import_wizard = MagicMock(**{'exec.return_value': True})
|
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)
|
self.assertTrue(self.mocked_main_window.information_message.called)
|
||||||
mocked_display_results.assert_called_once_with()
|
mocked_display_results.assert_called_once_with()
|
||||||
|
|
||||||
# TODO: Test text_search
|
|
||||||
|
|
||||||
def test_on_search_edit_text_changed_search_while_typing_disabled(self):
|
def test_on_search_edit_text_changed_search_while_typing_disabled(self):
|
||||||
"""
|
"""
|
||||||
Test on_search_edit_text_changed when 'search while typing' is disabled
|
Test on_search_edit_text_changed when 'search while typing' is disabled
|
||||||
|
@ -42,11 +42,6 @@ class TestPptviewController(TestCase, TestMixin):
|
|||||||
"""
|
"""
|
||||||
Test the PptviewController Class
|
Test the PptviewController Class
|
||||||
"""
|
"""
|
||||||
# TODO: Items left to test
|
|
||||||
# PptviewController
|
|
||||||
# start_process(self)
|
|
||||||
# kill
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""
|
"""
|
||||||
Set up the patches and mocks need for all tests.
|
Set up the patches and mocks need for all tests.
|
||||||
@ -103,24 +98,6 @@ class TestPptviewDocument(TestCase):
|
|||||||
"""
|
"""
|
||||||
Test the PptviewDocument Class
|
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):
|
def setUp(self):
|
||||||
"""
|
"""
|
||||||
Set up the patches and mocks need for all tests.
|
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
Loading…
Reference in New Issue
Block a user