# -*- 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: projectormanager` 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 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.constants import *
from openlp.core.lib.projector.db import ProjectorDB
from openlp.core.lib.projector.pjlink1 import PJLink1
from openlp.core.ui.projector.wizard import ProjectorWizard
# 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=':/general/general_find.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('disconnect_projector',
text=translate('OpenLP.ProjectorManager',
'Disconnect from selected projector'),
icon=':/projector/projector_disconnect.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('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.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('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.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)
# Build the context menu
self.menu = QtGui.QMenu()
self.status_action = create_widget_action(self.menu,
text=translate('OpenLP.ProjectorManager',
'&View Projector Information'),
icon=':/projector/projector_status.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_connectors.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, QtGui.QWidget, Ui_ProjectorManager, RegistryProperties):
"""
Manage the projectors.
"""
def __init__(self, parent=None, projectordb=None):
log.debug('__init__()')
super().__init__(parent)
self.settings_section = 'projector'
self.projectordb = projectordb
self.projector_list = []
def bootstrap_initialise(self):
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')
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')
settings.endGroup()
del(settings)
def bootstrap_post_set_up(self):
self.load_projectors()
self.projector_form = ProjectorWizard(self, projectordb=self.projectordb)
self.projector_form.edit_page.newProjector.connect(self.add_projector_from_wizard)
self.projector_form.edit_page.editProjector.connect(self.edit_projector_from_wizard)
self.projector_list_widget.itemSelectionChanged.connect(self.update_icons)
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
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 _select_input_widget(self, parent, selected, code, text):
"""
Build the radio button widget for selecting source input menu
:param parent: parent widget
:param selected: Selected widget text
:param code: PJLink code for this widget
:param text: Text to display
:returns: radio button widget
"""
widget = QtGui.QRadioButton(text, parent=parent)
widget.setChecked(True if code == selected else False)
widget.button_role = code
widget.clicked.connect(self._select_input_radio)
self.radio_buttons.append(widget)
return widget
def _select_input_radio(self, opt1=None, opt2=None):
"""
Returns the currently selected radio button
:param opt1: Needed by PyQt4
:param op2: future
:returns: Selected button role
"""
for button in self.radio_buttons:
if button.isChecked():
self.radio_button_selected = button.button_role
break
return
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
:returns: None
"""
list_item = self.projector_list_widget.item(self.projector_list_widget.currentRow())
projector = list_item.data(QtCore.Qt.UserRole)
layout = QtGui.QVBoxLayout()
box = QtGui.QDialog(parent=self)
box.setModal(True)
title = QtGui.QLabel(translate('OpenLP.ProjectorManager', 'Select the input source below'))
layout.addWidget(title)
self.radio_button_selected = None
self.radio_buttons = []
source_list = self.projectordb.get_source_list(make=projector.link.manufacturer,
model=projector.link.model,
sources=projector.link.source_available
)
if source_list is None:
return
sort = []
for item in source_list.keys():
sort.append(item)
sort.sort()
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)
button_box.rejected.connect(box.reject)
layout.addWidget(button_box)
box.setLayout(layout)
check = box.exec_()
if check == 0:
# Cancel button clicked or window closed - don't set source
return
selected = self.radio_button_selected
projector.link.set_input_source(self.radio_button_selected)
self.radio_button_selected = None
def on_add_projector(self, opt=None):
"""
Calls wizard to add a new projector to the database
:param opt: Needed by PyQt4
:returns: None
"""
self.projector_form.exec_()
def on_blank_projector(self, opt=None):
"""
Calls projector thread to send blank screen command
:param opt: Needed by PyQt4
:returns: None
"""
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_connect_projector(self, opt=None):
"""
Calls projector thread to connect to projector
:param opt: Needed by PyQt4
:returns: None
"""
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
:returns: None
"""
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 TypeError:
pass
try:
projector.link.changeStatus.disconnect(self.update_status)
except TypeError:
pass
try:
projector.link.authentication_error.disconnect(self.authentication_error)
except TypeError:
pass
try:
projector.link.no_authentication_error.disconnect(self.no_authentication_error)
except TypeError:
pass
try:
projector.link.projectorUpdateIcons.disconnect(self.update_icons)
except TypeError:
pass
try:
projector.timer.stop()
projector.timer.timeout.disconnect(projector.link.poll_loop)
except 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
:returns: None
"""
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 wizard with selected projector to edit information
:param opt: Needed by PyQt4
:returns: None
"""
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
:returns: None
"""
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
:returns: None
"""
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
:returns: None
"""
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
:returns: None
"""
lwi = self.projector_list_widget.item(self.projector_list_widget.currentRow())
projector = lwi.data(QtCore.Qt.UserRole)
message = '%s: %s
' % (translate('OpenLP.ProjectorManager', 'Name'),
projector.link.name)
message = '%s%s: %s
' % (message, translate('OpenLP.ProjectorManager', 'IP'),
projector.link.ip)
message = '%s%s: %s
' % (message, translate('OpenLP.ProjectorManager', 'Port'),
projector.link.port)
message = '%s%s: %s
' % (message, translate('OpenLP.ProjectorManager', 'Notes'),
projector.link.notes)
message = '%s