openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #24398
Re: [Merge] lp:~alisonken1/openlp/projector-2.1-merge into lp:openlp
Review: Needs Fixing
Cleanup imports and inline comments
Diff comments:
> === modified file '.bzrignore'
> --- .bzrignore 2014-07-11 11:35:56 +0000
> +++ .bzrignore 2014-10-21 20:17:27 +0000
> @@ -33,3 +33,10 @@
> __pycache__
> *.dll
> .directory
> +*.kate-swp
> +# Git files
> +.git
> +.gitignore
> +# Rejected diff's
> +*.rej
> +*.~\?~
>
> === modified file 'openlp/core/common/__init__.py'
> --- openlp/core/common/__init__.py 2014-08-27 23:18:06 +0000
> +++ openlp/core/common/__init__.py 2014-10-21 20:17:27 +0000
> @@ -30,13 +30,17 @@
> The :mod:`common` module contains most of the components and libraries that make
> OpenLP work.
> """
> +import hashlib
> import re
> import os
> import logging
> import sys
> import traceback
> +from ipaddress import IPv4Address, IPv6Address, AddressValueError
> +from codecs import decode, encode
>
> from PyQt4 import QtCore
> +from PyQt4.QtCore import QCryptographicHash as QHash
>
> log = logging.getLogger(__name__ + '.__init__')
>
> @@ -154,6 +158,81 @@
> """
> return sys.platform.startswith('linux')
>
> +
> +def verify_ipv4(addr):
> + """
> + Validate an IPv4 address
> +
> + :param addr: Address to validate
> + :returns: bool
> + """
> + try:
> + valid = IPv4Address(addr)
> + return True
> + except AddressValueError:
> + return False
> +
> +
> +def verify_ipv6(addr):
> + """
> + Validate an IPv6 address
> +
> + :param addr: Address to validate
> + :returns: bool
> + """
> + try:
> + valid = IPv6Address(addr)
> + return True
> + except AddressValueError:
> + return False
> +
> +
> +def verify_ip_address(addr):
> + """
> + Validate an IP address as either IPv4 or IPv6
> +
> + :param addr: Address to validate
> + :returns: bool
> + """
> + return True if verify_ipv4(addr) else verify_ipv6(addr)
> +
> +
> +def md5_hash(salt, data):
> + """
> + Returns the hashed output of md5sum on salt,data
> + using Python3 hashlib
> +
> + :param salt: Initial salt
> + :param data: Data to hash
> + :returns: str
> + """
> + log.debug('md5_hash(salt="%s")' % salt)
> + hash_obj = hashlib.new('md5')
> + hash_obj.update(salt.encode('ascii'))
> + hash_obj.update(data.encode('ascii'))
> + hash_value = hash_obj.hexdigest()
> + log.debug('md5_hash() returning "%s"' % hash_value)
> + return hash_value
> +
> +
> +def qmd5_hash(salt, data):
> + """
> + Returns the hashed output of MD5Sum on salt, data
> + using PyQt4.QCryptographicHash.
> +
> + :param salt: Initial salt
> + :param data: Data to hash
> + :returns: str
> + """
> + log.debug('qmd5_hash(salt="%s"' % salt)
> + hash_obj = QHash(QHash.Md5)
> + hash_obj.addData(salt)
> + hash_obj.addData(data)
> + hash_value = hash_obj.result().toHex()
> + log.debug('qmd5_hash() returning "%s"' % hash_value)
> + return decode(hash_value.data(), 'ascii')
> +
> +
> from .openlpmixin import OpenLPMixin
> from .registry import Registry
> from .registrymixin import RegistryMixin
>
> === modified file 'openlp/core/common/registryproperties.py'
> --- openlp/core/common/registryproperties.py 2014-08-27 23:18:06 +0000
> +++ openlp/core/common/registryproperties.py 2014-10-21 20:17:27 +0000
> @@ -148,3 +148,12 @@
> if not hasattr(self, '_alerts_manager') or not self._alerts_manager:
> self._alerts_manager = Registry().get('alerts_manager')
> return self._alerts_manager
> +
> + @property
> + def projector_manager(self):
> + """
> + Adds the projector manager to the class dynamically
> + """
> + if not hasattr(self, '_projector_manager') or not self._projector_manager:
> + self._projector_manager = Registry().get('projector_manager')
> + return self._projector_manager
>
> === modified file 'openlp/core/common/settings.py'
> --- openlp/core/common/settings.py 2014-09-08 21:08:49 +0000
> +++ openlp/core/common/settings.py 2014-10-21 20:17:27 +0000
> @@ -275,6 +275,7 @@
> 'shortcuts/toolsAddToolItem': [],
> 'shortcuts/updateThemeImages': [],
> 'shortcuts/up': [QtGui.QKeySequence(QtCore.Qt.Key_Up)],
> + 'shortcuts/viewProjectorManagerItem': [QtGui.QKeySequence('F6')],
> 'shortcuts/viewThemeManagerItem': [QtGui.QKeySequence('F10')],
> 'shortcuts/viewMediaManagerItem': [QtGui.QKeySequence('F8')],
> 'shortcuts/viewPreviewPanel': [QtGui.QKeySequence('F11')],
> @@ -295,7 +296,15 @@
> 'user interface/main window splitter geometry': QtCore.QByteArray(),
> 'user interface/main window state': QtCore.QByteArray(),
> 'user interface/preview panel': True,
> - 'user interface/preview splitter geometry': QtCore.QByteArray()
> + 'user interface/preview splitter geometry': QtCore.QByteArray(),
> + 'projector/db type': 'sqlite',
> + 'projector/enable': True,
> + 'projector/connect on start': False,
> + 'projector/last directory import': '',
> + 'projector/last directory export': '',
> + 'projector/poll time': 20, # PJLink timeout is 30 seconds
> + 'projector/socket timeout': 5, # 5 second socket timeout
> + 'projector/source dialog type': 0 # Source select dialog box type
> }
> __file_path__ = ''
> __obsolete_settings__ = [
>
> === modified file 'openlp/core/common/uistrings.py'
> --- openlp/core/common/uistrings.py 2014-03-20 19:10:31 +0000
> +++ openlp/core/common/uistrings.py 2014-10-21 20:17:27 +0000
> @@ -99,6 +99,10 @@
> self.LiveBGError = translate('OpenLP.Ui', 'Live Background Error')
> self.LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar')
> self.Load = translate('OpenLP.Ui', 'Load')
> + self.Manufacturer = translate('OpenLP.Ui', 'Manufacturer', 'Singular')
> + self.Manufacturers = translate('OpenLP.Ui', 'Manufacturers', 'Plural')
> + self.Model = translate('OpenLP.Ui', 'Model', 'Singular')
> + self.Models = translate('OpenLP.Ui', 'Models', 'Plural')
> self.Minutes = translate('OpenLP.Ui', 'm', 'The abbreviated unit for minutes')
> self.Middle = translate('OpenLP.Ui', 'Middle')
> self.New = translate('OpenLP.Ui', 'New')
> @@ -118,6 +122,8 @@
> self.PlaySlidesToEnd = translate('OpenLP.Ui', 'Play Slides to End')
> self.Preview = translate('OpenLP.Ui', 'Preview')
> self.PrintService = translate('OpenLP.Ui', 'Print Service')
> + self.Projector = translate('OpenLP.Ui', 'Projector', 'Singular')
> + self.Projectors = translate('OpenLP.Ui', 'Projectors', 'Plural')
> self.ReplaceBG = translate('OpenLP.Ui', 'Replace Background')
> self.ReplaceLiveBG = translate('OpenLP.Ui', 'Replace live background.')
> self.ResetBG = translate('OpenLP.Ui', 'Reset Background')
>
> === modified file 'openlp/core/lib/__init__.py'
> --- openlp/core/lib/__init__.py 2014-07-11 11:35:56 +0000
> +++ openlp/core/lib/__init__.py 2014-10-21 20:17:27 +0000
> @@ -334,3 +334,6 @@
> from .imagemanager import ImageManager
> from .renderer import Renderer
> from .mediamanageritem import MediaManagerItem
> +from .projector.db import ProjectorDB, Projector
> +from .projector.pjlink1 import PJLink1
> +from .projector.constants import PJLINK_PORT, ERROR_MSG, ERROR_STRING
>
> === modified file 'openlp/core/lib/db.py'
> --- openlp/core/lib/db.py 2014-07-17 21:04:58 +0000
> +++ openlp/core/lib/db.py 2014-10-21 20:17:27 +0000
> @@ -48,20 +48,53 @@
> log = logging.getLogger(__name__)
>
>
> -def init_db(url, auto_flush=True, auto_commit=False):
> +def init_db(url, auto_flush=True, auto_commit=False, base=None):
> """
> Initialise and return the session and metadata for a database
>
> :param url: The database to initialise connection with
> :param auto_flush: Sets the flushing behaviour of the session
> :param auto_commit: Sets the commit behaviour of the session
> + :param base: If using declarative, the base class to bind with
> """
> engine = create_engine(url, poolclass=NullPool)
> - metadata = MetaData(bind=engine)
> + if base is None:
> + metadata = MetaData(bind=engine)
> + else:
> + base.metadata.bind = engine
> + metadata = None
> session = scoped_session(sessionmaker(autoflush=auto_flush, autocommit=auto_commit, bind=engine))
> return session, metadata
>
>
> +def init_url(plugin_name, db_file_name=None):
> + """
> + Return the database URL.
> +
> + :param plugin_name: The name of the plugin for the database creation.
> + :param db_file_name: The database file name. Defaults to None resulting in the plugin_name being used.
> + """
> + settings = Settings()
> + settings.beginGroup(plugin_name)
> + db_url = ''
> + db_type = settings.value('db type')
> + if db_type == 'sqlite':
> + if db_file_name is None:
> + db_url = 'sqlite:///%s/%s.sqlite' % (AppLocation.get_section_data_path(plugin_name), plugin_name)
> + else:
> + db_url = 'sqlite:///%s/%s' % (AppLocation.get_section_data_path(plugin_name), db_file_name)
> + else:
> + db_url = '%s://%s:%s@%s/%s' % (db_type, urlquote(settings.value('db username')),
> + urlquote(settings.value('db password')),
> + urlquote(settings.value('db hostname')),
> + urlquote(settings.value('db database')))
> + if db_type == 'mysql':
> + db_encoding = settings.value('db encoding')
> + db_url += '?charset=%s' % urlquote(db_encoding)
> + settings.endGroup()
> + return db_url
> +
> +
> def get_upgrade_op(session):
> """
> Create a migration context and an operations object for performing upgrades.
> @@ -159,7 +192,7 @@
> """
> Provide generic object persistence management
> """
> - def __init__(self, plugin_name, init_schema, db_file_name=None, upgrade_mod=None):
> + def __init__(self, plugin_name, init_schema, db_file_name=None, upgrade_mod=None, session=None):
> """
> Runs the initialisation process that includes creating the connection to the database and the tables if they do
> not exist.
> @@ -170,26 +203,15 @@
> :param upgrade_mod: The file name to use for this database. Defaults to None resulting in the plugin_name
> being used.
> """
> - settings = Settings()
> - settings.beginGroup(plugin_name)
> - self.db_url = ''
> self.is_dirty = False
> self.session = None
> - db_type = settings.value('db type')
> - if db_type == 'sqlite':
> - if db_file_name:
> - self.db_url = 'sqlite:///%s/%s' % (AppLocation.get_section_data_path(plugin_name), db_file_name)
> - else:
> - self.db_url = 'sqlite:///%s/%s.sqlite' % (AppLocation.get_section_data_path(plugin_name), plugin_name)
> + # See if we're using declarative_base with a pre-existing session.
> + log.debug('Manager: Testing for pre-existing session')
> + if session is not None:
> + log.debug('Manager: Using existing session')
> else:
> - self.db_url = '%s://%s:%s@%s/%s' % (db_type, urlquote(settings.value('db username')),
> - urlquote(settings.value('db password')),
> - urlquote(settings.value('db hostname')),
> - urlquote(settings.value('db database')))
> - if db_type == 'mysql':
> - db_encoding = settings.value('db encoding')
> - self.db_url += '?charset=%s' % urlquote(db_encoding)
> - settings.endGroup()
> + log.debug('Manager: Creating new session')
> + self.db_url = init_url(plugin_name, db_file_name)
> if upgrade_mod:
> try:
> db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
>
> === added directory 'openlp/core/lib/projector'
> === added file 'openlp/core/lib/projector/__init__.py'
> --- openlp/core/lib/projector/__init__.py 1970-01-01 00:00:00 +0000
> +++ openlp/core/lib/projector/__init__.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,41 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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.ui.projector`
> +
> + Initialization for the openlp.core.ui.projector modules.
> +"""
> +
> +
> +class DialogSourceStyle(object):
> + """
> + An enumeration for projector dialog box type.
> + """
> + Tabbed = 0
> + Single = 1
>
> === added file 'openlp/core/lib/projector/constants.py'
> --- openlp/core/lib/projector/constants.py 1970-01-01 00:00:00 +0000
> +++ openlp/core/lib/projector/constants.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,366 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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.constants` module
> +
> + Provides the constants used for projector errors/status/defaults
> +"""
> +
> +import logging
> +log = logging.getLogger(__name__)
> +log.debug('projector_constants loaded')
> +
> +from openlp.core.common import translate
> +
> +
> +__all__ = ['S_OK', 'E_GENERAL', 'E_NOT_CONNECTED', 'E_FAN', 'E_LAMP', 'E_TEMP',
> + 'E_COVER', 'E_FILTER', 'E_AUTHENTICATION', 'E_NO_AUTHENTICATION',
> + 'E_UNDEFINED', 'E_PARAMETER', 'E_UNAVAILABLE', 'E_PROJECTOR',
> + 'E_INVALID_DATA', 'E_WARN', 'E_ERROR', 'E_CLASS', 'E_PREFIX',
> + 'E_CONNECTION_REFUSED', 'E_REMOTE_HOST_CLOSED_CONNECTION', 'E_HOST_NOT_FOUND',
> + 'E_SOCKET_ACCESS', 'E_SOCKET_RESOURCE', 'E_SOCKET_TIMEOUT', 'E_DATAGRAM_TOO_LARGE',
> + 'E_NETWORK', 'E_ADDRESS_IN_USE', 'E_SOCKET_ADDRESS_NOT_AVAILABLE',
> + 'E_UNSUPPORTED_SOCKET_OPERATION', 'E_PROXY_AUTHENTICATION_REQUIRED',
> + 'E_SLS_HANDSHAKE_FAILED', 'E_UNFINISHED_SOCKET_OPERATION', 'E_PROXY_CONNECTION_REFUSED',
> + 'E_PROXY_CONNECTION_CLOSED', 'E_PROXY_CONNECTION_TIMEOUT', 'E_PROXY_NOT_FOUND',
> + 'E_PROXY_PROTOCOL', 'E_UNKNOWN_SOCKET_ERROR',
> + 'S_NOT_CONNECTED', 'S_CONNECTING', 'S_CONNECTED',
> + 'S_STATUS', 'S_OFF', 'S_INITIALIZE', 'S_STANDBY', 'S_WARMUP', 'S_ON', 'S_COOLDOWN',
> + 'S_INFO', 'S_NETWORK_SENDING', 'S_NETWORK_RECEIVED',
> + 'ERROR_STRING', 'CR', 'LF', 'PJLINK_ERST_STATUS', 'PJLINK_POWR_STATUS',
> + 'PJLINK_PORT', 'PJLINK_MAX_PACKET', 'TIMEOUT', 'ERROR_MSG', 'PJLINK_ERRORS',
> + 'STATUS_STRING', 'PJLINK_VALID_CMD', 'CONNECTION_ERRORS']
> +
> +# Set common constants.
> +CR = chr(0x0D) # \r
> +LF = chr(0x0A) # \n
> +PJLINK_PORT = 4352
> +TIMEOUT = 30.0
> +PJLINK_MAX_PACKET = 136
> +PJLINK_VALID_CMD = {'1': ['PJLINK', # Initial connection
> + 'POWR', # Power option
> + 'INPT', # Video sources option
> + 'AVMT', # Shutter option
> + 'ERST', # Error status option
> + 'LAMP', # Lamp(s) query (Includes fans)
> + 'INST', # Input sources available query
> + 'NAME', # Projector name query
> + 'INF1', # Manufacturer name query
> + 'INF2', # Product name query
> + 'INFO', # Other information query
> + 'CLSS' # PJLink class support query
> + ]}
> +
> +# Error and status codes
> +S_OK = E_OK = 0 # E_OK included since I sometimes forget
> +# Error codes. Start at 200 so we don't duplicate system error codes.
> +E_GENERAL = 200 # Unknown error
> +E_NOT_CONNECTED = 201
> +E_FAN = 202
> +E_LAMP = 203
> +E_TEMP = 204
> +E_COVER = 205
> +E_FILTER = 206
> +E_NO_AUTHENTICATION = 207 # PIN set and no authentication set on projector
> +E_UNDEFINED = 208 # ERR1
> +E_PARAMETER = 209 # ERR2
> +E_UNAVAILABLE = 210 # ERR3
> +E_PROJECTOR = 211 # ERR4
> +E_INVALID_DATA = 212
> +E_WARN = 213
> +E_ERROR = 214
> +E_AUTHENTICATION = 215 # ERRA
> +E_CLASS = 216
> +E_PREFIX = 217
> +
> +# Remap Qt socket error codes to projector error codes
> +E_CONNECTION_REFUSED = 230
> +E_REMOTE_HOST_CLOSED_CONNECTION = 231
> +E_HOST_NOT_FOUND = 232
> +E_SOCKET_ACCESS = 233
> +E_SOCKET_RESOURCE = 234
> +E_SOCKET_TIMEOUT = 235
> +E_DATAGRAM_TOO_LARGE = 236
> +E_NETWORK = 237
> +E_ADDRESS_IN_USE = 238
> +E_SOCKET_ADDRESS_NOT_AVAILABLE = 239
> +E_UNSUPPORTED_SOCKET_OPERATION = 240
> +E_PROXY_AUTHENTICATION_REQUIRED = 241
> +E_SLS_HANDSHAKE_FAILED = 242
> +E_UNFINISHED_SOCKET_OPERATION = 243
> +E_PROXY_CONNECTION_REFUSED = 244
> +E_PROXY_CONNECTION_CLOSED = 245
> +E_PROXY_CONNECTION_TIMEOUT = 246
> +E_PROXY_NOT_FOUND = 247
> +E_PROXY_PROTOCOL = 248
> +E_UNKNOWN_SOCKET_ERROR = -1
> +
> +# Status codes start at 300
> +S_NOT_CONNECTED = 300
> +S_CONNECTING = 301
> +S_CONNECTED = 302
> +S_INITIALIZE = 303
> +S_STATUS = 304
> +S_OFF = 305
> +S_STANDBY = 306
> +S_WARMUP = 307
> +S_ON = 308
> +S_COOLDOWN = 309
> +S_INFO = 310
> +
> +# Information that does not affect status
> +S_NETWORK_SENDING = 400
> +S_NETWORK_RECEIVED = 401
> +
> +CONNECTION_ERRORS = {E_NOT_CONNECTED, E_NO_AUTHENTICATION, E_AUTHENTICATION, E_CLASS,
> + E_PREFIX, E_CONNECTION_REFUSED, E_REMOTE_HOST_CLOSED_CONNECTION,
> + E_HOST_NOT_FOUND, E_SOCKET_ACCESS, E_SOCKET_RESOURCE, E_SOCKET_TIMEOUT,
> + E_DATAGRAM_TOO_LARGE, E_NETWORK, E_ADDRESS_IN_USE, E_SOCKET_ADDRESS_NOT_AVAILABLE,
> + E_UNSUPPORTED_SOCKET_OPERATION, E_PROXY_AUTHENTICATION_REQUIRED,
> + E_SLS_HANDSHAKE_FAILED, E_UNFINISHED_SOCKET_OPERATION, E_PROXY_CONNECTION_REFUSED,
> + E_PROXY_CONNECTION_CLOSED, E_PROXY_CONNECTION_TIMEOUT, E_PROXY_NOT_FOUND,
> + E_PROXY_PROTOCOL, E_UNKNOWN_SOCKET_ERROR
> + }
> +
> +PJLINK_ERRORS = {'ERRA': E_AUTHENTICATION, # Authentication error
> + 'ERR1': E_UNDEFINED, # Undefined command error
> + 'ERR2': E_PARAMETER, # Invalid parameter error
> + 'ERR3': E_UNAVAILABLE, # Projector busy
> + 'ERR4': E_PROJECTOR, # Projector or display failure
> + E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'ERRA'),
> + E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'ERR1'),
> + E_PARAMETER: translate('OpenLP.ProjectorConstants', 'ERR2'),
> + E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'ERR3'),
> + E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'ERR4')}
> +
> +# Map error/status codes to string
> +ERROR_STRING = {0: translate('OpenLP.ProjectorConstants', 'S_OK'),
> + E_GENERAL: translate('OpenLP.ProjectorConstants', 'E_GENERAL'),
> + E_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'E_NOT_CONNECTED'),
> + E_FAN: translate('OpenLP.ProjectorConstants', 'E_FAN'),
> + E_LAMP: translate('OpenLP.ProjectorConstants', 'E_LAMP'),
> + E_TEMP: translate('OpenLP.ProjectorConstants', 'E_TEMP'),
> + E_COVER: translate('OpenLP.ProjectorConstants', 'E_COVER'),
> + E_FILTER: translate('OpenLP.ProjectorConstants', 'E_FILTER'),
> + E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'E_AUTHENTICATION'),
> + E_NO_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'E_NO_AUTHENTICATION'),
> + E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'E_UNDEFINED'),
> + E_PARAMETER: translate('OpenLP.ProjectorConstants', 'E_PARAMETER'),
> + E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'E_UNAVAILABLE'),
> + E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'E_PROJECTOR'),
> + E_INVALID_DATA: translate('OpenLP.ProjectorConstants', 'E_INVALID_DATA'),
> + E_WARN: translate('OpenLP.ProjectorConstants', 'E_WARN'),
> + E_ERROR: translate('OpenLP.ProjectorConstants', 'E_ERROR'),
> + E_CLASS: translate('OpenLP.ProjectorConstants', 'E_CLASS'),
> + E_PREFIX: translate('OpenLP.ProjectorConstants', 'E_PREFIX'), # Last projector error
> + E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
> + 'E_CONNECTION_REFUSED'), # First QtSocket error
> + E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants',
> + 'E_REMOTE_HOST_CLOSED_CONNECTION'),
> + E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'E_HOST_NOT_FOUND'),
> + E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants', 'E_SOCKET_ACCESS'),
> + E_SOCKET_RESOURCE: translate('OpenLP.ProjectorConstants', 'E_SOCKET_RESOURCE'),
> + E_SOCKET_TIMEOUT: translate('OpenLP.ProjectorConstants', 'E_SOCKET_TIMEOUT'),
> + E_DATAGRAM_TOO_LARGE: translate('OpenLP.ProjectorConstants', 'E_DATAGRAM_TOO_LARGE'),
> + E_NETWORK: translate('OpenLP.ProjectorConstants', 'E_NETWORK'),
> + E_ADDRESS_IN_USE: translate('OpenLP.ProjectorConstants', 'E_ADDRESS_IN_USE'),
> + E_SOCKET_ADDRESS_NOT_AVAILABLE: translate('OpenLP.ProjectorConstants',
> + 'E_SOCKET_ADDRESS_NOT_AVAILABLE'),
> + E_UNSUPPORTED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
> + 'E_UNSUPPORTED_SOCKET_OPERATION'),
> + E_PROXY_AUTHENTICATION_REQUIRED: translate('OpenLP.ProjectorConstants',
> + 'E_PROXY_AUTHENTICATION_REQUIRED'),
> + E_SLS_HANDSHAKE_FAILED: translate('OpenLP.ProjectorConstants', 'E_SLS_HANDSHAKE_FAILED'),
> + E_UNFINISHED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
> + 'E_UNFINISHED_SOCKET_OPERATION'),
> + E_PROXY_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants', 'E_PROXY_CONNECTION_REFUSED'),
> + E_PROXY_CONNECTION_CLOSED: translate('OpenLP.ProjectorConstants', 'E_PROXY_CONNECTION_CLOSED'),
> + E_PROXY_CONNECTION_TIMEOUT: translate('OpenLP.ProjectorConstants', 'E_PROXY_CONNECTION_TIMEOUT'),
> + E_PROXY_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'E_PROXY_NOT_FOUND'),
> + E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants', 'E_PROXY_PROTOCOL'),
> + E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'E_UNKNOWN_SOCKET_ERROR')}
> +
> +STATUS_STRING = {S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'S_NOT_CONNECTED'),
> + S_CONNECTING: translate('OpenLP.ProjectorConstants', 'S_CONNECTING'),
> + S_CONNECTED: translate('OpenLP.ProjectorConstants', 'S_CONNECTED'),
> + S_STATUS: translate('OpenLP.ProjectorConstants', 'S_STATUS'),
> + S_OFF: translate('OpenLP.ProjectorConstants', 'S_OFF'),
> + S_INITIALIZE: translate('OpenLP.ProjectorConstants', 'S_INITIALIZE'),
> + S_STANDBY: translate('OpenLP.ProjectorConstants', 'S_STANDBY'),
> + S_WARMUP: translate('OpenLP.ProjectorConstants', 'S_WARMUP'),
> + S_ON: translate('OpenLP.ProjectorConstants', 'S_ON'),
> + S_COOLDOWN: translate('OpenLP.ProjectorConstants', 'S_COOLDOWN'),
> + S_INFO: translate('OpenLP.ProjectorConstants', 'S_INFO'),
> + S_NETWORK_SENDING: translate('OpenLP.ProjectorConstants', 'S_NETWORK_SENDING'),
> + S_NETWORK_RECEIVED: translate('OpenLP.ProjectorConstants', 'S_NETWORK_RECEIVED')}
> +
> +# Map error/status codes to message strings
> +ERROR_MSG = {E_OK: translate('OpenLP.ProjectorConstants', 'OK'), # E_OK | S_OK
> + E_GENERAL: translate('OpenLP.ProjectorConstants', 'General projector error'),
> + E_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected error'),
> + E_LAMP: translate('OpenLP.ProjectorConstants', 'Lamp error'),
> + E_FAN: translate('OpenLP.ProjectorConstants', 'Fan error'),
> + E_TEMP: translate('OpenLP.ProjectorConstants', 'High temperature detected'),
> + E_COVER: translate('OpenLP.ProjectorConstants', 'Cover open detected'),
> + E_FILTER: translate('OpenLP.ProjectorConstants', 'Check filter'),
> + E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'Authentication Error'),
> + E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'Undefined Command'),
> + E_PARAMETER: translate('OpenLP.ProjectorConstants', 'Invalid Parameter'),
> + E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'Projector Busy'),
> + E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'Projector/Display Error'),
> + E_INVALID_DATA: translate('OpenLP.ProjectorConstants', 'Invalid packet received'),
> + E_WARN: translate('OpenLP.ProjectorConstants', 'Warning condition detected'),
> + E_ERROR: translate('OpenLP.ProjectorConstants', 'Error condition detected'),
> + E_CLASS: translate('OpenLP.ProjectorConstants', 'PJLink class not supported'),
> + E_PREFIX: translate('OpenLP.ProjectorConstants', 'Invalid prefix character'),
> + E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
> + 'The connection was refused by the peer (or timed out)'),
> + E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants',
> + 'The remote host closed the connection'),
> + E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The host address was not found'),
> + E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants',
> + 'The socket operation failed because the application '
> + 'lacked the required privileges'),
> + E_SOCKET_RESOURCE: translate('OpenLP.ProjectorConstants',
> + 'The local system ran out of resources (e.g., too many sockets)'),
> + E_SOCKET_TIMEOUT: translate('OpenLP.ProjectorConstants',
> + 'The socket operation timed out'),
> + E_DATAGRAM_TOO_LARGE: translate('OpenLP.ProjectorConstants',
> + 'The datagram was larger than the operating system\'s limit'),
> + E_NETWORK: translate('OpenLP.ProjectorConstants',
> + 'An error occurred with the network (Possibly someone pulled the plug?)'),
> + E_ADDRESS_IN_USE: translate('OpenLP.ProjectorConstants',
> + 'The address specified with socket.bind() '
> + 'is already in use and was set to be exclusive'),
> + E_SOCKET_ADDRESS_NOT_AVAILABLE: translate('OpenLP.ProjectorConstants',
> + 'The address specified to socket.bind() '
> + 'does not belong to the host'),
> + E_UNSUPPORTED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
> + 'The requested socket operation is not supported by the local '
> + 'operating system (e.g., lack of IPv6 support)'),
> + E_PROXY_AUTHENTICATION_REQUIRED: translate('OpenLP.ProjectorConstants',
> + 'The socket is using a proxy, '
> + 'and the proxy requires authentication'),
> + E_SLS_HANDSHAKE_FAILED: translate('OpenLP.ProjectorConstants',
> + 'The SSL/TLS handshake failed'),
> + E_UNFINISHED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants',
> + 'The last operation attempted has not finished yet '
> + '(still in progress in the background)'),
> + E_PROXY_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
> + 'Could not contact the proxy server because the connection '
> + 'to that server was denied'),
> + E_PROXY_CONNECTION_CLOSED: translate('OpenLP.ProjectorConstants',
> + 'The connection to the proxy server was closed unexpectedly '
> + '(before the connection to the final peer was established)'),
> + E_PROXY_CONNECTION_TIMEOUT: translate('OpenLP.ProjectorConstants',
> + 'The connection to the proxy server timed out or the proxy '
> + 'server stopped responding in the authentication phase.'),
> + E_PROXY_NOT_FOUND: translate('OpenLP.ProjectorConstants',
> + 'The proxy address set with setProxy() was not found'),
> + E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants',
> + 'The connection negotiation with the proxy server because the response '
> + 'from the proxy server could not be understood'),
> + E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified error occurred'),
> + S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected'),
> + S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'),
> + S_CONNECTED: translate('OpenLP.ProjectorConstants', 'Connected'),
> + S_STATUS: translate('OpenLP.ProjectorConstants', 'Getting status'),
> + S_OFF: translate('OpenLP.ProjectorConstants', 'Off'),
> + S_INITIALIZE: translate('OpenLP.ProjectorConstants', 'Initialize in progress'),
> + S_STANDBY: translate('OpenLP.ProjectorConstants', 'Power in standby'),
> + S_WARMUP: translate('OpenLP.ProjectorConstants', 'Warmup in progress'),
> + S_ON: translate('OpenLP.ProjectorConstants', 'Power is on'),
> + S_COOLDOWN: translate('OpenLP.ProjectorConstants', 'Cooldown in progress'),
> + S_INFO: translate('OpenLP.ProjectorConstants', 'Projector Information available'),
> + S_NETWORK_SENDING: translate('OpenLP.ProjectorConstants', 'Sending data'),
> + S_NETWORK_RECEIVED: translate('OpenLP.ProjectorConstants', 'Received data')}
> +
> +# Map for ERST return codes to string
> +PJLINK_ERST_STATUS = {'0': ERROR_STRING[E_OK],
> + '1': ERROR_STRING[E_WARN],
> + '2': ERROR_STRING[E_ERROR]}
> +
> +# Map for POWR return codes to status code
> +PJLINK_POWR_STATUS = {'0': S_STANDBY,
> + '1': S_ON,
> + '2': S_COOLDOWN,
> + '3': S_WARMUP}
> +
> +PJLINK_DEFAULT_SOURCES = {'1': translate('OpenLP.DB', 'RGB'),
> + '2': translate('OpenLP.DB', 'Video'),
> + '3': translate('OpenLP.DB', 'Digital'),
> + '4': translate('OpenLP.DB', 'Storage'),
> + '5': translate('OpenLP.DB', 'Network')}
> +
> +PJLINK_DEFAULT_CODES = {'11': translate('OpenLP.DB', 'RGB 1'),
> + '12': translate('OpenLP.DB', 'RGB 2'),
> + '13': translate('OpenLP.DB', 'RGB 3'),
> + '14': translate('OpenLP.DB', 'RGB 4'),
> + '15': translate('OpenLP.DB', 'RGB 5'),
> + '16': translate('OpenLP.DB', 'RGB 6'),
> + '17': translate('OpenLP.DB', 'RGB 7'),
> + '18': translate('OpenLP.DB', 'RGB 8'),
> + '19': translate('OpenLP.DB', 'RGB 9'),
> + '21': translate('OpenLP.DB', 'Video 1'),
> + '22': translate('OpenLP.DB', 'Video 2'),
> + '23': translate('OpenLP.DB', 'Video 3'),
> + '24': translate('OpenLP.DB', 'Video 4'),
> + '25': translate('OpenLP.DB', 'Video 5'),
> + '26': translate('OpenLP.DB', 'Video 6'),
> + '27': translate('OpenLP.DB', 'Video 7'),
> + '28': translate('OpenLP.DB', 'Video 8'),
> + '29': translate('OpenLP.DB', 'Video 9'),
> + '31': translate('OpenLP.DB', 'Digital 1'),
> + '32': translate('OpenLP.DB', 'Digital 2'),
> + '33': translate('OpenLP.DB', 'Digital 3'),
> + '34': translate('OpenLP.DB', 'Digital 4'),
> + '35': translate('OpenLP.DB', 'Digital 5'),
> + '36': translate('OpenLP.DB', 'Digital 6'),
> + '37': translate('OpenLP.DB', 'Digital 7'),
> + '38': translate('OpenLP.DB', 'Digital 8'),
> + '39': translate('OpenLP.DB', 'Digital 9'),
> + '41': translate('OpenLP.DB', 'Storage 1'),
> + '42': translate('OpenLP.DB', 'Storage 2'),
> + '43': translate('OpenLP.DB', 'Storage 3'),
> + '44': translate('OpenLP.DB', 'Storage 4'),
> + '45': translate('OpenLP.DB', 'Storage 5'),
> + '46': translate('OpenLP.DB', 'Storage 6'),
> + '47': translate('OpenLP.DB', 'Storage 7'),
> + '48': translate('OpenLP.DB', 'Storage 8'),
> + '49': translate('OpenLP.DB', 'Storage 9'),
> + '51': translate('OpenLP.DB', 'Network 1'),
> + '52': translate('OpenLP.DB', 'Network 2'),
> + '53': translate('OpenLP.DB', 'Network 3'),
> + '54': translate('OpenLP.DB', 'Network 4'),
> + '55': translate('OpenLP.DB', 'Network 5'),
> + '56': translate('OpenLP.DB', 'Network 6'),
> + '57': translate('OpenLP.DB', 'Network 7'),
> + '58': translate('OpenLP.DB', 'Network 8'),
> + '59': translate('OpenLP.DB', 'Network 9')
> + }
>
> === added file 'openlp/core/lib/projector/db.py'
> --- openlp/core/lib/projector/db.py 1970-01-01 00:00:00 +0000
> +++ openlp/core/lib/projector/db.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,383 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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.db` module
> +
> + Provides the database functions for the Projector module.
> +
> + The Manufacturer, Model, Source tables keep track of the video source
> + strings used for display of input sources. The Source table maps
> + manufacturer-defined or user-defined strings from PJLink default strings
> + to end-user readable strings; ex: PJLink code 11 would map "RGB 1"
> + default string to "RGB PC (analog)" string.
> + (Future feature).
> +
> + The Projector table keeps track of entries for controlled projectors.
> +"""
> +
> +import logging
> +log = logging.getLogger(__name__)
> +log.debug('projector.lib.db module loaded')
> +
> +from os import path
> +
> +from sqlalchemy import Column, ForeignKey, Integer, MetaData, String
> +from sqlalchemy.ext.declarative import declarative_base, declared_attr
> +from sqlalchemy.orm import backref, relationship
> +
> +from openlp.core.lib.db import Manager, init_db, init_url
> +from openlp.core.lib.projector.constants import PJLINK_DEFAULT_SOURCES, PJLINK_DEFAULT_CODES
> +
> +metadata = MetaData()
> +Base = declarative_base(metadata)
> +
> +
> +class CommonBase(object):
> + """
> + Base class to automate table name and ID column.
> + """
> + @declared_attr
> + def __tablename__(cls):
> + return cls.__name__.lower()
> +
> + id = Column(Integer, primary_key=True)
> +
> +
> +class Manufacturer(CommonBase, Base):
> + """
> + Projector manufacturer table.
> +
> + Manufacturer:
> + name: Column(String(30))
> + models: Relationship(Model.id)
> +
> + Model table is related.
> + """
> + def __repr__(self):
> + """
> + Returns a basic representation of a Manufacturer table entry.
> + """
> + return '<Manufacturer(name="%s")>' % self.name
> +
> + name = Column(String(30))
> + models = relationship('Model',
> + order_by='Model.name',
> + backref='manufacturer',
> + cascade='all, delete-orphan',
> + primaryjoin='Manufacturer.id==Model.manufacturer_id',
> + lazy='joined')
> +
> +
> +class Model(CommonBase, Base):
> + """
> + Projector model table.
> +
> + Model:
> + name: Column(String(20))
> + sources: Relationship(Source.id)
> + manufacturer_id: Foreign_key(Manufacturer.id)
> +
> + Manufacturer table links here.
> + Source table is related.
> + """
> + def __repr__(self):
> + """
> + Returns a basic representation of a Model table entry.
> + """
> + return '<Model(name=%s)>' % self.name
> +
> + manufacturer_id = Column(Integer, ForeignKey('manufacturer.id'))
> + name = Column(String(20))
> + sources = relationship('Source',
> + order_by='Source.pjlink_name',
> + backref='model',
> + cascade='all, delete-orphan',
> + primaryjoin='Model.id==Source.model_id',
> + lazy='joined')
> +
> +
> +class Source(CommonBase, Base):
> + """
> + Projector video source table.
> +
> + Source:
> + pjlink_name: Column(String(15))
> + pjlink_code: Column(String(2))
> + text: Column(String(30))
> + model_id: Foreign_key(Model.id)
> +
> + Model table links here.
> +
> + These entries map PJLink input video source codes to text strings.
> + """
> + def __repr__(self):
> + """
> + Return basic representation of Source table entry.
> + """
> + return '<Source(pjlink_name="%s", pjlink_code="%s", text="%s")>' % \
> + (self.pjlink_name, self.pjlink_code, self.text)
> + model_id = Column(Integer, ForeignKey('model.id'))
> + pjlink_name = Column(String(15))
> + pjlink_code = Column(String(2))
> + text = Column(String(30))
> +
> +
> +class Projector(CommonBase, Base):
> + """
> + Projector table.
> +
> + Projector:
> + ip: Column(String(100)) # Allow for IPv6 or FQDN
> + port: Column(String(8))
> + pin: Column(String(20)) # Allow for test strings
> + name: Column(String(20))
> + location: Column(String(30))
> + notes: Column(String(200))
> + pjlink_name: Column(String(128)) # From projector (future)
> + manufacturer: Column(String(128)) # From projector (future)
> + model: Column(String(128)) # From projector (future)
> + other: Column(String(128)) # From projector (future)
> + sources: Column(String(128)) # From projector (future)
> + """
> + ip = Column(String(100))
> + port = Column(String(8))
> + pin = Column(String(20))
> + name = Column(String(20))
> + location = Column(String(30))
> + notes = Column(String(200))
> + pjlink_name = Column(String(128))
> + manufacturer = Column(String(128))
> + model = Column(String(128))
> + other = Column(String(128))
> + sources = Column(String(128))
> +
> +
> +class ProjectorSource(CommonBase, Base):
> + """
> + Projector local source table
> + This table allows mapping specific projector source input to a local
> + connection; i.e., '11': 'DVD Player'
> +
> + Projector Source:
> + projector_id: Foreign_key(Column(Projector.id))
> + code: Column(String(3)) # PJLink source code
> + text: Column(String(20)) # Text to display
> + """
> + code = Column(String(3))
> + text = Column(String(20))
> + projector_id = (Integer, ForeignKey('projector.id'))
> +
> +
> +class ProjectorDB(Manager):
> + """
> + Class to access the projector database.
> + """
> + def __init__(self, *args, **kwargs):
> + log.debug('ProjectorDB().__init__(args="%s", kwargs="%s")' % (args, kwargs))
> + super().__init__(plugin_name='projector',
> + init_schema=self.init_schema)
> + log.debug('ProjectorDB() Initialized using db url %s' % self.db_url)
> +
> + def init_schema(*args, **kwargs):
> + """
> + Setup the projector database and initialize the schema.
> +
> + Declarative uses table classes to define schema.
> + """
> + url = init_url('projector')
> + session, metadata = init_db(url, base=Base)
> + Base.metadata.create_all(checkfirst=True)
> + return session
> +
> + def get_projector_by_id(self, dbid):
> + """
> + Locate a DB record by record ID.
> +
> + :param dbid: DB record id
> + :returns: Projector() instance
> + """
> + log.debug('get_projector_by_id(id="%s")' % dbid)
> + projector = self.get_object_filtered(Projector, Projector.id == dbid)
> + if projector is None:
> + # Not found
> + log.warn('get_projector_by_id() did not find %s' % id)
> + return None
> + log.debug('get_projectorby_id() returning 1 entry for "%s" id="%s"' % (dbid, projector.id))
> + return projector
> +
> + def get_projector_all(self):
> + """
> + Retrieve all projector entries.
> +
> + :returns: List with Projector() instances used in Manager() QListWidget.
> + """
> + log.debug('get_all() called')
> + return_list = []
> + new_list = self.get_all_objects(Projector)
> + if new_list is None or new_list.count == 0:
> + return return_list
> + for new_projector in new_list:
> + return_list.append(new_projector)
> + log.debug('get_all() returning %s item(s)' % len(return_list))
> + return return_list
> +
> + def get_projector_by_ip(self, ip):
> + """
> + Locate a projector by host IP/Name.
> +
> + :param ip: Host IP/Name
> + :returns: Projector() instance
> + """
> + log.debug('get_projector_by_ip(ip="%s")' % ip)
> + projector = self.get_object_filtered(Projector, Projector.ip == ip)
> + if projector is None:
> + # Not found
> + log.warn('get_projector_by_ip() did not find %s' % ip)
> + return None
> + log.debug('get_projectorby_ip() returning 1 entry for "%s" id="%s"' % (ip, projector.id))
> + return projector
> +
> + def get_projector_by_name(self, name):
> + """
> + Locate a projector by name field
> +
> + :param name: Name of projector
> + :returns: Projector() instance
> + """
> + log.debug('get_projector_by_name(name="%s")' % name)
> + projector = self.get_object_filtered(Projector, Projector.name == name)
> + if projector is None:
> + # Not found
> + log.warn('get_projector_by_name() did not find "%s"' % name)
> + return None
> + log.debug('get_projector_by_name() returning one entry for "%s" id="%s"' % (name, projector.id))
> + return projector
> +
> + def add_projector(self, projector):
> + """
> + Add a new projector entry
> +
> + :param projector: Projector() instance to add
> + :returns: bool
> + True if entry added
> + False if entry already in DB or db error
> + """
> + old_projector = self.get_object_filtered(Projector, Projector.ip == projector.ip)
> + if old_projector is not None:
> + log.warn('add_new() skipping entry ip="%s" (Already saved)' % old_projector.ip)
> + return False
> + log.debug('add_new() saving new entry')
> + log.debug('ip="%s", name="%s", location="%s"' % (projector.ip,
> + projector.name,
> + projector.location))
> + log.debug('notes="%s"' % projector.notes)
> + return self.save_object(projector)
> +
> + def update_projector(self, projector=None):
> + """
> + Update projector entry
> +
> + :param projector: Projector() instance with new information
> + :returns: bool
> + True if DB record updated
> + False if entry not in DB or DB error
> + """
> + if projector is None:
> + log.error('No Projector() instance to update - cancelled')
> + return False
> + old_projector = self.get_object_filtered(Projector, Projector.id == projector.id)
> + if old_projector is None:
> + log.error('Edit called on projector instance not in database - cancelled')
> + return False
> + log.debug('(%s) Updating projector with dbid=%s' % (projector.ip, projector.id))
> + old_projector.ip = projector.ip
> + old_projector.name = projector.name
> + old_projector.location = projector.location
> + old_projector.pin = projector.pin
> + old_projector.port = projector.port
> + old_projector.pjlink_name = projector.pjlink_name
> + old_projector.manufacturer = projector.manufacturer
> + old_projector.model = projector.model
> + old_projector.other = projector.other
> + old_projector.sources = projector.sources
> + return self.save_object(old_projector)
> +
> + def delete_projector(self, projector):
> + """
> + Delete an entry by record id
> +
> + :param projector: Projector() instance to delete
> + :returns: bool
> + True if record deleted
> + False if DB error
> + """
> + deleted = self.delete_object(Projector, projector.id)
> + if deleted:
> + log.debug('delete_by_id() Removed entry id="%s"' % projector.id)
> + else:
> + log.error('delete_by_id() Entry id="%s" not deleted for some reason' % projector.id)
> + return deleted
> +
> + def get_source_list(self, projector):
> + """
> + Retrieves the source inputs pjlink code-to-text if available based on
> + manufacturer and model.
> + If not available, then returns the PJLink code to default text.
> +
> + :param projector: Projector instance
> + :returns: dict
> + key: (str) PJLink code for source
> + value: (str) From ProjectorSource, Sources tables or PJLink default code list
> + """
> + # Get manufacturer-defined source text
> + model_list = self.get_all_objects(Model, Model.name == projector.model)
> + if model_list is None or len(model_list) < 1:
> + # No entry for model, so see if there's a default entry
> + default_list = self.get_object_filtered(Manufacturer, Manufacturer.name == projector.manufacturer)
> + if default_list is None or len(default_list) < 1:
> + # No entry for manufacturer, so can't check for default text
> + model_list = {}
> + else:
> + model_list = default_list.models['DEFAULT']
> + # Get user-defined source text
> + local_list = self.get_all_objects(ProjectorSource, ProjectorSource.projector_id == projector.dbid)
> + if local_list is None or len(local_list) < 1:
> + local_list = {}
> + source_dict = {}
> + for source in projector.source_available:
> + if source in local_list:
> + # User defined source text
> + source_dict[source] = local_list[source]
> + elif source in model_list:
> + # Default manufacturer defined source text
> + source_dict[source] = model_list[source]
> + else:
> + # Default PJLink source text
> + source_dict[source] = PJLINK_DEFAULT_CODES[source]
> + return source_dict
>
> === added file 'openlp/core/lib/projector/pjlink1.py'
> --- openlp/core/lib/projector/pjlink1.py 1970-01-01 00:00:00 +0000
> +++ openlp/core/lib/projector/pjlink1.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,935 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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.pjlink1` module
> + Provides the necessary functions for connecting to a PJLink-capable projector.
> +
> + See PJLink Class 1 Specifications for details.
> + http://pjlink.jbmia.or.jp/english/dl.html
> + Section 5-1 PJLink Specifications
> + Section 5-5 Guidelines for Input Terminals
> +
> + NOTE:
> + Function names follow the following syntax:
> + def process_CCCC(...):
> + WHERE:
> + CCCC = PJLink command being processed.
> +"""
> +
> +import logging
> +log = logging.getLogger(__name__)
> +
> +log.debug('rpjlink1 loaded')
Typo in pjlink
> +
> +__all__ = ['PJLink1']
> +
> +from codecs import decode
> +
> +from PyQt4 import QtCore, QtGui
> +from PyQt4.QtCore import pyqtSignal, pyqtSlot
> +from PyQt4.QtNetwork import QAbstractSocket, QTcpSocket
> +
> +from openlp.core.common import translate, qmd5_hash
> +from openlp.core.lib.projector.constants import *
> +
> +# Shortcuts
> +SocketError = QAbstractSocket.SocketError
> +SocketSTate = QAbstractSocket.SocketState
> +
> +PJLINK_PREFIX = '%'
> +PJLINK_CLASS = '1'
> +PJLINK_HEADER = '%s%s' % (PJLINK_PREFIX, PJLINK_CLASS)
> +PJLINK_SUFFIX = CR
> +
> +
> +class PJLink1(QTcpSocket):
> + """
> + Socket service for connecting to a PJLink-capable projector.
> + """
> + # Signals sent by this module
> + changeStatus = pyqtSignal(str, int, str)
> + projectorNetwork = pyqtSignal(int) # Projector network activity
> + projectorStatus = pyqtSignal(int) # Status update
> + projectorAuthentication = pyqtSignal(str) # Authentication error
> + projectorNoAuthentication = pyqtSignal(str) # PIN set and no authentication needed
> + projectorReceivedData = pyqtSignal() # Notify when received data finished processing
> + projectorUpdateIcons = pyqtSignal() # Update the status icons on toolbar
> +
> + def __init__(self, name=None, ip=None, port=PJLINK_PORT, pin=None, *args, **kwargs):
> + """
> + Setup for instance.
> +
> + :param name: Display name
> + :param ip: IP address to connect to
> + :param port: Port to use. Default to PJLINK_PORT
> + :param pin: Access pin (if needed)
> +
> + Optional parameters
> + :param dbid: Database ID number
> + :param location: Location where projector is physically located
> + :param notes: Extra notes about the projector
> + """
> + log.debug('PJlink(args="%s" kwargs="%s")' % (args, kwargs))
> + self.name = name
> + self.ip = ip
> + self.port = port
> + self.pin = pin
> + super(PJLink1, self).__init__()
> + self.dbid = None
> + self.location = None
> + self.notes = None
> + # Allowances for Projector Wizard option
> + if 'dbid' in kwargs:
> + self.dbid = kwargs['dbid']
> + else:
> + self.dbid = None
> + if 'location' in kwargs:
> + self.location = kwargs['location']
> + else:
> + self.location = None
> + if 'notes' in kwargs:
> + self.notes = kwargs['notes']
> + else:
> + self.notes = None
> + if 'wizard' in kwargs:
> + self.new_wizard = True
> + else:
> + self.new_wizard = False
> + if 'poll_time' in kwargs:
> + # Convert seconds to milliseconds
> + self.poll_time = kwargs['poll_time'] * 1000
> + else:
> + # Default 20 seconds
> + self.poll_time = 20000
> + if 'socket_timeout' in kwargs:
> + # Convert seconds to milliseconds
> + self.socket_timeout = kwargs['socket_timeout'] * 1000
> + else:
> + # Default is 5 seconds
> + self.socket_timeout = 5000
> + self.i_am_running = False
> + self.status_connect = S_NOT_CONNECTED
> + self.last_command = ''
> + self.projector_status = S_NOT_CONNECTED
> + self.error_status = S_OK
> + # Socket information
> + # Account for self.readLine appending \0 and/or extraneous \r
> + self.maxSize = PJLINK_MAX_PACKET + 2
> + self.setReadBufferSize(self.maxSize)
> + # PJLink projector information
> + self.pjlink_class = '1' # Default class
> + self.reset_information()
> + # Set from ProjectorManager.add_projector()
> + self.widget = None # QListBox entry
> + self.timer = None # Timer that calls the poll_loop
> + self.send_queue = []
> + self.send_busy = False
> + self.socket_timer = None # Test for send_busy and brain-dead projectors
> + # Map command to function
> + self.PJLINK1_FUNC = {'AVMT': self.process_avmt,
> + 'CLSS': self.process_clss,
> + 'ERST': self.process_erst,
> + 'INFO': self.process_info,
> + 'INF1': self.process_inf1,
> + 'INF2': self.process_inf2,
> + 'INPT': self.process_inpt,
> + 'INST': self.process_inst,
> + 'LAMP': self.process_lamp,
> + 'NAME': self.process_name,
> + 'PJLINK': self.check_login,
> + 'POWR': self.process_powr
> + }
> +
> + def reset_information(self):
> + """
> + Reset projector-specific information to default
> + """
> + log.debug('(%s) reset_information() connect status is %s' % (self.ip, self.state()))
> + self.power = S_OFF
> + self.pjlink_name = None
> + self.manufacturer = None
> + self.model = None
> + self.shutter = None
> + self.mute = None
> + self.lamp = None
> + self.fan = None
> + self.source_available = None
> + self.source = None
> + self.other_info = None
> + if hasattr(self, 'timer'):
> + self.timer.stop()
> + if hasattr(self, 'socket_timer'):
> + self.socket_timer.stop()
> + self.send_queue = []
> + self.send_busy = False
> +
> + def thread_started(self):
> + """
> + Connects signals to methods when thread is started.
> + """
> + log.debug('(%s) Thread starting' % self.ip)
> + self.i_am_running = True
> + self.connected.connect(self.check_login)
> + self.disconnected.connect(self.disconnect_from_host)
> + self.error.connect(self.get_error)
> +
> + def thread_stopped(self):
> + """
> + Cleanups when thread is stopped.
> + """
> + log.debug('(%s) Thread stopped' % self.ip)
> + try:
> + self.connected.disconnect(self.check_login)
> + except TypeError:
> + pass
> + try:
> + self.disconnected.disconnect(self.disconnect_from_host)
> + except TypeError:
> + pass
> + try:
> + self.error.disconnect(self.get_error)
> + except TypeError:
> + pass
> + try:
> + self.projectorReceivedData.disconnect(self._send_command)
> + except TypeError:
> + pass
> + self.disconnect_from_host()
> + self.deleteLater()
> + self.i_am_running = False
> +
> + def socket_abort(self):
> + """
> + Aborts connection and closes socket in case of brain-dead projectors.
> + Should normally be called by socket_timer().
> + """
> + log.debug('(%s) socket_abort() - Killing connection' % self.ip)
> + self.disconnect_from_host(abort=True)
> +
> + def poll_loop(self):
> + """
> + Retrieve information from projector that changes.
> + Normally called by timer().
> + """
> + if self.state() != self.ConnectedState:
> + return
> + log.debug('(%s) Updating projector status' % self.ip)
> + # Reset timer in case we were called from a set command
> + if self.timer.interval() < self.poll_time:
> + # Reset timer to 5 seconds
> + self.timer.setInterval(self.poll_time)
> + self.timer.start()
> + for command in ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT']:
> + # Changeable information
> + self.send_command(command, queue=True)
> + if self.power == S_ON and self.source_available is None:
> + self.send_command('INST', queue=True)
> + if self.other_info is None:
> + self.send_command('INFO', queue=True)
> + if self.manufacturer is None:
> + self.send_command('INF1', queue=True)
> + if self.model is None:
> + self.send_command('INF2', queue=True)
> + if self.pjlink_name is None:
> + self.send_command('NAME', queue=True)
> + if self.power == S_ON and self.source_available is None:
> + self.send_command('INST', queue=True)
> +
> + def _get_status(self, status):
> + """
> + Helper to retrieve status/error codes and convert to strings.
> +
> + :param status: Status/Error code
> + :returns: (Status/Error code, String)
> + """
> + if status in ERROR_STRING:
> + return (ERROR_STRING[status], ERROR_MSG[status])
Are the parentheses necessary?
> + elif status in STATUS_STRING:
> + return (STATUS_STRING[status], ERROR_MSG[status])
Are the parentheses necessary?
> + else:
> + return (status, translate('OpenLP.PJLink1', 'Unknown status'))
Are the parentheses necessary?
> +
> + def change_status(self, status, msg=None):
> + """
> + Check connection/error status, set status for projector, then emit status change signal
> + for gui to allow changing the icons.
> +
> + :param status: Status code
> + :param msg: Optional message
> + """
> + message = translate('OpenLP.PJLink1', 'No message') if msg is None else msg
> + (code, message) = self._get_status(status)
> + if msg is not None:
> + message = msg
> + if status in CONNECTION_ERRORS:
> + # Projector, connection state
> + self.projector_status = self.error_status = self.status_connect = E_NOT_CONNECTED
> + elif status >= S_NOT_CONNECTED and status < S_STATUS:
> + self.status_connect = status
> + self.projector_status = S_NOT_CONNECTED
> + elif status < S_NETWORK_SENDING:
> + self.status_connect = S_CONNECTED
> + self.projector_status = status
> + (status_code, status_message) = self._get_status(self.status_connect)
> + log.debug('(%s) status_connect: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
> + (status_code, status_message) = self._get_status(self.projector_status)
> + log.debug('(%s) projector_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
> + (status_code, status_message) = self._get_status(self.error_status)
> + log.debug('(%s) error_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
> + self.changeStatus.emit(self.ip, status, message)
> +
> + def check_command(self, cmd):
> + """
> + Verifies command is valid based on PJLink class.
> +
> + :param cmd: PJLink command to validate.
> + :returns: bool
> + True if command is valid PJLink command
> + False if command is not a valid PJLink command
> + """
> + return self.pjlink_class in PJLINK_VALID_CMD and \
> + cmd in PJLINK_VALID_CMD[self.pjlink_class]
> +
> + @pyqtSlot()
> + def check_login(self, data=None):
> + """
> + Processes the initial connection and authentication (if needed).
> + Starts poll timer if connection is established.
> +
> + :param data: Optional data if called from another routine
> + """
> + log.debug('(%s) check_login(data="%s")' % (self.ip, data))
> + if data is None:
> + # Reconnected setup?
> + if not self.waitForReadyRead(2000):
> + # Possible timeout issue
> + log.error('(%s) Socket timeout waiting for login' % self.ip)
> + self.change_status(E_SOCKET_TIMEOUT)
> + return
> + read = self.readLine(self.maxSize)
> + dontcare = self.readLine(self.maxSize) # Clean out the trailing \r\n
> + if read is None:
> + log.warn('(%s) read is None - socket error?' % self.ip)
> + return
> + elif len(read) < 8:
> + log.warn('(%s) Not enough data read)' % self.ip)
> + return
> + data = decode(read, 'ascii')
> + # Possibility of extraneous data on input when reading.
> + # Clean out extraneous characters in buffer.
> + dontcare = self.readLine(self.maxSize)
> + log.debug('(%s) check_login() read "%s"' % (self.ip, data.strip()))
> + # At this point, we should only have the initial login prompt with
> + # possible authentication
> + if not data.upper().startswith('PJLINK'):
> + # Invalid response
> + return self.disconnect_from_host()
> + # Test for authentication error
> + if '=' in data:
> + data_check = data.strip().split('=')
> + else:
> + data_check = data.strip().split(' ')
> + log.debug('(%s) data_check="%s"' % (self.ip, data_check))
> + # PJLink initial login will be:
> + # 'PJLink 0' - Unauthenticated login - no extra steps required.
> + # 'PJLink 1 XXXXXX' Authenticated login - extra processing required.
> + # Oops - projector error
> + if data_check[1].upper() == 'ERRA':
> + # Authentication error
> + self.disconnect_from_host()
> + self.change_status(E_AUTHENTICATION)
> + log.debug('(%s) emitting projectorAuthentication() signal' % self.name)
> + return
> + elif data_check[1] == '0' and self.pin is not None:
> + # Pin set and no authentication needed
> + self.disconnect_from_host()
> + self.change_status(E_AUTHENTICATION)
> + log.debug('(%s) emitting projectorNoAuthentication() signal' % self.name)
> + self.projectorNoAuthentication.emit(self.name)
> + return
> + elif data_check[1] == '1':
> + # Authenticated login with salt
> + log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2]))
> + log.debug('(%s) pin="%s"' % (self.ip, self.pin))
> + salt = qmd5_hash(salt=data_check[2], data=self.pin)
> + else:
> + salt = None
> + # We're connected at this point, so go ahead and do regular I/O
> + self.readyRead.connect(self.get_data)
> + # Initial data we should know about
> + self.send_command(cmd='CLSS', salt=salt)
> + self.waitForReadyRead()
> + self.projectorReceivedData.connect(self._send_command)
> + if not self.new_wizard and self.state() == self.ConnectedState:
> + self.timer.setInterval(2000) # Set 2 seconds for initial information
> + self.timer.start()
> +
> + @pyqtSlot()
> + def get_data(self):
> + """
> + Socket interface to retrieve data.
> + """
> + log.debug('(%s) get_data(): Reading data' % self.ip)
> + if self.state() != self.ConnectedState:
> + log.debug('(%s) get_data(): Not connected - returning' % self.ip)
> + return
> + read = self.readLine(self.maxSize)
> + if read == -1:
> + # No data available
> + log.debug('(%s) get_data(): No data available (-1)' % self.ip)
> + self.projectorReceivedData.emit()
> + return
> + self.socket_timer.stop()
> + self.projectorNetwork.emit(S_NETWORK_RECEIVED)
> + data_in = decode(read, 'ascii')
> + data = data_in.strip()
> + if len(data) < 7:
> + # Not enough data for a packet
> + log.debug('(%s) get_data(): Packet length < 7: "%s"' % (self.ip, data))
> + self.projectorReceivedData.emit()
> + return
> + log.debug('(%s) get_data(): Checking new data "%s"' % (self.ip, data))
> + if data.upper().startswith('PJLINK'):
> + # Reconnected from remote host disconnect ?
> + self.check_login(data)
> + self.projectorReceivedData.emit()
> + return
> + elif '=' not in data:
> + log.warn('(%s) get_data(): Invalid packet received' % self.ip)
> + self.projectorReceivedData.emit()
> + return
> + data_split = data.split('=')
> + try:
> + (prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1])
> + except ValueError as e:
> + log.warn('(%s) get_data(): Invalid packet - expected header + command + data' % self.ip)
> + log.warn('(%s) get_data(): Received data: "%s"' % (self.ip, read))
> + self.change_status(E_INVALID_DATA)
> + self.projectorReceivedData.emit()
> + return
> +
> + if not self.check_command(cmd):
> + log.warn('(%s) get_data(): Invalid packet - unknown command "%s"' % (self.ip, cmd))
> + self.projectorReceivedData.emit()
> + return
> + return self.process_command(cmd, data)
> +
> + @pyqtSlot(int)
> + def get_error(self, err):
> + """
> + Process error from SocketError signal.
> + Remaps system error codes to projector error codes.
> +
> + :param err: Error code
> + """
> + log.debug('(%s) get_error(err=%s): %s' % (self.ip, err, self.errorString()))
> + if err <= 18:
> + # QSocket errors. Redefined in projector.constants so we don't mistake
> + # them for system errors
> + check = err + E_CONNECTION_REFUSED
> + self.timer.stop()
> + else:
> + check = err
> + if check < E_GENERAL:
> + # Some system error?
> + self.change_status(err, self.errorString())
> + else:
> + self.change_status(E_NETWORK, self.errorString())
> + self.projectorUpdateIcons.emit()
> + if self.status_connect == E_NOT_CONNECTED:
> + self.abort()
> + self.reset_information()
> + return
> +
> + def send_command(self, cmd, opts='?', salt=None, queue=False):
> + """
> + Add command to output queue if not already in queue.
> +
> + :param cmd: Command to send
> + :param opts: Optional command option - defaults to '?' (get information)
> + :param salt: Optional salt for md5 hash for initial authentication
> + :param queue: Option to force add to queue rather than sending directly
> + """
> + if self.state() != self.ConnectedState:
> + log.warn('(%s) send_command(): Not connected - returning' % self.ip)
> + self.send_queue = []
> + return
> + self.projectorNetwork.emit(S_NETWORK_SENDING)
> + log.debug('(%s) send_command(): Building cmd="%s" opts="%s" %s' % (self.ip,
> + cmd,
> + opts,
> + '' if salt is None else 'with hash'))
> + if salt is None:
> + out = '%s%s %s%s' % (PJLINK_HEADER, cmd, opts, CR)
> + else:
> + out = '%s%s%s %s%s' % (salt, PJLINK_HEADER, cmd, opts, CR)
> + if out in self.send_queue:
> + # Already there, so don't add
> + log.debug('(%s) send_command(out="%s") Already in queue - skipping' % (self.ip, out.strip()))
> + elif not queue and len(self.send_queue) == 0:
> + return self._send_command(data=out)
> + else:
> + log.debug('(%s) send_command(out="%s") adding to queue' % (self.ip, out.strip()))
> + self.send_queue.append(out)
> + self.projectorReceivedData.emit()
> + log.debug('(%s) send_command(): send_busy is %s' % (self.ip, self.send_busy))
> + if not self.send_busy:
> + log.debug('(%s) send_command() calling _send_string()')
> + self._send_command()
> +
> + @pyqtSlot()
> + def _send_command(self, data=None):
> + """
> + Socket interface to send data. If data=None, then check queue.
> +
> + :param data: Immediate data to send
> + """
> + log.debug('(%s) _send_string()' % self.ip)
> + log.debug('(%s) _send_string(): Connection status: %s' % (self.ip, self.state()))
> + if self.state() != self.ConnectedState:
> + log.debug('(%s) _send_string() Not connected - abort' % self.ip)
> + self.send_queue = []
> + self.send_busy = False
> + return
> + if self.send_busy:
> + # Still waiting for response from last command sent
> + return
> + if data is not None:
> + out = data
> + log.debug('(%s) _send_string(data=%s)' % (self.ip, out.strip()))
> + elif len(self.send_queue) != 0:
> + out = self.send_queue.pop(0)
> + log.debug('(%s) _send_string(queued data=%s)' % (self.ip, out.strip()))
> + else:
> + # No data to send
> + log.debug('(%s) _send_string(): No data to send' % self.ip)
> + self.send_busy = False
> + return
> + self.send_busy = True
> + log.debug('(%s) _send_string(): Sending "%s"' % (self.ip, out.strip()))
> + log.debug('(%s) _send_string(): Queue = %s' % (self.ip, self.send_queue))
> + self.socket_timer.start()
> + try:
> + self.projectorNetwork.emit(S_NETWORK_SENDING)
> + sent = self.write(out)
> + self.waitForBytesWritten(2000) # 2 seconds should be enough
> + if sent == -1:
> + # Network error?
> + self.change_status(E_NETWORK,
> + translate('OpenLP.PJLink1', 'Error while sending data to projector'))
> + except SocketError as e:
> + self.disconnect_from_host(abort=True)
> + self.changeStatus(E_NETWORK, '%s : %s' % (e.error(), e.errorString()))
> +
> + def process_command(self, cmd, data):
> + """
> + Verifies any return error code. Calls the appropriate command handler.
> +
> + :param cmd: Command to process
> + :param data: Data being processed
> + """
> + log.debug('(%s) Processing command "%s"' % (self.ip, cmd))
> + if data in PJLINK_ERRORS:
> + # Oops - projector error
> + if data.upper() == 'ERRA':
> + # Authentication error
> + self.send_busy = False
> + self.disconnect_from_host()
> + self.change_status(E_AUTHENTICATION)
> + log.debug('(%s) emitting projectorAuthentication() signal' % self.ip)
> + self.projectorAuthentication.emit(self.name)
> + elif data.upper() == 'ERR1':
> + # Undefined command
> + self.send_busy = False
> + self.change_status(E_UNDEFINED, '%s "%s"' %
> + (translate('OpenLP.PJLink1', 'Undefined command:'), cmd))
> + elif data.upper() == 'ERR2':
> + # Invalid parameter
> + self.send_busy = False
> + self.change_status(E_PARAMETER)
> + elif data.upper() == 'ERR3':
> + # Projector busy
> + self.send_busy = False
> + self.change_status(E_UNAVAILABLE)
> + elif data.upper() == 'ERR4':
> + # Projector/display error
> + self.send_busy = False
> + self.change_status(E_PROJECTOR)
> + self.send_busy = False
> + self.projectorReceivedData.emit()
> + return
> + # Command succeeded - no extra information
> + elif data.upper() == 'OK':
> + log.debug('(%s) Command returned OK' % self.ip)
> + # A command returned successfully, recheck data
> + self.send_busy = False
> + self.projectorReceivedData.emit()
> + return
> +
> + if cmd in self.PJLINK1_FUNC:
> + self.PJLINK1_FUNC[cmd](data)
> + else:
> + log.warn('(%s) Invalid command %s' % (self.ip, cmd))
> + self.send_busy = False
> + self.projectorReceivedData.emit()
> +
> + def process_lamp(self, data):
> + """
> + Lamp(s) status. See PJLink Specifications for format.
> + Data may have more than 1 lamp to process.
> + Update self.lamp dictionary with lamp status.
> +
> + :param data: Lamp(s) status.
> + """
> + lamps = []
> + data_dict = data.split()
> + while data_dict:
> + try:
> + fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True}
> + except ValueError:
> + # In case of invalid entry
> + log.warn('(%s) process_lamp(): Invalid data "%s"' % (self.ip, data))
> + return
> + lamps.append(fill)
> + data_dict.pop(0) # Remove lamp hours
> + data_dict.pop(0) # Remove lamp on/off
> + self.lamp = lamps
> + return
> +
> + def process_powr(self, data):
> + """
> + Power status. See PJLink specification for format.
> + Update self.power with status. Update icons if change from previous setting.
> +
> + :param data: Power status
> + """
> + if data in PJLINK_POWR_STATUS:
> + power = PJLINK_POWR_STATUS[data]
> + update_icons = self.power != power
> + self.power = power
> + self.change_status(PJLINK_POWR_STATUS[data])
> + if update_icons:
> + self.projectorUpdateIcons.emit()
> + # Update the input sources available
> + if power == S_ON:
> + self.send_command('INST')
> + else:
> + # Log unknown status response
> + log.warn('Unknown power response: %s' % data)
> + return
> +
> + def process_avmt(self, data):
> + """
> + Process shutter and speaker status. See PJLink specification for format.
> + Update self.mute (audio) and self.shutter (video shutter).
> +
> + :param data: Shutter and audio status
> + """
> + shutter = self.shutter
> + mute = self.mute
> + if data == '11':
> + shutter = True
> + mute = False
> + elif data == '21':
> + shutter = False
> + mute = True
> + elif data == '30':
> + shutter = False
> + mute = False
> + elif data == '31':
> + shutter = True
> + mute = True
> + else:
> + log.warn('Unknown shutter response: %s' % data)
> + update_icons = shutter != self.shutter
> + update_icons = update_icons or mute != self.mute
> + self.shutter = shutter
> + self.mute = mute
> + if update_icons:
> + self.projectorUpdateIcons.emit()
> + return
> +
> + def process_inpt(self, data):
> + """
> + Current source input selected. See PJLink specification for format.
> + Update self.source
> +
> + :param data: Currently selected source
> + """
> + self.source = data
> + return
> +
> + def process_clss(self, data):
> + """
> + PJLink class that this projector supports. See PJLink specification for format.
> + Updates self.class.
> +
> + :param data: Class that projector supports.
> + """
> + self.pjlink_class = data
> + log.debug('(%s) Setting pjlink_class for this projector to "%s"' % (self.ip, self.pjlink_class))
> + return
> +
> + def process_name(self, data):
> + """
> + Projector name set in projector.
> + Updates self.pjlink_name
> +
> + :param data: Projector name
> + """
> + self.pjlink_name = data
> + return
> +
> + def process_inf1(self, data):
> + """
> + Manufacturer name set in projector.
> + Updates self.manufacturer
> +
> + :param data: Projector manufacturer
> + """
> + self.manufacturer = data
> + return
> +
> + def process_inf2(self, data):
> + """
> + Projector Model set in projector.
> + Updates self.model.
> +
> + :param data: Model name
> + """
> + self.model = data
> + return
> +
> + def process_info(self, data):
> + """
> + Any extra info set in projector.
> + Updates self.other_info.
> +
> + :param data: Projector other info
> + """
> + self.other_info = data
> + return
> +
> + def process_inst(self, data):
> + """
> + Available source inputs. See PJLink specification for format.
> + Updates self.source_available
> +
> + :param data: Sources list
> + """
> + sources = []
> + check = data.split()
> + for source in check:
> + sources.append(source)
> + sources.sort()
> + self.source_available = sources
> + self.projectorUpdateIcons.emit()
> + return
> +
> + def process_erst(self, data):
> + """
> + Error status. See PJLink Specifications for format.
> + Updates self.projector_errors
> +
> + :param data: Error status
> + """
> + try:
> + datacheck = int(data)
> + except ValueError:
> + # Bad data - ignore
> + return
> + if datacheck == 0:
> + self.projector_errors = None
> + else:
> + self.projector_errors = {}
> + # Fan
> + if data[0] != '0':
> + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \
> + PJLINK_ERST_STATUS[data[0]]
> + # Lamp
> + if data[1] != '0':
> + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] = \
> + PJLINK_ERST_STATUS[data[1]]
> + # Temp
> + if data[2] != '0':
> + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] = \
> + PJLINK_ERST_STATUS[data[2]]
> + # Cover
> + if data[3] != '0':
> + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] = \
> + PJLINK_ERST_STATUS[data[3]]
> + # Filter
> + if data[4] != '0':
> + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] = \
> + PJLINK_ERST_STATUS[data[4]]
> + # Other
> + if data[5] != '0':
> + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] = \
> + PJLINK_ERST_STATUS[data[5]]
> + return
> +
> + def connect_to_host(self):
> + """
> + Initiate connection to projector.
> + """
> + if self.state() == self.ConnectedState:
> + log.warn('(%s) connect_to_host(): Already connected - returning' % self.ip)
> + return
> + self.change_status(S_CONNECTING)
> + self.connectToHost(self.ip, self.port if type(self.port) is int else int(self.port))
> +
> + @pyqtSlot()
> + def disconnect_from_host(self, abort=False):
> + """
> + Close socket and cleanup.
> + """
> + if abort or self.state() != self.ConnectedState:
> + if abort:
> + log.warn('(%s) disconnect_from_host(): Aborting connection' % self.ip)
> + else:
> + log.warn('(%s) disconnect_from_host(): Not connected - returning' % self.ip)
> + self.reset_information()
> + self.disconnectFromHost()
> + try:
> + self.readyRead.disconnect(self.get_data)
> + except TypeError:
> + pass
> + if abort:
> + self.change_status(E_NOT_CONNECTED)
> + else:
> + log.debug('(%s) disconnect_from_host() Current status %s' % (self.ip,
> + self._get_status(self.status_connect)[0]))
> + if self.status_connect != E_NOT_CONNECTED:
> + self.change_status(S_NOT_CONNECTED)
> + self.reset_information()
> + self.projectorUpdateIcons.emit()
> +
> + def get_available_inputs(self):
> + """
> + Send command to retrieve available source inputs.
> + """
> + return self.send_command(cmd='INST')
> +
> + def get_error_status(self):
> + """
> + Send command to retrieve currently known errors.
> + """
> + return self.send_command(cmd='ERST')
> +
> + def get_input_source(self):
> + """
> + Send command to retrieve currently selected source input.
> + """
> + return self.send_command(cmd='INPT')
> +
> + def get_lamp_status(self):
> + """
> + Send command to return the lap status.
> + """
> + return self.send_command(cmd='LAMP')
> +
> + def get_manufacturer(self):
> + """
> + Send command to retrieve manufacturer name.
> + """
> + return self.send_command(cmd='INF1')
> +
> + def get_model(self):
> + """
> + Send command to retrieve the model name.
> + """
> + return self.send_command(cmd='INF2')
> +
> + def get_name(self):
> + """
> + Send command to retrieve name as set by end-user (if set).
> + """
> + return self.send_command(cmd='NAME')
> +
> + def get_other_info(self):
> + """
> + Send command to retrieve extra info set by manufacturer.
> + """
> + return self.send_command(cmd='INFO')
> +
> + def get_power_status(self):
> + """
> + Send command to retrieve power status.
> + """
> + return self.send_command(cmd='POWR')
> +
> + def get_shutter_status(self):
> + """
> + Send command to retrieve shutter status.
> + """
> + return self.send_command(cmd='AVMT')
> +
> + def set_input_source(self, src=None):
> + """
> + Verify input source available as listed in 'INST' command,
> + then send the command to select the input source.
> +
> + :param src: Video source to select in projector
> + """
> + log.debug('(%s) set_input_source(src=%s)' % (self.ip, src))
> + if self.source_available is None:
> + return
> + elif src not in self.source_available:
> + return
> + log.debug('(%s) Setting input source to %s' % (self.ip, src))
> + self.send_command(cmd='INPT', opts=src)
> + self.poll_loop()
> +
> + def set_power_on(self):
> + """
> + Send command to turn power to on.
> + """
> + self.send_command(cmd='POWR', opts='1')
> + self.poll_loop()
> +
> + def set_power_off(self):
> + """
> + Send command to turn power to standby.
> + """
> + self.send_command(cmd='POWR', opts='0')
> + self.poll_loop()
> +
> + def set_shutter_closed(self):
> + """
> + Send command to set shutter to closed position.
> + """
> + self.send_command(cmd='AVMT', opts='11')
> + self.poll_loop()
> +
> + def set_shutter_open(self):
> + """
> + Send command to set shutter to open position.
> + """
> + self.send_command(cmd='AVMT', opts='10')
> + self.poll_loop()
>
> === modified file 'openlp/core/lib/toolbar.py'
> --- openlp/core/lib/toolbar.py 2014-06-30 20:59:22 +0000
> +++ openlp/core/lib/toolbar.py 2014-10-21 20:17:27 +0000
> @@ -82,3 +82,16 @@
> self.actions[handle].setVisible(visible)
> else:
> log.warning('No handle "%s" in actions list.', str(handle))
> +
> + def set_widget_enabled(self, widgets, enabled=True):
> + """
> + Set the enabled state for a widget or a list of widgets.
> +
> + :param widgets: A list of string with widget object names.
> + :param enabled: The new state as bool.
> + """
> + for handle in widgets:
> + if handle in self.actions:
> + self.actions[handle].setEnabled(enabled)
> + else:
> + log.warning('No handle "%s" in actions list.', str(handle))
>
> === modified file 'openlp/core/ui/__init__.py'
> --- openlp/core/ui/__init__.py 2014-02-27 21:39:44 +0000
> +++ openlp/core/ui/__init__.py 2014-10-21 20:17:27 +0000
> @@ -124,9 +124,13 @@
> from .mediadockmanager import MediaDockManager
> from .servicemanager import ServiceManager
> from .thememanager import ThemeManager
> +from .projector.manager import ProjectorManager
> +from .projector.tab import ProjectorTab
> +from .projector.editform import ProjectorEditForm
>
> __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeForm',
> 'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm',
> 'Display', 'ServiceNoteForm', 'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay',
> 'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
> - 'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'SingleColumnTableWidget']
> + 'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'SingleColumnTableWidget',
> + 'ProjectorManager', 'ProjectorTab', 'ProjectorEditForm']
>
> === modified file 'openlp/core/ui/mainwindow.py'
> --- openlp/core/ui/mainwindow.py 2014-09-04 21:38:39 +0000
> +++ openlp/core/ui/mainwindow.py 2014-10-21 20:17:27 +0000
> @@ -53,6 +53,7 @@
> from openlp.core.utils import LanguageManager, add_actions, get_application_version
> from openlp.core.utils.actions import ActionList, CategoryOrder
> from openlp.core.ui.firsttimeform import FirstTimeForm
> +from openlp.core.ui.projector.manager import ProjectorManager
>
> log = logging.getLogger(__name__)
>
> @@ -178,6 +179,14 @@
> self.theme_manager_contents.setObjectName('theme_manager_contents')
> self.theme_manager_dock.setWidget(self.theme_manager_contents)
> main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.theme_manager_dock)
> + # Create the projector manager
> + self.projector_manager_dock = OpenLPDockWidget(parent=main_window,
> + name='projector_manager_dock',
> + icon=':/projector/projector_manager.png')
> + self.projector_manager_contents = ProjectorManager(self.projector_manager_dock)
> + self.projector_manager_contents.setObjectName('projector_manager_contents')
> + self.projector_manager_dock.setWidget(self.projector_manager_contents)
> + main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.projector_manager_dock)
> # Create the menu items
> action_list = ActionList.get_instance()
> action_list.add_category(UiStrings().File, CategoryOrder.standard_menu)
> @@ -210,6 +219,16 @@
> can_shortcuts=True)
> self.export_language_item = create_action(main_window, 'exportLanguageItem')
> action_list.add_category(UiStrings().View, CategoryOrder.standard_menu)
> + # Projector items
> + self.import_projector_item = create_action(main_window, 'importProjectorItem', category=UiStrings().Import,
> + can_shortcuts=False)
> + action_list.add_category(UiStrings().Import, CategoryOrder.standard_menu)
> + self.view_projector_manager_item = create_action(main_window, 'viewProjectorManagerItem',
> + icon=':/projector/projector_manager.png',
> + checked=self.projector_manager_dock.isVisible(),
> + can_shortcuts=True,
> + category=UiStrings().View,
> + triggers=self.toggle_projector_manager)
> self.view_media_manager_item = create_action(main_window, 'viewMediaManagerItem',
> icon=':/system/system_mediamanager.png',
> checked=self.media_manager_dock.isVisible(),
> @@ -310,6 +329,11 @@
> 'searchShortcut', can_shortcuts=True,
> category=translate('OpenLP.MainWindow', 'General'),
> triggers=self.on_search_shortcut_triggered)
> + '''
> + Leave until the import projector options are finished
> + add_actions(self.file_import_menu, (self.settings_import_item, self.import_theme_item,
> + self.import_projector_item, self.import_language_item, None))
> + '''
> add_actions(self.file_import_menu, (self.settings_import_item, self.import_theme_item,
> self.import_language_item, None))
> add_actions(self.file_export_menu, (self.settings_export_item, self.export_theme_item,
> @@ -320,8 +344,8 @@
> self.print_service_order_item, self.file_exit_item))
> add_actions(self.view_mode_menu, (self.mode_default_item, self.mode_setup_item, self.mode_live_item))
> add_actions(self.view_menu, (self.view_mode_menu.menuAction(), None, self.view_media_manager_item,
> - self.view_service_manager_item, self.view_theme_manager_item, None, self.view_preview_panel,
> - self.view_live_panel, None, self.lock_panel))
> + self.view_projector_manager_item, self.view_service_manager_item, self.view_theme_manager_item,
> + None, self.view_preview_panel, self.view_live_panel, None, self.lock_panel))
> # i18n add Language Actions
> add_actions(self.settings_language_menu, (self.auto_language_item, None))
> add_actions(self.settings_language_menu, self.language_group.actions())
> @@ -375,6 +399,7 @@
> self.media_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Library'))
> self.service_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Service Manager'))
> self.theme_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Theme Manager'))
> + self.projector_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Projector Manager'))
> self.file_new_item.setText(translate('OpenLP.MainWindow', '&New'))
> self.file_new_item.setToolTip(UiStrings().NewService)
> self.file_new_item.setStatusTip(UiStrings().CreateService)
> @@ -406,6 +431,10 @@
> translate('OpenLP.MainWindow', 'Import OpenLP settings from a specified *.config file previously '
> 'exported on this or another machine'))
> self.settings_import_item.setText(translate('OpenLP.MainWindow', 'Settings'))
> + self.view_projector_manager_item.setText(translate('OPenLP.MainWindow', '&ProjectorManager'))
> + self.view_projector_manager_item.setToolTip(translate('OpenLP.MainWindow', 'Toggle Projector Manager'))
> + self.view_projector_manager_item.setStatusTip(translate('OpenLP.MainWindow',
> + 'Toggle the visibility of the Projector Manager'))
> self.view_media_manager_item.setText(translate('OpenLP.MainWindow', '&Media Manager'))
> self.view_media_manager_item.setToolTip(translate('OpenLP.MainWindow', 'Toggle Media Manager'))
> self.view_media_manager_item.setStatusTip(translate('OpenLP.MainWindow',
> @@ -485,6 +514,7 @@
> self.service_manager_settings_section = 'servicemanager'
> self.songs_settings_section = 'songs'
> self.themes_settings_section = 'themes'
> + self.projector_settings_section = 'projector'
> self.players_settings_section = 'players'
> self.display_tags_section = 'displayTags'
> self.header_section = 'SettingsImport'
> @@ -515,6 +545,7 @@
> self.media_manager_dock.visibilityChanged.connect(self.view_media_manager_item.setChecked)
> self.service_manager_dock.visibilityChanged.connect(self.view_service_manager_item.setChecked)
> self.theme_manager_dock.visibilityChanged.connect(self.view_theme_manager_item.setChecked)
> + self.projector_manager_dock.visibilityChanged.connect(self.view_projector_manager_item.setChecked)
> self.import_theme_item.triggered.connect(self.theme_manager_contents.on_import_theme)
> self.export_theme_item.triggered.connect(self.theme_manager_contents.on_export_theme)
> self.web_site_item.triggered.connect(self.on_help_web_site_clicked)
> @@ -825,6 +856,7 @@
> setting_sections.extend([self.shortcuts_settings_section])
> setting_sections.extend([self.service_manager_settings_section])
> setting_sections.extend([self.themes_settings_section])
> + setting_sections.extend([self.projector_settings_section])
> setting_sections.extend([self.players_settings_section])
> setting_sections.extend([self.display_tags_section])
> setting_sections.extend([self.header_section])
> @@ -1114,6 +1146,12 @@
> """
> self.media_manager_dock.setVisible(not self.media_manager_dock.isVisible())
>
> + def toggle_projector_manager(self):
> + """
> + Toggle visibility of the projector manager
> + """
> + self.projector_manager_dock.setVisible(not self.projector_manager_dock.isVisible())
> +
> def toggle_service_manager(self):
> """
> Toggle the visibility of the service manager
>
> === added directory 'openlp/core/ui/projector'
> === added file 'openlp/core/ui/projector/editform.py'
> --- openlp/core/ui/projector/editform.py 1970-01-01 00:00:00 +0000
> +++ openlp/core/ui/projector/editform.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,268 @@
> +
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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.ui.projector.editform` module
> +
> + Provides the functions for adding/editing entries in the projector database.
> +"""
> +
> +import logging
> +log = logging.getLogger(__name__)
> +log.debug('editform loaded')
> +
> +from PyQt4 import QtCore, QtGui
> +from PyQt4.QtCore import pyqtSlot, pyqtSignal
> +from PyQt4.QtGui import QDialog, QPlainTextEdit, QLineEdit, QDialogButtonBox, QLabel, QGridLayout
> +
> +from openlp.core.common import translate, verify_ip_address
> +from openlp.core.lib import build_icon
> +from openlp.core.lib.projector.db import Projector
> +from openlp.core.lib.projector.constants import PJLINK_PORT
> +
> +
> +class Ui_ProjectorEditForm(object):
> + """
> + The :class:`~opelp.core.lib.ui.projector.editform.Ui_ProjectorEdiForm` class defines
Typo in openlp
> + the user interface for the ProjectorEditForm dialog.
> + """
> + def setupUi(self, edit_projector_dialog):
> + """
> + Create the interface layout.
> + """
> + edit_projector_dialog.setObjectName('edit_projector_dialog')
> + edit_projector_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo-32x32.png'))
> + edit_projector_dialog.setMinimumWidth(400)
> + edit_projector_dialog.setModal(True)
> + # Define the basic layout
> + self.dialog_layout = QGridLayout(edit_projector_dialog)
> + self.dialog_layout.setObjectName('dialog_layout')
> + self.dialog_layout.setSpacing(8)
> + self.dialog_layout.setContentsMargins(8, 8, 8, 8)
> + # IP Address
> + self.ip_label = QLabel(edit_projector_dialog)
> + self.ip_label.setObjectName('projector_edit_ip_label')
> + self.ip_text = QLineEdit(edit_projector_dialog)
> + self.ip_text.setObjectName('projector_edit_ip_text')
> + self.dialog_layout.addWidget(self.ip_label, 0, 0)
> + self.dialog_layout.addWidget(self.ip_text, 0, 1)
> + # Port number
> + self.port_label = QLabel(edit_projector_dialog)
> + self.port_label.setObjectName('projector_edit_ip_label')
> + self.port_text = QLineEdit(edit_projector_dialog)
> + self.port_text.setObjectName('projector_edit_port_text')
> + self.dialog_layout.addWidget(self.port_label, 1, 0)
> + self.dialog_layout.addWidget(self.port_text, 1, 1)
> + # PIN
> + self.pin_label = QLabel(edit_projector_dialog)
> + self.pin_label.setObjectName('projector_edit_pin_label')
> + self.pin_text = QLineEdit(edit_projector_dialog)
> + self.pin_label.setObjectName('projector_edit_pin_text')
> + self.dialog_layout.addWidget(self.pin_label, 2, 0)
> + self.dialog_layout.addWidget(self.pin_text, 2, 1)
> + # Name
> + self.name_label = QLabel(edit_projector_dialog)
> + self.name_label.setObjectName('projector_edit_name_label')
> + self.name_text = QLineEdit(edit_projector_dialog)
> + self.name_text.setObjectName('projector_edit_name_text')
> + self.dialog_layout.addWidget(self.name_label, 3, 0)
> + self.dialog_layout.addWidget(self.name_text, 3, 1)
> + # Location
> + self.location_label = QLabel(edit_projector_dialog)
> + self.location_label.setObjectName('projector_edit_location_label')
> + self.location_text = QLineEdit(edit_projector_dialog)
> + self.location_text.setObjectName('projector_edit_location_text')
> + self.dialog_layout.addWidget(self.location_label, 4, 0)
> + self.dialog_layout.addWidget(self.location_text, 4, 1)
> + # Notes
> + self.notes_label = QLabel(edit_projector_dialog)
> + self.notes_label.setObjectName('projector_edit_notes_label')
> + self.notes_text = QPlainTextEdit(edit_projector_dialog)
> + self.notes_text.setObjectName('projector_edit_notes_text')
> + self.dialog_layout.addWidget(self.notes_label, 5, 0, alignment=QtCore.Qt.AlignTop)
> + self.dialog_layout.addWidget(self.notes_text, 5, 1)
> + # Time for the buttons
> + self.button_box = QDialogButtonBox(QDialogButtonBox.Help |
> + QDialogButtonBox.Save |
> + QDialogButtonBox.Cancel)
> + self.dialog_layout.addWidget(self.button_box, 8, 0, 1, 2)
> +
> + def retranslateUi(self, edit_projector_dialog):
> + if self.new_projector:
> + title = translate('OpenLP.ProjectorEditForm', 'Add New Projector')
> + self.projector.port = PJLINK_PORT
> + else:
> + title = translate('OpenLP.ProjectorEditForm', 'Edit Projector')
> + edit_projector_dialog.setWindowTitle(title)
> + self.ip_label.setText(translate('OpenLP.ProjetorEditForm', 'IP Address'))
Typo in Projector
> + self.ip_text.setText(self.projector.ip)
> + self.port_label.setText(translate('OpenLP.ProjectorEditForm', 'Port Number'))
> + self.port_text.setText(str(self.projector.port))
> + self.pin_label.setText(translate('OpenLP.ProjectorEditForm', 'PIN'))
> + self.pin_text.setText(self.projector.pin)
> + self.name_label.setText(translate('OpenLP.ProjectorEditForm', 'Name'))
> + self.name_text.setText(self.projector.name)
> + self.location_label.setText(translate('OpenLP.ProjectorEditForm', 'Location'))
> + self.location_text.setText(self.projector.location)
> + self.notes_label.setText(translate('OpenLP.ProjectorEditForm', 'Notes'))
> + self.notes_text.insertPlainText(self.projector.notes)
> +
> +
> +class ProjectorEditForm(QDialog, Ui_ProjectorEditForm):
> + """
> + Class to add or edit a projector entry in the database.
> +
> + Fields that are editable:
> + ip = Column(String(100))
> + port = Column(String(8))
> + pin = Column(String(20))
> + name = Column(String(20))
> + location = Column(String(30))
> + notes = Column(String(200))
> + """
> + newProjector = pyqtSignal(str)
> + editProjector = pyqtSignal(object)
> +
> + def __init__(self, parent=None, projectordb=None):
> + super(ProjectorEditForm, self).__init__(parent=parent)
> + self.projectordb = projectordb
> + self.setupUi(self)
> + self.button_box.accepted.connect(self.accept_me)
> + self.button_box.helpRequested.connect(self.help_me)
> + self.button_box.rejected.connect(self.cancel_me)
> +
> + def exec_(self, projector=None):
> + if projector is None:
> + self.projector = Projector()
> + self.new_projector = True
> + else:
> + self.projector = projector
> + self.new_projector = False
> + self.retranslateUi(self)
> + reply = QDialog.exec_(self)
> + self.projector = None
> + return reply
> +
> + @pyqtSlot()
> + def accept_me(self):
> + """
> + Validate input before accepting input.
> + """
> + log.debug('accept_me() signal received')
> + if len(self.name_text.text().strip()) < 1:
> + QtGui.QMessageBox.warning(self,
> + translate('OpenLP.ProjectorEdit', 'Name Not Set'),
> + translate('OpenLP.ProjectorEdit',
> + 'You must enter a name for this entry.<br />'
> + 'Please enter a new name for this entry.'))
> + valid = False
> + return
> + name = self.name_text.text().strip()
> + record = self.projectordb.get_projector_by_name(name)
> + if record is not None and record.id != self.projector.id:
> + QtGui.QMessageBox.warning(self,
> + translate('OpenLP.ProjectorEdit', 'Duplicate Name'),
> + translate('OpenLP.ProjectorEdit',
> + 'There is already an entry with name "%s" in '
> + 'the database as ID "%s". <br />'
> + 'Please enter a different name.' % (name, record.id)))
> + valid = False
> + return
> + adx = self.ip_text.text()
> + valid = verify_ip_address(adx)
> + if valid:
> + ip = self.projectordb.get_projector_by_ip(adx)
> + if ip is None:
> + valid = True
> + self.new_projector = True
> + elif ip.id != self.projector.id:
> + QtGui.QMessageBox.warning(self,
> + translate('OpenLP.ProjectorWizard', 'Duplicate IP Address'),
> + translate('OpenLP.ProjectorWizard',
> + 'IP address "%s"<br />is already in the database as ID %s.'
> + '<br /><br />Please Enter a different IP address.' % (adx, ip.id)))
> + valid = False
> + return
> + else:
> + QtGui.QMessageBox.warning(self,
> + translate('OpenLP.ProjectorWizard', 'Invalid IP Address'),
> + translate('OpenLP.ProjectorWizard',
> + 'IP address "%s"<br>is not a valid IP address.'
> + '<br /><br />Please enter a valid IP address.' % adx))
> + valid = False
> + return
> + port = int(self.port_text.text())
> + if port < 1000 or port > 32767:
> + QtGui.QMessageBox.warning(self,
> + translate('OpenLP.ProjectorWizard', 'Invalid Port Number'),
> + translate('OpenLP.ProjectorWizard',
> + 'Port numbers below 1000 are reserved for admin use only, '
> + '<br />and port numbers above 32767 are not currently usable.'
> + '<br /><br />Please enter a valid port number between '
> + ' 1000 and 32767.'
> + '<br /><br />Default PJLink port is %s' % PJLINK_PORT))
> + valid = False
> + if valid:
> + self.projector.ip = self.ip_text.text()
> + self.projector.pin = self.pin_text.text()
> + self.projector.port = int(self.port_text.text())
> + self.projector.name = self.name_text.text()
> + self.projector.location = self.location_text.text()
> + self.projector.notes = self.notes_text.toPlainText()
> + if self.new_projector:
> + saved = self.projectordb.add_projector(self.projector)
> + else:
> + saved = self.projectordb.update_projector(self.projector)
> + if not saved:
> + QtGui.QMessageBox.warning(self,
> + translate('OpenLP.ProjectorEditForm', 'Database Error'),
> + translate('OpenLP.ProjectorEditForm',
> + 'There was an error saving projector '
> + 'information. See the log for the error'))
> + return saved
> + if self.new_projector:
> + self.newProjector.emit(adx)
> + else:
> + self.editProjector.emit(self.projector)
> + self.close()
> +
> + @pyqtSlot()
> + def help_me(self):
> + """
> + Show a help message about the input fields.
> + """
> + log.debug('help_me() signal received')
> +
> + @pyqtSlot()
> + def cancel_me(self):
> + """
> + Cancel button clicked - just close.
> + """
> + log.debug('cancel_me() signal received')
> + self.close()
>
> === added file 'openlp/core/ui/projector/manager.py'
> --- openlp/core/ui/projector/manager.py 1970-01-01 00:00:00 +0000
> +++ openlp/core/ui/projector/manager.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,978 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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.ui.projector.manager` module
> +
> + Provides the functions for the display/control of Projectors.
> +"""
> +
> +import logging
> +log = logging.getLogger(__name__)
> +log.debug('projectormanager loaded')
> +
> +from PyQt4 import QtCore, QtGui
> +from PyQt4.QtCore import QObject, QThread, pyqtSlot
> +from PyQt4.QtGui import QWidget
> +
> +from openlp.core.common import RegistryProperties, Settings, OpenLPMixin, \
> + RegistryMixin, translate
> +from openlp.core.lib import OpenLPToolbar
> +from openlp.core.lib.ui import create_widget_action
> +from openlp.core.lib.projector import DialogSourceStyle
> +from openlp.core.lib.projector.constants import *
> +from openlp.core.lib.projector.db import ProjectorDB
> +from openlp.core.lib.projector.pjlink1 import PJLink1
> +from openlp.core.ui.projector.editform import ProjectorEditForm
> +from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle
> +
> +# Dict for matching projector status to display icon
> +STATUS_ICONS = {S_NOT_CONNECTED: ':/projector/projector_item_disconnect.png',
> + S_CONNECTING: ':/projector/projector_item_connect.png',
> + S_CONNECTED: ':/projector/projector_off.png',
> + S_OFF: ':/projector/projector_off.png',
> + S_INITIALIZE: ':/projector/projector_off.png',
> + S_STANDBY: ':/projector/projector_off.png',
> + S_WARMUP: ':/projector/projector_warmup.png',
> + S_ON: ':/projector/projector_on.png',
> + S_COOLDOWN: ':/projector/projector_cooldown.png',
> + E_ERROR: ':/projector/projector_error.png',
> + E_NETWORK: ':/projector/projector_not_connected_error.png',
> + E_AUTHENTICATION: ':/projector/projector_not_connected_error.png',
> + E_UNKNOWN_SOCKET_ERROR: ':/projector/projector_not_connected_error.png',
> + E_NOT_CONNECTED: ':/projector/projector_not_connected_error.png'
> + }
> +
> +
> +class Ui_ProjectorManager(object):
> + """
> + UI part of the Projector Manager
> + """
> + def setup_ui(self, widget):
> + """
> + Define the UI
> +
> + :param widget: The screen object the dialog is to be attached to.
> + """
> + log.debug('setup_ui()')
> + # Create ProjectorManager box
> + self.layout = QtGui.QVBoxLayout(widget)
> + self.layout.setSpacing(0)
> + self.layout.setMargin(0)
> + self.layout.setObjectName('layout')
> + # Add one selection toolbar
> + self.one_toolbar = OpenLPToolbar(widget)
> + self.one_toolbar.add_toolbar_action('new_projector',
> + text=translate('OpenLP.ProjectorManager', 'Add Projector'),
> + icon=':/projector/projector_new.png',
> + tooltip=translate('OpenLP.ProjectorManager', 'Add a new projector'),
> + triggers=self.on_add_projector)
> + # Show edit/delete when projector not connected
> + self.one_toolbar.add_toolbar_action('edit_projector',
> + text=translate('OpenLP.ProjectorManager', 'Edit Projector'),
> + icon=':/general/general_edit.png',
> + tooltip=translate('OpenLP.ProjectorManager', 'Edit selected projector'),
> + triggers=self.on_edit_projector)
> + self.one_toolbar.add_toolbar_action('delete_projector',
> + text=translate('OpenLP.ProjectorManager', 'Delete Projector'),
> + icon=':/general/general_delete.png',
> + tooltip=translate('OpenLP.ProjectorManager', 'Delete selected projector'),
> + triggers=self.on_delete_projector)
> + # Show source/view when projector connected
> + self.one_toolbar.add_toolbar_action('source_projector',
> + text=translate('OpenLP.ProjectorManager', 'Select Input Source'),
> + icon=':/projector/projector_hdmi.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Choose input source on selected projector'),
> + triggers=self.on_select_input)
> + self.one_toolbar.add_toolbar_action('view_projector',
> + text=translate('OpenLP.ProjectorManager', 'View Projector'),
> + icon=':/system/system_about.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'View selected projector information'),
> + triggers=self.on_status_projector)
> + self.one_toolbar.addSeparator()
> + self.one_toolbar.add_toolbar_action('connect_projector',
> + text=translate('OpenLP.ProjectorManager',
> + 'Connect to selected projector'),
> + icon=':/projector/projector_connect.png',
> + tootip=translate('OpenLP.ProjectorManager',
> + 'Connect to selected projector'),
> + triggers=self.on_connect_projector)
> + self.one_toolbar.add_toolbar_action('connect_projector_multiple',
> + text=translate('OpenLP.ProjectorManager',
> + 'Connect to selected projectors'),
> + icon=':/projector/projector_connect_tiled.png',
> + tootip=translate('OpenLP.ProjectorManager',
> + 'Connect to selected projector'),
> + triggers=self.on_connect_projector)
> + self.one_toolbar.add_toolbar_action('disconnect_projector',
> + text=translate('OpenLP.ProjectorManager',
> + 'Disconnect from selected projectors'),
> + icon=':/projector/projector_disconnect.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Disconnect from selected projector'),
> + triggers=self.on_disconnect_projector)
> + self.one_toolbar.add_toolbar_action('disconnect_projector_multiple',
> + text=translate('OpenLP.ProjectorManager',
> + 'Disconnect from selected projector'),
> + icon=':/projector/projector_disconnect_tiled.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Disconnect from selected projector'),
> + triggers=self.on_disconnect_projector)
> + self.one_toolbar.addSeparator()
> + self.one_toolbar.add_toolbar_action('poweron_projector',
> + text=translate('OpenLP.ProjectorManager',
> + 'Power on selected projector'),
> + icon=':/projector/projector_power_on.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Power on selected projector'),
> + triggers=self.on_poweron_projector)
> + self.one_toolbar.add_toolbar_action('poweron_projector_multiple',
> + text=translate('OpenLP.ProjectorManager',
> + 'Power on selected projector'),
> + icon=':/projector/projector_power_on_tiled.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Power on selected projector'),
> + triggers=self.on_poweron_projector)
> + self.one_toolbar.add_toolbar_action('poweroff_projector',
> + text=translate('OpenLP.ProjectorManager', 'Standby selected projector'),
> + icon=':/projector/projector_power_off.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Put selected projector in standby'),
> + triggers=self.on_poweroff_projector)
> + self.one_toolbar.add_toolbar_action('poweroff_projector_multiple',
> + text=translate('OpenLP.ProjectorManager', 'Standby selected projector'),
> + icon=':/projector/projector_power_off_tiled.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Put selected projector in standby'),
> + triggers=self.on_poweroff_projector)
> + self.one_toolbar.addSeparator()
> + self.one_toolbar.add_toolbar_action('blank_projector',
> + text=translate('OpenLP.ProjectorManager',
> + 'Blank selected projector screen'),
> + icon=':/projector/projector_blank.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Blank selected projector screen'),
> + triggers=self.on_blank_projector)
> + self.one_toolbar.add_toolbar_action('blank_projector_multiple',
> + text=translate('OpenLP.ProjectorManager',
> + 'Blank selected projector screen'),
> + icon=':/projector/projector_blank_tiled.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Blank selected projector screen'),
> + triggers=self.on_blank_projector)
> + self.one_toolbar.add_toolbar_action('show_projector',
> + ext=translate('OpenLP.ProjectorManager',
> + 'Show selected projector screen'),
> + icon=':/projector/projector_show.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Show selected projector screen'),
> + triggers=self.on_show_projector)
> + self.one_toolbar.add_toolbar_action('show_projector_multiple',
> + ext=translate('OpenLP.ProjectorManager',
> + 'Show selected projector screen'),
> + icon=':/projector/projector_show_tiled.png',
> + tooltip=translate('OpenLP.ProjectorManager',
> + 'Show selected projector screen'),
> + triggers=self.on_show_projector)
> + self.layout.addWidget(self.one_toolbar)
> + self.projector_one_widget = QtGui.QWidgetAction(self.one_toolbar)
> + self.projector_one_widget.setObjectName('projector_one_toolbar_widget')
> + # Create projector manager list
> + self.projector_list_widget = QtGui.QListWidget(widget)
> + self.projector_list_widget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
> + self.projector_list_widget.setAlternatingRowColors(True)
> + self.projector_list_widget.setIconSize(QtCore.QSize(90, 50))
> + self.projector_list_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
> + self.projector_list_widget.setObjectName('projector_list_widget')
> + self.layout.addWidget(self.projector_list_widget)
> + self.projector_list_widget.customContextMenuRequested.connect(self.context_menu)
> + self.projector_list_widget.itemDoubleClicked.connect(self.on_doubleclick_item)
> + # Build the context menu
> + self.menu = QtGui.QMenu()
> + self.status_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + '&View Projector Information'),
> + icon=':/system/system_about.png',
> + triggers=self.on_status_projector)
> + self.edit_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + '&Edit Projector'),
> + icon=':/projector/projector_edit.png',
> + triggers=self.on_edit_projector)
> + self.menu.addSeparator()
> + self.connect_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + '&Connect Projector'),
> + icon=':/projector/projector_connect.png',
> + triggers=self.on_connect_projector)
> + self.disconnect_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + 'D&isconnect Projector'),
> + icon=':/projector/projector_disconnect.png',
> + triggers=self.on_disconnect_projector)
> + self.menu.addSeparator()
> + self.poweron_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + 'Power &On Projector'),
> + icon=':/projector/projector_power_on.png',
> + triggers=self.on_poweron_projector)
> + self.poweroff_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + 'Power O&ff Projector'),
> + icon=':/projector/projector_power_off.png',
> + triggers=self.on_poweroff_projector)
> + self.menu.addSeparator()
> + self.select_input_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + 'Select &Input'),
> + icon=':/projector/projector_hdmi.png',
> + triggers=self.on_select_input)
> + self.blank_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + '&Blank Projector Screen'),
> + icon=':/projector/projector_blank.png',
> + triggers=self.on_blank_projector)
> + self.show_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + '&Show Projector Screen'),
> + icon=':/projector/projector_show.png',
> + triggers=self.on_show_projector)
> + self.menu.addSeparator()
> + self.delete_action = create_widget_action(self.menu,
> + text=translate('OpenLP.ProjectorManager',
> + '&Delete Projector'),
> + icon=':/general/general_delete.png',
> + triggers=self.on_delete_projector)
> + self.update_icons()
> +
> +
> +class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager, RegistryProperties):
> + """
> + Manage the projectors.
> + """
> + def __init__(self, parent=None, projectordb=None):
> + """
> + Basic initialization.
> +
> + :param parent: Who I belong to.
> + :param projectordb: Database session inherited from superclass.
> + """
> + log.debug('__init__()')
> + super().__init__(parent)
> + self.settings_section = 'projector'
> + self.projectordb = projectordb
> + self.projector_list = []
> + self.source_select_form = None
> +
> + def bootstrap_initialise(self):
> + """
> + Pre-initialize setups.
> + """
> + self.setup_ui(self)
> + if self.projectordb is None:
> + # Work around for testing creating a ~/.openlp.data.projector.projector.sql file
> + log.debug('Creating new ProjectorDB() instance')
> + self.projectordb = ProjectorDB()
> + else:
> + log.debug('Using existing ProjectorDB() instance')
> + self.get_settings()
> +
> + def bootstrap_post_set_up(self):
> + """
> + Post-initialize setups.
> + """
> + self._load_projectors()
> + self.projector_form = ProjectorEditForm(self, projectordb=self.projectordb)
> + self.projector_form.newProjector.connect(self.add_projector_from_wizard)
> + self.projector_form.editProjector.connect(self.edit_projector_from_wizard)
> + self.projector_list_widget.itemSelectionChanged.connect(self.update_icons)
> +
> + def get_settings(self):
> + """
> + Retrieve the saved settings
> + """
> + settings = Settings()
> + settings.beginGroup(self.settings_section)
> + self.autostart = settings.value('connect on start')
> + self.poll_time = settings.value('poll time')
> + self.socket_timeout = settings.value('socket timeout')
> + self.source_select_dialog_type = settings.value('source dialog type')
> + settings.endGroup()
> + del(settings)
Are the parentheses necessary?
> +
> + def context_menu(self, point):
> + """
> + Build the Right Click Context menu and set state.
> +
> + :param point: The position of the mouse so the correct item can be found.
> + """
> + # QListWidgetItem to build menu for.
> + item = self.projector_list_widget.itemAt(point)
> + if item is None:
> + return
> + real_projector = item.data(QtCore.Qt.UserRole)
> + projector_name = str(item.text())
> + visible = real_projector.link.status_connect >= S_CONNECTED
> + log.debug('(%s) Building menu - visible = %s' % (projector_name, visible))
> + self.delete_action.setVisible(True)
> + self.edit_action.setVisible(True)
> + self.connect_action.setVisible(not visible)
> + self.disconnect_action.setVisible(visible)
> + self.status_action.setVisible(visible)
> + if visible:
> + self.select_input_action.setVisible(real_projector.link.power == S_ON)
> + self.poweron_action.setVisible(real_projector.link.power == S_STANDBY)
> + self.poweroff_action.setVisible(real_projector.link.power == S_ON)
> + self.blank_action.setVisible(real_projector.link.power == S_ON and
> + not real_projector.link.shutter)
> + self.show_action.setVisible(real_projector.link.power == S_ON and
> + real_projector.link.shutter)
> + else:
> + self.select_input_action.setVisible(False)
> + self.poweron_action.setVisible(False)
> + self.poweroff_action.setVisible(False)
> + self.blank_action.setVisible(False)
> + self.show_action.setVisible(False)
> + self.menu.projector = real_projector
> + self.menu.exec_(self.projector_list_widget.mapToGlobal(point))
> +
> + def on_select_input(self, opt=None):
> + """
> + Builds menu for 'Select Input' option, then calls the selected projector
> + item to change input source.
> +
> + :param opt: Needed by PyQt4
> + """
> + self.get_settings() # In case the dialog interface setting was changed
> + list_item = self.projector_list_widget.item(self.projector_list_widget.currentRow())
> + projector = list_item.data(QtCore.Qt.UserRole)
> + # QTabwidget for source select
> + if self.source_select_dialog_type == DialogSourceStyle.Tabbed:
> + source_select_form = SourceSelectTabs(parent=self,
> + projectordb=self.projectordb)
> + else:
> + source_select_form = SourceSelectSingle(parent=self,
> + projectordb=self.projectordb)
> + source = source_select_form.exec_(projector.link)
> + log.debug('(%s) source_select_form() returned %s' % (projector.link.ip, source))
> + if source is not None and source > 0:
> + projector.link.set_input_source(str(source))
> + return
> +
> + def on_add_projector(self, opt=None):
> + """
> + Calls edit dialog to add a new projector to the database
> +
> + :param opt: Needed by PyQt4
> + """
> + self.projector_form.exec_()
> +
> + def on_blank_projector(self, opt=None):
> + """
> + Calls projector thread to send blank screen command
> +
> + :param opt: Needed by PyQt4
> + """
> + try:
> + ip = opt.link.ip
> + projector = opt
> + projector.link.set_shutter_closed()
> + except AttributeError:
> + for list_item in self.projector_list_widget.selectedItems():
> + if list_item is None:
> + return
> + projector = list_item.data(QtCore.Qt.UserRole)
> + try:
> + projector.link.set_shutter_closed()
> + except:
> + continue
> +
> + def on_doubleclick_item(self, item, opt=None):
> + """
> + When item is doubleclicked, will connect to projector.
> +
> + :param item: List widget item for connection.
> + :param opt: Needed by PyQt4
> + """
> + projector = item.data(QtCore.Qt.UserRole)
> + if projector.link.state() != projector.link.ConnectedState:
> + try:
> + projector.link.connect_to_host()
> + except:
> + pass
> + return
> +
> + def on_connect_projector(self, opt=None):
> + """
> + Calls projector thread to connect to projector
> +
> + :param opt: Needed by PyQt4
> + """
> + try:
> + ip = opt.link.ip
> + projector = opt
> + projector.link.connect_to_host()
> + except AttributeError:
> + for list_item in self.projector_list_widget.selectedItems():
> + if list_item is None:
> + return
> + projector = list_item.data(QtCore.Qt.UserRole)
> + try:
> + projector.link.connect_to_host()
> + except:
> + continue
> +
> + def on_delete_projector(self, opt=None):
> + """
> + Deletes a projector from the list and the database
> +
> + :param opt: Needed by PyQt4
> + """
> + list_item = self.projector_list_widget.item(self.projector_list_widget.currentRow())
> + if list_item is None:
> + return
> + projector = list_item.data(QtCore.Qt.UserRole)
> + msg = QtGui.QMessageBox()
> + msg.setText('Delete projector (%s) %s?' % (projector.link.ip, projector.link.name))
> + msg.setInformativeText('Are you sure you want to delete this projector?')
> + msg.setStandardButtons(msg.Cancel | msg.Ok)
> + msg.setDefaultButton(msg.Cancel)
> + ans = msg.exec_()
> + if ans == msg.Cancel:
> + return
> + try:
> + projector.link.projectorNetwork.disconnect(self.update_status)
> + except (AttributeError, TypeError):
> + pass
> + try:
> + projector.link.changeStatus.disconnect(self.update_status)
> + except (AttributeError, TypeError):
> + pass
> + try:
> + projector.link.authentication_error.disconnect(self.authentication_error)
> + except (AttributeError, TypeError):
> + pass
> + try:
> + projector.link.no_authentication_error.disconnect(self.no_authentication_error)
> + except (AttributeError, TypeError):
> + pass
> + try:
> + projector.link.projectorUpdateIcons.disconnect(self.update_icons)
> + except (AttributeError, TypeError):
> + pass
> + try:
> + projector.timer.stop()
> + projector.timer.timeout.disconnect(projector.link.poll_loop)
> + except (AttributeError, TypeError):
> + pass
> + try:
> + projector.socket_timer.stop()
> + projector.socket_timer.timeout.disconnect(projector.link.socket_abort)
> + except (AttributeError, TypeError):
> + pass
> + projector.thread.quit()
> + new_list = []
> + for item in self.projector_list:
> + if item.link.dbid == projector.link.dbid:
> + continue
> + new_list.append(item)
> + self.projector_list = new_list
> + list_item = self.projector_list_widget.takeItem(self.projector_list_widget.currentRow())
> + list_item = None
> + deleted = self.projectordb.delete_projector(projector.db_item)
> + for item in self.projector_list:
> + log.debug('New projector list - item: %s %s' % (item.link.ip, item.link.name))
> +
> + def on_disconnect_projector(self, opt=None):
> + """
> + Calls projector thread to disconnect from projector
> +
> + :param opt: Needed by PyQt4
> + """
> + try:
> + ip = opt.link.ip
> + projector = opt
> + projector.link.disconnect_from_host()
> + except AttributeError:
> + for list_item in self.projector_list_widget.selectedItems():
> + if list_item is None:
> + return
> + projector = list_item.data(QtCore.Qt.UserRole)
> + try:
> + projector.link.disconnect_from_host()
> + except:
> + continue
> +
> + def on_edit_projector(self, opt=None):
> + """
> + Calls edit dialog with selected projector to edit information
> +
> + :param opt: Needed by PyQt4
> + """
> + list_item = self.projector_list_widget.item(self.projector_list_widget.currentRow())
> + projector = list_item.data(QtCore.Qt.UserRole)
> + if projector is None:
> + return
> + self.old_projector = projector
> + projector.link.disconnect_from_host()
> + record = self.projectordb.get_projector_by_ip(projector.link.ip)
> + self.projector_form.exec_(record)
> + new_record = self.projectordb.get_projector_by_id(record.id)
> +
> + def on_poweroff_projector(self, opt=None):
> + """
> + Calls projector link to send Power Off command
> +
> + :param opt: Needed by PyQt4
> + """
> + try:
> + ip = opt.link.ip
> + projector = opt
> + projector.link.set_power_off()
> + except AttributeError:
> + for list_item in self.projector_list_widget.selectedItems():
> + if list_item is None:
> + return
> + projector = list_item.data(QtCore.Qt.UserRole)
> + try:
> + projector.link.set_power_off()
> + except:
> + continue
> +
> + def on_poweron_projector(self, opt=None):
> + """
> + Calls projector link to send Power On command
> +
> + :param opt: Needed by PyQt4
> + """
> + try:
> + ip = opt.link.ip
> + projector = opt
> + projector.link.set_power_on()
> + except AttributeError:
> + for list_item in self.projector_list_widget.selectedItems():
> + if list_item is None:
> + return
> + projector = list_item.data(QtCore.Qt.UserRole)
> + try:
> + projector.link.set_power_on()
> + except:
> + continue
> +
> + def on_show_projector(self, opt=None):
> + """
> + Calls projector thread to send open shutter command
> +
> + :param opt: Needed by PyQt4
> + """
> + try:
> + ip = opt.link.ip
> + projector = opt
> + projector.link.set_shutter_open()
> + except AttributeError:
> + for list_item in self.projector_list_widget.selectedItems():
> + if list_item is None:
> + return
> + projector = list_item.data(QtCore.Qt.UserRole)
> + try:
> + projector.link.set_shutter_open()
> + except:
> + continue
> +
> + def on_status_projector(self, opt=None):
> + """
> + Builds message box with projector status information
> +
> + :param opt: Needed by PyQt4
> + """
> + lwi = self.projector_list_widget.item(self.projector_list_widget.currentRow())
> + projector = lwi.data(QtCore.Qt.UserRole)
> + message = '<b>%s</b>: %s<BR />' % (translate('OpenLP.ProjectorManager', 'Name'),
> + projector.link.name)
> + message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'IP'),
> + projector.link.ip)
> + message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Port'),
> + projector.link.port)
> + message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Notes'),
> + projector.link.notes)
> + message = '%s<hr /><br >' % message
> + if projector.link.manufacturer is None:
> + message = '%s%s' % (message, translate('OpenLP.ProjectorManager',
> + 'Projector information not available at this time.'))
> + else:
> + message = '%s<b>%s</b>: %s<BR />' % (message, translate('OpenLP.ProjectorManager', 'Projector Name'),
> + projector.link.pjlink_name)
> + message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Manufacturer'),
> + projector.link.manufacturer)
> + message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Model'),
> + projector.link.model)
> + message = '%s<b>%s</b>: %s<br /><br />' % (message, translate('OpenLP.ProjectorManager', 'Other info'),
> + projector.link.other_info)
> + message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Power status'),
> + ERROR_MSG[projector.link.power])
> + message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Shutter is'),
> + translate('OpenLP.ProjectorManager', 'Closed')
> + if projector.link.shutter else translate('OpenLP', 'Open'))
> + message = '%s<b>%s</b>: %s<br />' % (message,
> + translate('OpenLP.ProjectorManager', 'Current source input is'),
> + projector.link.source)
> + count = 1
> + for item in projector.link.lamp:
> + message = '%s <b>%s %s</b> (%s) %s: %s<br />' % (message,
> + translate('OpenLP.ProjectorManager', 'Lamp'),
> + count,
> + translate('OpenLP.ProjectorManager', 'On')
> + if item['On']
> + else translate('OpenLP.ProjectorManager', 'Off'),
> + translate('OpenLP.ProjectorManager', 'Hours'),
> + item['Hours'])
> + count = count + 1
> + message = '%s<hr /><br />' % message
> + if projector.link.projector_errors is None:
> + message = '%s%s' % (message, translate('OpenLP.ProjectorManager', 'No current errors or warnings'))
> + else:
> + message = '%s<b>%s</b>' % (message, translate('OpenLP.ProjectorManager', 'Current errors/warnings'))
> + for (key, val) in projector.link.projector_errors.items():
> + message = '%s<b>%s</b>: %s<br />' % (message, key, ERROR_MSG[val])
> + QtGui.QMessageBox.information(self, translate('OpenLP.ProjectorManager', 'Projector Information'), message)
> +
> + def _add_projector(self, projector):
> + """
> + Helper app to build a projector instance
> +
> + :param projector: Dict of projector database information
> + :returns: PJLink1() instance
> + """
> + log.debug('_add_projector()')
> + return PJLink1(dbid=projector.id,
> + ip=projector.ip,
> + port=int(projector.port),
> + name=projector.name,
> + location=projector.location,
> + notes=projector.notes,
> + pin=None if projector.pin == '' else projector.pin,
> + poll_time=self.poll_time,
> + socket_timeout=self.socket_timeout
> + )
> +
> + def add_projector(self, opt1, opt2=None):
> + """
> + Builds manager list item, projector thread, and timer for projector instance.
> +
> + If called by add projector wizard:
> + opt1 = wizard instance
> + opt2 = item
> + Otherwise:
> + opt1 = item
> + opt2 = None
> +
> + We are not concerned with the wizard instance,
> + just the projector item
> +
> + :param opt1: See above
> + :param opt2: See above
> + """
> + if opt1 is None:
> + return
> + if opt2 is None:
> + projector = opt1
> + else:
> + projector = opt2
> + item = ProjectorItem(link=self._add_projector(projector))
> + item.db_item = projector
> + icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[S_NOT_CONNECTED]))
> + item.icon = icon
> + widget = QtGui.QListWidgetItem(icon,
> + item.link.name,
> + self.projector_list_widget
> + )
> + widget.setData(QtCore.Qt.UserRole, item)
> + item.widget = widget
> + thread = QThread(parent=self)
> + thread.my_parent = self
> + item.moveToThread(thread)
> + thread.started.connect(item.link.thread_started)
> + thread.finished.connect(item.link.thread_stopped)
> + thread.finished.connect(thread.deleteLater)
> + item.link.projectorNetwork.connect(self.update_status)
> + item.link.changeStatus.connect(self.update_status)
> + item.link.projectorAuthentication.connect(self.authentication_error)
> + item.link.projectorNoAuthentication.connect(self.no_authentication_error)
> + item.link.projectorUpdateIcons.connect(self.update_icons)
> + timer = QtCore.QTimer(self)
> + timer.setInterval(self.poll_time)
> + timer.timeout.connect(item.link.poll_loop)
> + item.timer = timer
> + socket_timer = QtCore.QTimer(self)
> + socket_timer.setInterval(5000) # % second timer in case of brain-dead projectors
> + socket_timer.timeout.connect(item.link.socket_abort)
> + item.socket_timer = socket_timer
> + thread.start()
> + item.thread = thread
> + item.link.timer = timer
> + item.link.socket_timer = socket_timer
> + item.link.widget = item.widget
> + self.projector_list.append(item)
> + if self.autostart:
> + item.link.connect_to_host()
> + for i in self.projector_list:
> + log.debug('New projector list - item: (%s) %s' % (i.link.ip, i.link.name))
> +
> + @pyqtSlot(str)
> + def add_projector_from_wizard(self, ip, opts=None):
> + """
> + Add a projector from the edit dialog
> +
> + :param ip: IP address of new record item to find
> + :param opts: Needed by PyQt4
> + """
> + log.debug('load_projector(ip=%s)' % ip)
> + item = self.projectordb.get_projector_by_ip(ip)
> + self.add_projector(item)
> +
> + @pyqtSlot(object)
> + def edit_projector_from_wizard(self, projector, opts=None):
> + """
> + Update projector from the wizard edit page
> +
> + :param projector: Projector() instance of projector with updated information
> + :param opts: Needed by PyQt4
> + """
> +
> + self.old_projector.link.name = projector.name
> + self.old_projector.link.ip = projector.ip
> + self.old_projector.link.pin = None if projector.pin == '' else projector.pin
> + self.old_projector.link.port = projector.port
> + self.old_projector.link.location = projector.location
> + self.old_projector.link.notes = projector.notes
> + self.old_projector.widget.setText(projector.name)
> +
> + def _load_projectors(self):
> + """'
> + Load projectors - only call when initializing
> + """
> + log.debug('load_projectors()')
> + self.projector_list_widget.clear()
> + for item in self.projectordb.get_projector_all():
> + self.add_projector(item)
> +
> + def get_projector_list(self):
> + """
> + Return the list of active projectors
> +
> + :returns: list
> + """
> + return self.projector_list
> +
> + @pyqtSlot(str, int, str)
> + def update_status(self, ip, status=None, msg=None):
> + """
> + Update the status information/icon for selected list item
> +
> + :param ip: IP address of projector
> + :param status: Optional status code
> + :param msg: Optional status message
> + """
> + if status is None:
> + return
> + item = None
> + for list_item in self.projector_list:
> + if ip == list_item.link.ip:
> + item = list_item
> + break
> + message = translate('OpenLP.ProjectorManager', 'No message') if msg is None else msg
> + if status in STATUS_STRING:
> + status_code = STATUS_STRING[status]
> + message = ERROR_MSG[status] if msg is None else msg
> + elif status in ERROR_STRING:
> + status_code = ERROR_STRING[status]
> + message = ERROR_MSG[status] if msg is None else msg
> + else:
> + status_code = status
> + message = ERROR_MSG[status] if msg is None else msg
> + log.debug('(%s) updateStatus(status=%s) message: "%s"' % (item.link.name, status_code, message))
> + if status in STATUS_ICONS:
> + if item.status == status:
> + return
> + item.status = status
> + item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status]))
> + if status in ERROR_STRING:
> + status_code = ERROR_STRING[status]
> + elif status in STATUS_STRING:
> + status_code = STATUS_STRING[status]
> + log.debug('(%s) Updating icon with %s' % (item.link.name, status_code))
> + item.widget.setIcon(item.icon)
> + self.update_icons()
> +
> + def get_toolbar_item(self, name, enabled=False, hidden=False):
> + item = self.one_toolbar.findChild(QtGui.QAction, name)
> + if item == 0:
> + log.debug('No item found with name "%s"' % name)
> + else:
> + log.debug('item "%s" updating enabled=%s hidden=%s' % (name, enabled, hidden))
> + item.setVisible(False if hidden else True)
> + item.setEnabled(True if enabled else False)
> +
> + @pyqtSlot()
> + def update_icons(self):
> + """
> + Update the icons when the selected projectors change
> + """
> + count = len(self.projector_list_widget.selectedItems())
> + projector = None
> + if count == 0:
> + self.get_toolbar_item('edit_projector')
> + self.get_toolbar_item('delete_projector')
> + self.get_toolbar_item('view_projector', hidden=True)
> + self.get_toolbar_item('source_projector', hidden=True)
> + self.get_toolbar_item('connect_projector')
> + self.get_toolbar_item('disconnect_projector')
> + self.get_toolbar_item('poweron_projector')
> + self.get_toolbar_item('poweroff_projector')
> + self.get_toolbar_item('blank_projector')
> + self.get_toolbar_item('show_projector')
> + self.get_toolbar_item('connect_projector_multiple', hidden=True)
> + self.get_toolbar_item('disconnect_projector_multiple', hidden=True)
> + self.get_toolbar_item('poweron_projector_multiple', hidden=True)
> + self.get_toolbar_item('poweroff_projector_multiple', hidden=True)
> + self.get_toolbar_item('blank_projector_multiple', hidden=True)
> + self.get_toolbar_item('show_projector_multiple', hidden=True)
> + elif count == 1:
> + projector = self.projector_list_widget.selectedItems()[0].data(QtCore.Qt.UserRole)
> + connected = projector.link.state() == projector.link.ConnectedState
> + power = projector.link.power == S_ON
> + self.get_toolbar_item('connect_projector_multiple', hidden=True)
> + self.get_toolbar_item('disconnect_projector_multiple', hidden=True)
> + self.get_toolbar_item('poweron_projector_multiple', hidden=True)
> + self.get_toolbar_item('poweroff_projector_multiple', hidden=True)
> + self.get_toolbar_item('blank_projector_multiple', hidden=True)
> + self.get_toolbar_item('show_projector_multiple', hidden=True)
> + if connected:
> + self.get_toolbar_item('view_projector', enabled=True)
> + self.get_toolbar_item('source_projector',
> + enabled=connected and power and projector.link.source_available is not None)
> + self.get_toolbar_item('edit_projector', hidden=True)
> + self.get_toolbar_item('delete_projector', hidden=True)
> + else:
> + self.get_toolbar_item('view_projector', hidden=True)
> + self.get_toolbar_item('source_projector', hidden=True)
> + self.get_toolbar_item('edit_projector', enabled=True)
> + self.get_toolbar_item('delete_projector', enabled=True)
> + self.get_toolbar_item('connect_projector', enabled=not connected)
> + self.get_toolbar_item('disconnect_projector', enabled=connected)
> + self.get_toolbar_item('poweron_projector', enabled=connected and (projector.link.power == S_STANDBY))
> + self.get_toolbar_item('poweroff_projector', enabled=connected and (projector.link.power == S_ON))
> + if projector.link.shutter is not None:
> + self.get_toolbar_item('blank_projector', enabled=(connected and power and not projector.link.shutter))
> + self.get_toolbar_item('show_projector', enabled=(connected and power and projector.link.shutter))
> + else:
> + self.get_toolbar_item('blank_projector', enabled=False)
> + self.get_toolbar_item('show_projector', enabled=False)
> + else:
> + self.get_toolbar_item('edit_projector', enabled=False)
> + self.get_toolbar_item('delete_projector', enabled=False)
> + self.get_toolbar_item('view_projector', hidden=True)
> + self.get_toolbar_item('source_projector', hidden=True)
> + self.get_toolbar_item('connect_projector', hidden=True)
> + self.get_toolbar_item('disconnect_projector', hidden=True)
> + self.get_toolbar_item('poweron_projector', hidden=True)
> + self.get_toolbar_item('poweroff_projector', hidden=True)
> + self.get_toolbar_item('blank_projector', hidden=True)
> + self.get_toolbar_item('show_projector', hidden=True)
> + self.get_toolbar_item('connect_projector_multiple', hidden=False, enabled=True)
> + self.get_toolbar_item('disconnect_projector_multiple', hidden=False, enabled=True)
> + self.get_toolbar_item('poweron_projector_multiple', hidden=False, enabled=True)
> + self.get_toolbar_item('poweroff_projector_multiple', hidden=False, enabled=True)
> + self.get_toolbar_item('blank_projector_multiple', hidden=False, enabled=True)
> + self.get_toolbar_item('show_projector_multiple', hidden=False, enabled=True)
> +
> + @pyqtSlot(str)
> + def authentication_error(self, name):
> + """
> + Display warning dialog when attempting to connect with invalid pin
> +
> + :param name: Name from QListWidgetItem
> + """
> + QtGui.QMessageBox.warning(self, translate('OpenLP.ProjectorManager',
> + '"%s" Authentication Error' % name),
> + '<br />There was an authentictaion error while trying to connect.'
Typo in authentication
> + '<br /><br />Please verify your PIN setting '
> + 'for projector item "%s"' % name)
> +
> + @pyqtSlot(str)
> + def no_authentication_error(self, name):
> + """
> + Display warning dialog when pin saved for item but projector does not
> + require pin.
> +
> + :param name: Name from QListWidgetItem
> + """
> + QtGui.QMessageBox.warning(self, translate('OpenLP.ProjectorManager',
> + '"%s" No Authentication Error' % name),
> + '<br />PIN is set and projector does not require authentication.'
> + '<br /><br />Please verify your PIN setting '
> + 'for projector item "%s"' % name)
> +
> +
> +class ProjectorItem(QObject):
> + """
> + Class for the projector list widget item.
> + NOTE: Actual PJLink class instance should be saved as self.link
> + """
> + def __init__(self, link=None):
> + """
> + Initialization for ProjectorItem instance
> +
> + :param link: PJLink1 instance for QListWidgetItem
> + """
> + self.link = link
> + self.thread = None
> + self.icon = None
> + self.widget = None
> + self.my_parent = None
> + self.timer = None
> + self.projectordb_item = None
> + self.poll_time = None
> + self.socket_timeout = None
> + self.status = S_NOT_CONNECTED
> + super(ProjectorItem, self).__init__()
> +
> +
> +def not_implemented(function):
> + """
> + Temporary function to build an information message box indicating function not implemented yet
> +
> + :param func: Function name
> + """
> + QtGui.QMessageBox.information(None,
> + translate('OpenLP.ProjectorManager', 'Not Implemented Yet'),
> + translate('OpenLP.ProjectorManager',
> + 'Function "%s"<br />has not been implemented yet.'
> + '<br />Please check back again later.' % function))
>
> === added file 'openlp/core/ui/projector/sourceselectform.py'
> --- openlp/core/ui/projector/sourceselectform.py 1970-01-01 00:00:00 +0000
> +++ openlp/core/ui/projector/sourceselectform.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,362 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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.ui.projector.sourceselectform` module
> +
> + Provides the dialog window for selecting video source for projector.
> +"""
> +import logging
> +log = logging.getLogger(__name__)
> +log.debug('editform loaded')
> +
> +from PyQt4 import QtCore, QtGui
> +from PyQt4.QtCore import pyqtSlot, pyqtSignal, QSize
> +from PyQt4.QtGui import QDialog, QButtonGroup, QDialogButtonBox, QGroupBox, QRadioButton, \
> + QStyle, QStylePainter, QStyleOptionTab, QTabBar, QTabWidget, QVBoxLayout, QWidget
> +
> +from openlp.core.common import translate, is_macosx
> +from openlp.core.lib import build_icon
> +from openlp.core.lib.projector.constants import PJLINK_DEFAULT_SOURCES
> +
> +
> +def source_group(inputs, source_text):
> + """
> + Return a dictionary where key is source[0] and values are inputs
> + grouped by source[0].
> +
> + source_text = dict{"key1": "key1-text",
> + "key2": "key2-text",
> + ...}
> + ex:
> + dict{ key1[0]: { "key11": "key11-text",
> + "key12": "key12-text",
> + "key13": "key13-text",
> + ... }
> + key2[0]: {"key21": "key21-text",
> + "key22": "key22-text",
> + ... }
> +
> + :param inputs: List of inputs
> + :param source_text: Dictionary of {code: text} values to display
> + :returns: dict
> + """
> + groupdict = {}
> + keydict = {}
> + checklist = inputs
> + key = checklist[0][0]
> + for item in checklist:
> + if item[0] == key:
> + groupdict[item] = source_text[item]
> + continue
> + else:
> + keydict[key] = groupdict
> + key = item[0]
> + groupdict = {item: source_text[item]}
> + keydict[key] = groupdict
> + return keydict
> +
> +
> +def Build_Tab(group, source_key, default):
> + """
> + Create the radio button page for a tab.
> + Dictionary will be a 1-key entry where key=tab to setup, val=list of inputs.
> +
> + source_key: {"groupkey1": {"key11": "key11-text",
> + "key12": "key12-text",
> + ...
> + },
> + "groupkey2": {"key21": "key21-text",
> + "key22": "key22-text",
> + ....
> + },
> + ...
> + }
> +
> + :param group: Button group widget to add buttons to
> + :param source_key: Dictionary of sources for radio buttons
> + :param default: Default radio button to check
> + """
> + buttonchecked = False
> + widget = QWidget()
> + layout = QVBoxLayout()
> + layout.setSpacing(10)
> + widget.setLayout(layout)
> + tempkey = list(source_key.keys())[0] # Should only be 1 key
> + sourcelist = list(source_key[tempkey])
> + sourcelist.sort()
> + button_count = len(sourcelist)
> + for key in sourcelist:
> + itemwidget = QRadioButton(source_key[tempkey][key])
> + itemwidget.setAutoExclusive(True)
> + if default == key:
> + itemwidget.setChecked(True)
> + buttonchecked = itemwidget.isChecked() or buttonchecked
> + group.addButton(itemwidget, int(key))
> + layout.addWidget(itemwidget)
> + layout.addStretch()
> + return (widget, button_count, buttonchecked)
Are the parentheses necessary?
> +
> +
> +class FingerTabBarWidget(QTabBar):
> + """
> + Realign west -orientation tabs to left-right text rather than south-north text
> + Borrowed from
> + http://www.kidstrythisathome.com/2013/03/fingertabs-horizontal-tabs-with-horizontal-text-in-pyqt/
> + """
> + def __init__(self, parent=None, *args, **kwargs):
> + """
> + Reset tab text orientation on initialization
> +
> + :param width: Remove default width parameter in kwargs
> + :param height: Remove default height parameter in kwargs
> + """
> + self.tabSize = QSize(kwargs.pop('width', 100), kwargs.pop('height', 25))
> + QTabBar.__init__(self, parent, *args, **kwargs)
> +
> + def paintEvent(self, event):
> + """
> + Repaint tab in left-right text orientation.
> +
> + :param event: Repaint event signal
> + """
> + painter = QStylePainter(self)
> + option = QStyleOptionTab()
> +
> + for index in range(self.count()):
> + self.initStyleOption(option, index)
> + tabRect = self.tabRect(index)
> + tabRect.moveLeft(10)
> + painter.drawControl(QStyle.CE_TabBarTabShape, option)
> + painter.drawText(tabRect, QtCore.Qt.AlignVCenter |
> + QtCore.Qt.TextDontClip,
> + self.tabText(index))
> + painter.end()
> +
> + def tabSizeHint(self, index):
> + """
> + Return tabsize
> +
> + :param index: Tab index to fetch tabsize from
> + :returns: instance tabSize
> + """
> + return self.tabSize
> +
> +
> +class FingerTabWidget(QTabWidget):
> + """
> + A QTabWidget equivalent which uses our FingerTabBarWidget
> +
> + Based on thread discussion
> + http://www.riverbankcomputing.com/pipermail/pyqt/2005-December/011724.html
> + """
> + def __init__(self, parent, *args):
> + """
> + Initialize FingerTabWidget instance
> + """
> + QTabWidget.__init__(self, parent, *args)
> + self.setTabBar(FingerTabBarWidget(self))
> +
> +
> +class SourceSelectTabs(QDialog):
> + """
> + Class for handling selecting the source for the projector to use.
> + Uses tabbed interface.
> + """
> + def __init__(self, parent, projectordb):
> + """
> + Build the source select dialog using tabbed interface.
> +
> + :param projectordb: ProjectorDB session to use
> + """
> + log.debug('Initializing SourceSelectTabs()')
> + self.projectordb = projectordb
> + super(SourceSelectTabs, self).__init__(parent)
> + self.setWindowTitle(translate('OpenLP.SourceSelectForm', 'Select Projector Source'))
> + self.setObjectName('source_select_tabs')
> + self.setWindowIcon(build_icon(':/icon/openlp-log-32x32.png'))
> + self.setModal(True)
> + self.layout = QVBoxLayout()
> + self.layout.setObjectName('source_select_tabs_layout')
> + if is_macosx():
> + self.tabwidget = QTabWidget(self)
> + else:
> + self.tabwidget = FingerTabWidget(self)
> + self.tabwidget.setObjectName('source_select_tabs_tabwidget')
> + self.tabwidget.setUsesScrollButtons(False)
> + if is_macosx():
> + self.tabwidget.setTabPosition(QTabWidget.North)
> + else:
> + self.tabwidget.setTabPosition(QTabWidget.West)
> + self.layout.addWidget(self.tabwidget)
> + self.setLayout(self.layout)
> +
> + def exec_(self, projector):
> + """
> + Override initial method so we can build the tabs.
> +
> + :param projector: Projector instance to build source list from
> + """
> + self.projector = projector
> + self.source_text = self.projectordb.get_source_list(projector=projector)
> + self.source_group = source_group(projector.source_available, self.source_text)
> + # self.source_group = {'4': {'41': 'Storage 1'}, '5': {"51": 'Network 1'}}
> + self.button_group = QButtonGroup()
> + keys = list(self.source_group.keys())
> + keys.sort()
> + for key in keys:
> + (tab, button_count, buttonchecked) = Build_Tab(group=self.button_group,
> + source_key={key: self.source_group[key]},
> + default=self.projector.source)
> + thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key])
> + if buttonchecked:
> + self.tabwidget.setCurrentIndex(thistab)
> + self.button_box = QDialogButtonBox(QtGui.QDialogButtonBox.Ok |
> + QtGui.QDialogButtonBox.Cancel)
> + self.button_box.accepted.connect(self.accept_me)
> + self.button_box.rejected.connect(self.reject_me)
> + self.layout.addWidget(self.button_box)
> + selected = super(SourceSelectTabs, self).exec_()
> + return selected
> +
> + @pyqtSlot()
> + def accept_me(self):
> + """
> + Slot to accept 'OK' button
> + """
> + selected = self.button_group.checkedId()
> + log.debug('SourceSelectTabs().accepted() Setting source to %s' % selected)
> + self.done(selected)
> +
> + @pyqtSlot()
> + def reject_me(self):
> + """
> + Slot to accept 'Cancel' button
> + """
> + log.debug('SourceSelectTabs() - Rejected')
> + self.done(0)
> +
> +
> +class SourceSelectSingle(QDialog):
> + """
> + Class for handling selecting the source for the projector to use.
> + Uses single dialog interface.
> + """
> + def __init__(self, parent, projectordb):
> + """
> + Build the source select dialog.
> +
> + :param projectordb: ProjectorDB session to use
> + """
> + log.debug('Initializing SourceSelectSingle()')
> + self.projectordb = projectordb
> + super(SourceSelectSingle, self).__init__(parent)
> + self.setWindowTitle(translate('OpenLP.SourceSelectSingle', 'Select Projector Source'))
> + self.setObjectName('source_select_single')
> + self.setWindowIcon(build_icon(':/icon/openlp-log-32x32.png'))
> + self.setModal(True)
> + self.layout = QVBoxLayout()
> + self.layout.setObjectName('source_select_tabs_layout')
> + self.layout.setSpacing(10)
> + self.setLayout(self.layout)
> + self.setMinimumWidth(350)
> + self.button_group = QButtonGroup()
> + self.button_group.setObjectName('source_select_single_buttongroup')
> +
> + def exec_(self, projector):
> + """
> + Override initial method so we can build the tabs.
> +
> + :param projector: Projector instance to build source list from
> + """
> + self.projector = projector
> + self.source_text = self.projectordb.get_source_list(projector.manufacturer,
> + projector.model,
> + projector.source_available)
> + keys = list(self.source_text.keys())
> + keys.sort()
> + key_count = len(keys)
> + button_list = []
> + for key in keys:
> + button = QtGui.QRadioButton(self.source_text[key])
> + button.setChecked(True if key == projector.source else False)
> + self.layout.addWidget(button)
> + self.button_group.addButton(button, int(key))
> + button_list.append(key)
> + self.button_box = QDialogButtonBox(QtGui.QDialogButtonBox.Ok |
> + QtGui.QDialogButtonBox.Cancel)
> + self.button_box.accepted.connect(self.accept_me)
> + self.button_box.rejected.connect(self.reject_me)
> + self.layout.addWidget(self.button_box)
> + self.setMinimumHeight(key_count*25)
> + selected = super(SourceSelectSingle, self).exec_()
> + return selected
Should this return be here?
> +
> + title = QtGui.QLabel(translate('OpenLP.SourceSelectSingle', 'Select the input source below'))
> + self.layout.addWidget(title)
> + self.radio_buttons = []
> + source_list = self.projectordb.get_source_list(make=projector.link.manufacturer,
> + model=projector.link.model,
> + sources=projector.link.source_available)
> + sort = []
> + for item in source_list.keys():
> + sort.append(item)
> + sort.sort()
> + current = QtGui.QLabel(translate('OpenLP.SourceSelectSingle', 'Current source is %s' %
> + source_list[projector.link.source]))
> + layout.addWidget(current)
> + for item in sort:
> + button = self._select_input_widget(parent=self,
> + selected=projector.link.source,
> + code=item,
> + text=source_list[item])
> + layout.addWidget(button)
> + button_box = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok |
> + QtGui.QDialogButtonBox.Cancel)
> + button_box.accepted.connect(box.accept_me)
> + button_box.rejected.connect(box.reject_me)
> + layout.addWidget(button_box)
> + selected = super(SourceSelectSingle, self).exec_()
> + return selected
> +
> + @pyqtSlot()
> + def accept_me(self):
> + """
> + Slot to accept 'OK' button
> + """
> + selected = self.button_group.checkedId()
> + log.debug('SourceSelectDialog().accepted() Setting source to %s' % selected)
> + self.done(selected)
> +
> + @pyqtSlot()
> + def reject_me(self):
> + """
> + Slot to accept 'Cancel' button
> + """
> + log.debug('SourceSelectDialog() - Rejected')
> + self.done(0)
>
> === added file 'openlp/core/ui/projector/tab.py'
> --- openlp/core/ui/projector/tab.py 1970-01-01 00:00:00 +0000
> +++ openlp/core/ui/projector/tab.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,146 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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.ui.projector.tab`
> +
> + Provides the settings tab in the settings dialog.
> +"""
> +
> +import logging
> +log = logging.getLogger(__name__)
> +log.debug('projectortab module loaded')
> +
> +from PyQt4 import QtCore, QtGui
> +
> +from openlp.core.common import Settings, UiStrings, translate
> +from openlp.core.lib import SettingsTab
> +from openlp.core.lib.projector import DialogSourceStyle
> +
> +
> +class ProjectorTab(SettingsTab):
> + """
> + Openlp Settings -> Projector settings
> + """
> + def __init__(self, parent):
> + """
> + ProjectorTab initialization
> +
> + :param parent: Parent widget
> + """
> + self.icon_path = ':/projector/projector_manager.png'
> + projector_translated = translate('OpenLP.ProjectorTab', 'Projector')
> + super(ProjectorTab, self).__init__(parent, 'Projector', projector_translated)
> +
> + def setupUi(self):
> + """
> + Setup the UI
> + """
> + self.setObjectName('ProjectorTab')
> + super(ProjectorTab, self).setupUi()
> + self.connect_box = QtGui.QGroupBox(self.left_column)
> + self.connect_box.setObjectName('connect_box')
> + self.connect_box_layout = QtGui.QFormLayout(self.connect_box)
> + self.connect_box_layout.setObjectName('connect_box_layout')
> + # Start comms with projectors on startup
> + self.connect_on_startup = QtGui.QCheckBox(self.connect_box)
> + self.connect_on_startup.setObjectName('connect_on_startup')
> + self.connect_box_layout.addRow(self.connect_on_startup)
> + # Socket timeout
> + self.socket_timeout_label = QtGui.QLabel(self.connect_box)
> + self.socket_timeout_label.setObjectName('socket_timeout_label')
> + self.socket_timeout_spin_box = QtGui.QSpinBox(self.connect_box)
> + self.socket_timeout_spin_box.setObjectName('socket_timeout_spin_box')
> + self.socket_timeout_spin_box.setMinimum(2)
> + self.socket_timeout_spin_box.setMaximum(10)
> + self.connect_box_layout.addRow(self.socket_timeout_label, self.socket_timeout_spin_box)
> + # Poll interval
> + self.socket_poll_label = QtGui.QLabel(self.connect_box)
> + self.socket_poll_label.setObjectName('socket_poll_label')
> + self.socket_poll_spin_box = QtGui.QSpinBox(self.connect_box)
> + self.socket_poll_spin_box.setObjectName('socket_timeout_spin_box')
> + self.socket_poll_spin_box.setMinimum(5)
> + self.socket_poll_spin_box.setMaximum(60)
> + self.connect_box_layout.addRow(self.socket_poll_label, self.socket_poll_spin_box)
> + self.left_layout.addWidget(self.connect_box)
> + # Source input select dialog box type
> + self.dialog_type_label = QtGui.QLabel(self.connect_box)
> + self.dialog_type_label.setObjectName('dialog_type_label')
> + self.dialog_type_combo_box = QtGui.QComboBox(self.connect_box)
> + self.dialog_type_combo_box.setObjectName('dialog_type_combo_box')
> + self.dialog_type_combo_box.addItems(['', ''])
> + self.connect_box_layout.addRow(self.dialog_type_label, self.dialog_type_combo_box)
> + self.left_layout.addStretch()
> + self.dialog_type_combo_box.activated.connect(self.on_dialog_type_combo_box_changed)
> +
> + def retranslateUi(self):
> + """
> + Translate the UI on the fly
> + """
> + self.tab_title_visible = UiStrings().Projectors
> + self.connect_box.setTitle(
> + translate('OpenLP.ProjectorTab', 'Communication Options'))
> + self.connect_on_startup.setText(
> + translate('OpenLP.ProjectorTab', 'Connect to projectors on startup'))
> + self.socket_timeout_label.setText(
> + translate('OpenLP.ProjectorTab', 'Socket timeout (seconds)'))
> + self.socket_poll_label.setText(
> + translate('OpenLP.ProjectorTab', 'Poll time (seconds)'))
> + self.dialog_type_label.setText(
> + translate('Openlp.ProjectorTab', 'Source select dialog interface'))
> + self.dialog_type_combo_box.setItemText(DialogSourceStyle.Tabbed,
> + translate('OpenLP.ProjectorTab', 'Tabbed dialog box'))
> + self.dialog_type_combo_box.setItemText(DialogSourceStyle.Single,
> + translate('OpenLP.ProjectorTab', 'Single dialog box'))
> +
> + def load(self):
> + """
> + Load the projetor settings on startup
Typo in projector
> + """
> + settings = Settings()
> + settings.beginGroup(self.settings_section)
> + self.connect_on_startup.setChecked(settings.value('connect on start'))
> + self.socket_timeout_spin_box.setValue(settings.value('socket timeout'))
> + self.socket_poll_spin_box.setValue(settings.value('poll time'))
> + self.dialog_type_combo_box.setCurrentIndex(settings.value('source dialog type'))
> + settings.endGroup()
> +
> + def save(self):
> + """
> + Save the projector settings
> + """
> + settings = Settings()
> + settings.beginGroup(self.settings_section)
> + settings.setValue('connect on start', self.connect_on_startup.isChecked())
> + settings.setValue('socket timeout', self.socket_timeout_spin_box.value())
> + settings.setValue('poll time', self.socket_poll_spin_box.value())
> + settings.setValue('source dialog type', self.dialog_type_combo_box.currentIndex())
> + settings.endGroup
> +
> + def on_dialog_type_combo_box_changed(self):
> + self.dialog_type = self.dialog_type_combo_box.currentIndex()
>
> === modified file 'openlp/core/ui/settingsform.py'
> --- openlp/core/ui/settingsform.py 2014-04-12 20:19:22 +0000
> +++ openlp/core/ui/settingsform.py 2014-10-21 20:17:27 +0000
> @@ -38,6 +38,7 @@
> from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
> from openlp.core.ui.media import PlayerTab
> from .settingsdialog import Ui_SettingsDialog
> +from openlp.core.ui.projector.tab import ProjectorTab
>
> log = logging.getLogger(__name__)
>
> @@ -67,9 +68,10 @@
> self.stacked_layout.takeAt(0)
> self.insert_tab(self.general_tab, 0, PluginStatus.Active)
> self.insert_tab(self.themes_tab, 1, PluginStatus.Active)
> - self.insert_tab(self.advanced_tab, 2, PluginStatus.Active)
> - self.insert_tab(self.player_tab, 3, PluginStatus.Active)
> - count = 4
> + self.insert_tab(self.projector_tab, 2, PluginStatus.Active)
> + self.insert_tab(self.advanced_tab, 3, PluginStatus.Active)
> + self.insert_tab(self.player_tab, 4, PluginStatus.Active)
> + count = 5
> for plugin in self.plugin_manager.plugins:
> if plugin.settings_tab:
> self.insert_tab(plugin.settings_tab, count, plugin.status)
> @@ -125,6 +127,8 @@
> self.general_tab = GeneralTab(self)
> # Themes tab
> self.themes_tab = ThemesTab(self)
> + # Projector Tab
> + self.projector_tab = ProjectorTab(self)
> # Advanced tab
> self.advanced_tab = AdvancedTab(self)
> # Advanced tab
>
> === modified file 'resources/images/openlp-2.qrc'
> --- resources/images/openlp-2.qrc 2014-09-05 20:15:44 +0000
> +++ resources/images/openlp-2.qrc 2014-10-21 20:17:27 +0000
> @@ -106,6 +106,7 @@
> <file>wizard_firsttime.bmp</file>
> <file>wizard_createtheme.bmp</file>
> <file>wizard_duplicateremoval.bmp</file>
> + <file>wizard_createprojector.png</file>
> </qresource>
> <qresource prefix="services">
> <file>service_collapse_all.png</file>
> @@ -169,6 +170,34 @@
> <file>theme_new.png</file>
> <file>theme_edit.png</file>
> </qresource>
> + <qresource prefix="projector">
> + <file>projector_blank.png</file>
> + <file>projector_blank_tiled.png</file>
> + <file>projector_connect.png</file>
> + <file>projector_connect_tiled.png</file>
> + <file>projector_hdmi.png</file>
> + <file>projector_cooldown.png</file>
> + <file>projector_disconnect.png</file>
> + <file>projector_disconnect_tiled.png</file>
> + <file>projector_edit.png</file>
> + <file>projector_error.png</file>
> + <file>projector_item_connect.png</file>
> + <file>projector_item_disconnect.png</file>
> + <file>projector_manager.png</file>
> + <file>projector_new.png</file>
> + <file>projector_not_connected_error.png</file>
> + <file>projector_off.png</file>
> + <file>projector_on.png</file>
> + <file>projector_power_off.png</file>
> + <file>projector_power_off_tiled.png</file>
> + <file>projector_power_on.png</file>
> + <file>projector_power_on_tiled.png</file>
> + <file>projector_show.png</file>
> + <file>projector_show_tiled.png</file>
> + <file>projector_spacer.png</file>
> + <file>projector_warmup.png</file>
> + <file>projector_view.png</file>
> + </qresource>
> <qresource prefix="remotes">
> <file>android_app_qr.png</file>
> </qresource>
>
> === added file 'resources/images/projector_blank.png'
> Binary files resources/images/projector_blank.png 1970-01-01 00:00:00 +0000 and resources/images/projector_blank.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_blank_tiled.png'
> Binary files resources/images/projector_blank_tiled.png 1970-01-01 00:00:00 +0000 and resources/images/projector_blank_tiled.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_connect.png'
> Binary files resources/images/projector_connect.png 1970-01-01 00:00:00 +0000 and resources/images/projector_connect.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_connect_tiled.png'
> Binary files resources/images/projector_connect_tiled.png 1970-01-01 00:00:00 +0000 and resources/images/projector_connect_tiled.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_connectors.png'
> Binary files resources/images/projector_connectors.png 1970-01-01 00:00:00 +0000 and resources/images/projector_connectors.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_cooldown.png'
> Binary files resources/images/projector_cooldown.png 1970-01-01 00:00:00 +0000 and resources/images/projector_cooldown.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_disconnect.png'
> Binary files resources/images/projector_disconnect.png 1970-01-01 00:00:00 +0000 and resources/images/projector_disconnect.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_disconnect_tiled.png'
> Binary files resources/images/projector_disconnect_tiled.png 1970-01-01 00:00:00 +0000 and resources/images/projector_disconnect_tiled.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_edit.png'
> Binary files resources/images/projector_edit.png 1970-01-01 00:00:00 +0000 and resources/images/projector_edit.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_error.png'
> Binary files resources/images/projector_error.png 1970-01-01 00:00:00 +0000 and resources/images/projector_error.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_hdmi.png'
> Binary files resources/images/projector_hdmi.png 1970-01-01 00:00:00 +0000 and resources/images/projector_hdmi.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_item_connect.png'
> Binary files resources/images/projector_item_connect.png 1970-01-01 00:00:00 +0000 and resources/images/projector_item_connect.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_item_disconnect.png'
> Binary files resources/images/projector_item_disconnect.png 1970-01-01 00:00:00 +0000 and resources/images/projector_item_disconnect.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_manager.png'
> Binary files resources/images/projector_manager.png 1970-01-01 00:00:00 +0000 and resources/images/projector_manager.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_new.png'
> Binary files resources/images/projector_new.png 1970-01-01 00:00:00 +0000 and resources/images/projector_new.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_not_connected_error.png'
> Binary files resources/images/projector_not_connected_error.png 1970-01-01 00:00:00 +0000 and resources/images/projector_not_connected_error.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_off.png'
> Binary files resources/images/projector_off.png 1970-01-01 00:00:00 +0000 and resources/images/projector_off.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_on.png'
> Binary files resources/images/projector_on.png 1970-01-01 00:00:00 +0000 and resources/images/projector_on.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_power_off.png'
> Binary files resources/images/projector_power_off.png 1970-01-01 00:00:00 +0000 and resources/images/projector_power_off.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_power_off_tiled.png'
> Binary files resources/images/projector_power_off_tiled.png 1970-01-01 00:00:00 +0000 and resources/images/projector_power_off_tiled.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_power_on.png'
> Binary files resources/images/projector_power_on.png 1970-01-01 00:00:00 +0000 and resources/images/projector_power_on.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_power_on_tiled.png'
> Binary files resources/images/projector_power_on_tiled.png 1970-01-01 00:00:00 +0000 and resources/images/projector_power_on_tiled.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_show.png'
> Binary files resources/images/projector_show.png 1970-01-01 00:00:00 +0000 and resources/images/projector_show.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_show_tiled.png'
> Binary files resources/images/projector_show_tiled.png 1970-01-01 00:00:00 +0000 and resources/images/projector_show_tiled.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_spacer.png'
> Binary files resources/images/projector_spacer.png 1970-01-01 00:00:00 +0000 and resources/images/projector_spacer.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_view.png'
> Binary files resources/images/projector_view.png 1970-01-01 00:00:00 +0000 and resources/images/projector_view.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/projector_warmup.png'
> Binary files resources/images/projector_warmup.png 1970-01-01 00:00:00 +0000 and resources/images/projector_warmup.png 2014-10-21 20:17:27 +0000 differ
> === added file 'resources/images/wizard_createprojector.png'
> Binary files resources/images/wizard_createprojector.png 1970-01-01 00:00:00 +0000 and resources/images/wizard_createprojector.png 2014-10-21 20:17:27 +0000 differ
> === modified file 'scripts/generate_resources.sh'
> --- scripts/generate_resources.sh 2014-09-08 20:43:21 +0000
> +++ scripts/generate_resources.sh 2014-10-21 20:17:27 +0000
> @@ -53,5 +53,6 @@
> patch --posix -s openlp/core/resources.py scripts/resources.patch
>
> # Remove temporary file
> -rm openlp/core/resources.py.new
> -
> +rm openlp/core/resources.py.new 2>/dev/null
> +rm openlp/core/resources.py.old 2>/dev/null
> +rm openlp/core/resources.py.orig 2>/dev/null
>
> === added file 'tests/functional/openlp_core_common/test_projector_utilities.py'
> --- tests/functional/openlp_core_common/test_projector_utilities.py 1970-01-01 00:00:00 +0000
> +++ tests/functional/openlp_core_common/test_projector_utilities.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,163 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
> +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
> +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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 #
> +###############################################################################
> +"""
> +Package to test the openlp.core.ui.projector.networkutils package.
> +"""
> +
> +from unittest import TestCase
> +
> +from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash
> +
> +salt = '498e4a67'
> +pin = 'JBMIAProjectorLink'
> +test_hash = '5d8409bc1c3fa39749434aa3a5c38682'
> +
> +ip4_loopback = '127.0.0.1'
> +ip4_local = '192.168.1.1'
> +ip4_broadcast = '255.255.255.255'
> +ip4_bad = '192.168.1.256'
> +
> +ip6_loopback = '::1'
> +ip6_link_local = 'fe80::223:14ff:fe99:d315'
> +ip6_bad = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
> +
> +
> +class testProjectorUtilities(TestCase):
> + """
> + Validate functions in the projector utilities module
> + """
> + def test_ip4_loopback_valid(self):
> + """
> + Test IPv4 loopbackvalid
> + """
> + # WHEN: Test with a local loopback test
> + valid = verify_ip_address(addr=ip4_loopback)
> +
> + # THEN: Verify we received True
> + self.assertTrue(valid, 'IPv4 loopback address should have been valid')
> +
> + def test_ip4_local_valid(self):
> + """
> + Test IPv4 local valid
> + """
> + # WHEN: Test with a local loopback test
> + valid = verify_ip_address(addr=ip4_local)
> +
> + # THEN: Verify we received True
> + self.assertTrue(valid, 'IPv4 local address should have been valid')
> +
> + def test_ip4_broadcast_valid(self):
> + """
> + Test IPv4 broadcast valid
> + """
> + # WHEN: Test with a local loopback test
> + valid = verify_ip_address(addr=ip4_broadcast)
> +
> + # THEN: Verify we received True
> + self.assertTrue(valid, 'IPv4 broadcast address should have been valid')
> +
> + def test_ip4_address_invalid(self):
> + """
> + Test IPv4 address invalid
> + """
> + # WHEN: Test with a local loopback test
> + valid = verify_ip_address(addr=ip4_bad)
> +
> + # THEN: Verify we received True
> + self.assertFalse(valid, 'Bad IPv4 address should not have been valid')
> +
> + def test_ip6_loopback_valid(self):
> + """
> + Test IPv6 loopback valid
> + """
> + # WHEN: Test IPv6 loopback address
> + valid = verify_ip_address(addr=ip6_loopback)
> +
> + # THEN: Validate return
> + self.assertTrue(valid, 'IPv6 loopback address should have been valid')
> +
> + def test_ip6_local_valid(self):
> + """
> + Test IPv6 link-local valid
> + """
> + # WHEN: Test IPv6 link-local address
> + valid = verify_ip_address(addr=ip6_link_local)
> +
> + # THEN: Validate return
> + self.assertTrue(valid, 'IPv6 link-local address should have been valid')
> +
> + def test_ip6_address_invalid(self):
> + """
> + Test NetworkUtils IPv6 address invalid
> + """
> + # WHEN: Given an invalid IPv6 address
> + valid = verify_ip_address(addr=ip6_bad)
> +
> + # THEN: Validate bad return
> + self.assertFalse(valid, 'IPv6 bad address should have been invalid')
> +
> + def test_md5_hash(self):
> + """
> + Test MD5 hash from salt+data pass (python)
> + """
> + # WHEN: Given a known salt+data
> + hash_ = md5_hash(salt=salt, data=pin)
> +
> + # THEN: Validate return has is same
> + self.assertEquals(hash_, test_hash, 'MD5 should have returned a good hash')
> +
> + def test_md5_hash_bad(self):
> + """
> + Test MD5 hash from salt+data fail (python)
> + """
> + # WHEN: Given a different salt+hash
> + hash_ = md5_hash(salt=pin, data=salt)
> +
> + # THEN: return data is different
> + self.assertNotEquals(hash_, test_hash, 'MD5 should have returned a bad hash')
> +
> + def test_qmd5_hash(self):
> + """
> + Test MD5 hash from salt+data pass (Qt)
> + """
> + # WHEN: Given a known salt+data
> + hash_ = qmd5_hash(salt=salt, data=pin)
> +
> + # THEN: Validate return has is same
> + self.assertEquals(hash_, test_hash, 'Qt-MD5 should have returned a good hash')
> +
> + def test_qmd5_hash_bad(self):
> + """
> + Test MD5 hash from salt+hash fail (Qt)
> + """
> + # WHEN: Given a different salt+hash
> + hash_ = qmd5_hash(salt=pin, data=salt)
> +
> + # THEN: return data is different
> + self.assertNotEquals(hash_, test_hash, 'Qt-MD5 should have returned a bad hash')
>
> === modified file 'tests/functional/openlp_core_lib/__init__.py'
> --- tests/functional/openlp_core_lib/__init__.py 2014-04-02 18:51:21 +0000
> +++ tests/functional/openlp_core_lib/__init__.py 2014-10-21 20:17:27 +0000
> @@ -27,5 +27,28 @@
> # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
> ###############################################################################
> """
> -Package to test the openlp.core.lib package.
> +Module-level functions for the functional test suite
> """
> +
> +import os
> +from tests.functional import patch
> +
> +from openlp.core.common import is_win
> +
> +from .test_projectordb import tmpfile
> +
> +
> +def setUp():
> + if not is_win():
> + # Wine creates a sharing violation during tests. Ignore.
> + try:
> + os.remove(tmpfile)
> + except:
> + pass
> +
> +
> +def tearDown():
> + """
> + Ensure test suite has been cleaned up after tests
> + """
> + patch.stopall()
>
> === added file 'tests/functional/openlp_core_lib/test_projectordb.py'
> --- tests/functional/openlp_core_lib/test_projectordb.py 1970-01-01 00:00:00 +0000
> +++ tests/functional/openlp_core_lib/test_projectordb.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,160 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
> +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
> +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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 #
> +###############################################################################
> +"""
> +Package to test the openlp.core.ui.projectordb find, edit, delete
> +record functions.
> +
> +PREREQUISITE: add_record() and get_all() functions validated.
> +"""
> +
> +from unittest import TestCase
> +from tests.functional import MagicMock, patch
> +
> +from openlp.core.lib.projector.db import Projector, ProjectorDB
> +
> +from tests.resources.projector.data import TEST1_DATA, TEST2_DATA, TEST3_DATA
> +
> +tmpfile = '/tmp/openlp-test-projectordb.sql'
> +
> +
> +def compare_data(one, two):
> + """
> + Verify two Projector() instances contain the same data
> + """
> + return one is not None and \
> + two is not None and \
> + one.ip == two.ip and \
> + one.port == two.port and \
> + one.name == two.name and \
> + one.location == two.location and \
> + one.notes == two.notes
> +
> +
> +def add_records(self, test):
> + """
> + Add record if not in database
> + """
> + record_list = self.projector.get_projector_all()
> + if len(record_list) < 1:
> + added = False
> + for record in test:
> + added = self.projector.add_projector(record) or added
> + return added
> +
> + for new_record in test:
> + added = None
> + for record in record_list:
> + if compare_data(record, new_record):
> + break
> + added = self.projector.add_projector(new_record)
> + return added
> +
> +
> +class TestProjectorDB(TestCase):
> + """
> + Test case for ProjectorDB
> + """
> + def setUp(self):
> + """
> + Set up anything necessary for all tests
> + """
> + if not hasattr(self, 'projector'):
> + with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:
> + mocked_init_url.start()
> + mocked_init_url.return_value = 'sqlite:///%s' % tmpfile
> + self.projector = ProjectorDB()
> +
> + def find_record_by_ip_test(self):
> + """
> + Test find record by IP
> + """
> + # GIVEN: Record entries in database
> + add_records(self, [TEST1_DATA, TEST2_DATA])
> +
> + # WHEN: Search for record using IP
> + record = self.projector.get_projector_by_ip(TEST2_DATA.ip)
> +
> + # THEN: Verify proper record returned
> + self.assertTrue(compare_data(TEST2_DATA, record),
> + 'Record found should have been test_2 data')
> +
> + def find_record_by_name_test(self):
> + """
> + Test find record by name
> + """
> + # GIVEN: Record entries in database
> + add_records(self, [TEST1_DATA, TEST2_DATA])
> +
> + # WHEN: Search for record using name
> + record = self.projector.get_projector_by_name(TEST2_DATA.name)
> +
> + # THEN: Verify proper record returned
> + self.assertTrue(compare_data(TEST2_DATA, record),
> + 'Record found should have been test_2 data')
> +
> + def record_delete_test(self):
> + """
> + Test record can be deleted
> + """
> + # GIVEN: Record in database
> + add_records(self, [TEST3_DATA, ])
> + record = self.projector.get_projector_by_ip(TEST3_DATA.ip)
> +
> + # WHEN: Record deleted
> + self.projector.delete_projector(record)
> +
> + # THEN: Verify record not retrievable
> + found = self.projector.get_projector_by_ip(TEST3_DATA.ip)
> + self.assertFalse(found, 'test_3 record should have been deleted')
> +
> + def record_edit_test(self):
> + """
> + Test edited record returns the same record ID with different data
> + """
> + # GIVEN: Record entries in database
> + add_records(self, [TEST1_DATA, TEST2_DATA])
> +
> + # WHEN: We retrieve a specific record
> + record = self.projector.get_projector_by_ip(TEST1_DATA.ip)
> + record_id = record.id
> +
> + # WHEN: Data is changed
> + record.ip = TEST3_DATA.ip
> + record.port = TEST3_DATA.port
> + record.pin = TEST3_DATA.pin
> + record.name = TEST3_DATA.name
> + record.location = TEST3_DATA.location
> + record.notes = TEST3_DATA.notes
> + updated = self.projector.update_projector(record)
> + self.assertTrue(updated, 'Save updated record should have returned True')
> + record = self.projector.get_projector_by_ip(TEST3_DATA.ip)
> +
> + # THEN: Record ID should remain the same, but data should be changed
> + self.assertEqual(record_id, record.id, 'Edited record should have the same ID')
> + self.assertTrue(compare_data(TEST3_DATA, record), 'Edited record should have new data')
>
> === modified file 'tests/interfaces/openlp_core_ui/__init__.py'
> --- tests/interfaces/openlp_core_ui/__init__.py 2014-03-14 22:08:44 +0000
> +++ tests/interfaces/openlp_core_ui/__init__.py 2014-10-21 20:17:27 +0000
> @@ -26,3 +26,35 @@
> # with this program; if not, write to the Free Software Foundation, Inc., 59 #
> # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
> ###############################################################################
> +"""
> +Module-level functions for the functional test suite
> +"""
> +
> +import os
> +from tests.interfaces import patch
> +
> +from openlp.core.common import is_win
> +
> +from .test_projectormanager import tmpfile
> +
> +
> +def setUp():
> + if not is_win():
> + # Wine creates a sharing violation during tests. Ignore.
> + try:
> + os.remove(tmpfile)
> + except:
> + pass
> +
> +
> +def tearDown():
> + """
> + Ensure test suite has been cleaned up after tests
> + """
> + patch.stopall()
> + if not is_win():
> + try:
> + # In case of changed schema, remove old test file
> + os.remove(tmpfile)
> + except FileNotFoundError:
> + pass
>
> === added file 'tests/interfaces/openlp_core_ui/test_projectormanager.py'
> --- tests/interfaces/openlp_core_ui/test_projectormanager.py 1970-01-01 00:00:00 +0000
> +++ tests/interfaces/openlp_core_ui/test_projectormanager.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,106 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
> +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
> +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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 #
> +###############################################################################
> +"""
> +Interface tests to test the themeManager class and related methods.
> +"""
> +
> +import os
> +from unittest import TestCase
> +
> +from openlp.core.common import Registry, Settings
> +from tests.functional import patch, MagicMock
> +from tests.helpers.testmixin import TestMixin
> +
> +from openlp.core.ui import ProjectorManager, ProjectorEditForm
> +from openlp.core.lib.projector.db import Projector, ProjectorDB
> +
> +from tests.resources.projector.data import TEST1_DATA, TEST2_DATA, TEST3_DATA
> +
> +tmpfile = '/tmp/openlp-test-projectormanager.sql'
> +
> +
> +class TestProjectorManager(TestCase, TestMixin):
> + """
> + Test the functions in the ProjectorManager module
> + """
> + def setUp(self):
> + """
> + Create the UI and setup necessary options
> + """
> + self.build_settings()
> + self.get_application()
> + Registry.create()
> + if not hasattr(self, 'projector_manager'):
> + with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:
> + mocked_init_url.start()
> + mocked_init_url.return_value = 'sqlite:///%s' % tmpfile
> + self.projectordb = ProjectorDB()
> + if not hasattr(self, 'projector_manager'):
> + self.projector_manager = ProjectorManager(projectordb=self.projectordb)
> +
> + def tearDown(self):
> + """
> + Remove test database.
> + Delete all the C++ objects at the end so that we don't have a segfault.
> + """
> + self.projectordb.session.close()
> + del(self.projector_manager)
Are the parentheses necessary?
> + self.destroy_settings()
> +
> + def bootstrap_initialise_test(self):
> + """
> + Test initialize calls correct startup functions
> + """
> + # WHEN: we call bootstrap_initialise
> + self.projector_manager.bootstrap_initialise()
> + # THEN: ProjectorDB is setup
> + self.assertEqual(type(self.projector_manager.projectordb), ProjectorDB,
> + 'Initialization should have created a ProjectorDB() instance')
> +
> + def bootstrap_post_set_up_test(self):
> + """
> + Test post-initialize calls proper setups
> + """
> + # GIVEN: setup mocks
> + self.projector_manager._load_projectors = MagicMock()
> +
> + # WHEN: Call to initialize is run
> + self.projector_manager.bootstrap_initialise()
> + self.projector_manager.bootstrap_post_set_up()
> +
> + # THEN: verify calls to retrieve saved projectors
> + self.assertEqual(1, self.projector_manager._load_projectors.call_count,
> + 'Initialization should have called load_projectors()')
> +
> + # THEN: Verify edit page is initialized
> + self.assertEqual(type(self.projector_manager.projector_form), ProjectorEditForm,
> + 'Initialization should have created a Projector Edit Form')
> + self.assertIs(self.projector_manager.projectordb,
> + self.projector_manager.projector_form.projectordb,
> + 'ProjectorEditForm should be using same ProjectorDB() instance as ProjectorManager')
>
> === added directory 'tests/resources/projector'
> === added file 'tests/resources/projector/data.py'
> --- tests/resources/projector/data.py 1970-01-01 00:00:00 +0000
> +++ tests/resources/projector/data.py 2014-10-21 20:17:27 +0000
> @@ -0,0 +1,55 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
> +# Christian Richter, Philip Ridout, Ken Roberts, Simon Scudder, #
> +# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
> +# Dave Warnock, Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
> +# --------------------------------------------------------------------------- #
> +# 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:`tests.resources.projector.data file contains test data
> +"""
> +
> +from openlp.core.lib.projector.db import Projector
> +
> +# Test data
> +TEST1_DATA = Projector(ip='111.111.111.111',
> + port='1111',
> + pin='1111',
> + name='___TEST_ONE___',
> + location='location one',
> + notes='notes one')
> +
> +TEST2_DATA = Projector(ip='222.222.222.222',
> + port='2222',
> + pin='2222',
> + name='___TEST_TWO___',
> + location='location two',
> + notes='notes two')
> +
> +TEST3_DATA = Projector(ip='333.333.333.333',
> + port='3333',
> + pin='3333',
> + name='___TEST_THREE___',
> + location='location three',
> + notes='notes three')
>
--
https://code.launchpad.net/~alisonken1/openlp/projector-2.1-merge/+merge/239130
Your team OpenLP Core is requested to review the proposed merge of lp:~alisonken1/openlp/projector-2.1-merge into lp:openlp.
References