Source code for openlp.core.ui.projector.sourceselectform

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4

###############################################################################
# OpenLP - Open Source Lyrics Projection                                      #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2017 OpenLP Developers                                   #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it     #
# under the terms of the GNU General Public License as published by the Free  #
# Software Foundation; version 2 of the License.                              #
#                                                                             #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
# more details.                                                               #
#                                                                             #
# You should have received a copy of the GNU General Public License along     #
# with this program; if not, write to the Free Software Foundation, Inc., 59  #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
###############################################################################
"""
:mod: `openlp.core.ui.projector.sourceselectform` module

Provides the dialog window for selecting video source for projector.
"""
import logging

from PyQt5 import QtCore, QtWidgets

from openlp.core.common import translate, is_macosx
from openlp.core.lib import build_icon
from openlp.core.lib.projector.db import ProjectorSource
from openlp.core.lib.projector.constants import PJLINK_DEFAULT_SOURCES, PJLINK_DEFAULT_CODES

log = logging.getLogger(__name__)


[docs]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", ...} return: 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
[docs]def build_tab(group, source_key, default, projector, projectordb, edit=False): """ 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 :param projector: Projector instance :param projectordb: ProjectorDB instance for session :param edit: If we're editing the source text """ buttonchecked = False widget = QtWidgets.QWidget() layout = QtWidgets.QFormLayout() if edit else QtWidgets.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) if edit: for key in sourcelist: item = QtWidgets.QLineEdit() item.setObjectName('source_key_{key}'.format(key=key)) source_item = projectordb.get_source_by_code(code=key, projector_id=projector.db_item.id) if source_item is None: item.setText(PJLINK_DEFAULT_CODES[key]) else: item.setText(source_item.text) layout.addRow(PJLINK_DEFAULT_CODES[key], item) group.append(item) else: for key in sourcelist: source_item = projectordb.get_source_by_code(code=key, projector_id=projector.db_item.id) if source_item is None: text = source_key[tempkey][key] else: text = source_item.text itemwidget = QtWidgets.QRadioButton(text) 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
[docs]def set_button_tooltip(button_bar): """ Set the toolip for the standard buttons used :param button_bar: QDialogButtonBar instance to update """ for button in button_bar.buttons(): if button_bar.standardButton(button) == QtWidgets.QDialogButtonBox.Cancel: button.setToolTip(translate('OpenLP.SourceSelectForm', 'Ignoring current changes and return to OpenLP')) elif button_bar.standardButton(button) == QtWidgets.QDialogButtonBox.Reset: button.setToolTip(translate('OpenLP.SourceSelectForm', 'Delete all user-defined text and revert to PJLink default text')) elif button_bar.standardButton(button) == QtWidgets.QDialogButtonBox.Discard: button.setToolTip(translate('OpenLP.SourceSelectForm', 'Discard changes and reset to previous user-defined text')) elif button_bar.standardButton(button) == QtWidgets.QDialogButtonBox.Ok: button.setToolTip(translate('OpenLP.SourceSelectForm', 'Save changes and return to OpenLP')) else: log.debug('No tooltip for button {text}'.format(text=button.text()))
[docs]class FingerTabBarWidget(QtWidgets.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 = QtCore.QSize(kwargs.pop('width', 100), kwargs.pop('height', 25)) QtWidgets.QTabBar.__init__(self, parent, *args, **kwargs)
[docs] def paintEvent(self, event): """ Repaint tab in left-right text orientation. :param event: Repaint event signal """ painter = QtWidgets.QStylePainter(self) option = QtWidgets.QStyleOptionTab() for index in range(self.count()): self.initStyleOption(option, index) tabRect = self.tabRect(index) tabRect.moveLeft(10) painter.drawControl(QtWidgets.QStyle.CE_TabBarTabShape, option) painter.drawText(tabRect, QtCore.Qt.AlignVCenter | QtCore.Qt.TextDontClip, self.tabText(index)) painter.end()
[docs] def tabSizeHint(self, index): """ Return tabsize :param index: Tab index to fetch tabsize from :returns: instance tabSize """ return self.tabSize
[docs]class FingerTabWidget(QtWidgets.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 """ QtWidgets.QTabWidget.__init__(self, parent, *args) self.setTabBar(FingerTabBarWidget(self))
[docs]class SourceSelectTabs(QtWidgets.QDialog): """ Class for handling selecting the source for the projector to use. Uses tabbed interface. """ def __init__(self, parent, projectordb, edit=False): """ Build the source select dialog using tabbed interface. :param projectordb: ProjectorDB session to use """ log.debug('Initializing SourceSelectTabs()') super(SourceSelectTabs, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) self.setMinimumWidth(350) self.projectordb = projectordb self.edit = edit if self.edit: title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text') else: title = translate('OpenLP.SourceSelectForm', 'Select Projector Source') self.setWindowTitle(title) self.setObjectName('source_select_tabs') self.setWindowIcon(build_icon(':/icon/openlp-log.svg')) self.setModal(True) self.layout = QtWidgets.QVBoxLayout() self.layout.setObjectName('source_select_tabs_layout') if is_macosx(): self.tabwidget = QtWidgets.QTabWidget(self) else: self.tabwidget = FingerTabWidget(self) self.tabwidget.setObjectName('source_select_tabs_tabwidget') self.tabwidget.setUsesScrollButtons(False) if is_macosx(): self.tabwidget.setTabPosition(QtWidgets.QTabWidget.North) else: self.tabwidget.setTabPosition(QtWidgets.QTabWidget.West) self.layout.addWidget(self.tabwidget) self.setLayout(self.layout)
[docs] 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.button_group = [] if self.edit else QtWidgets.QButtonGroup() keys = list(self.source_group.keys()) keys.sort() if self.edit: 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, projector=self.projector, projectordb=self.projectordb, edit=self.edit) thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key]) if buttonchecked: self.tabwidget.setCurrentIndex(thistab) self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Reset | QtWidgets.QDialogButtonBox.Discard | QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) else: 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, projector=self.projector, projectordb=self.projectordb, edit=self.edit) thistab = self.tabwidget.addTab(tab, PJLINK_DEFAULT_SOURCES[key]) if buttonchecked: self.tabwidget.setCurrentIndex(thistab) self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) self.button_box.clicked.connect(self.button_clicked) self.layout.addWidget(self.button_box) set_button_tooltip(self.button_box) selected = super(SourceSelectTabs, self).exec() return selected
[docs] @QtCore.pyqtSlot(QtWidgets.QAbstractButton) def button_clicked(self, button): """ Checks which button was clicked :param button: Button that was clicked :returns: Ok: Saves current edits Delete: Resets text to last-saved text Reset: Reset all text to PJLink default text Cancel: Cancel text edit """ if self.button_box.standardButton(button) == self.button_box.Cancel: self.done(0) elif self.button_box.standardButton(button) == self.button_box.Reset: self.done(100) elif self.button_box.standardButton(button) == self.button_box.Discard: self.delete_sources() elif self.button_box.standardButton(button) == self.button_box.Ok: return self.accept_me() else: return 100
[docs] def delete_sources(self): """ Delete the sources for this projector """ msg = QtWidgets.QMessageBox() msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector')) msg.setInformativeText(translate('OpenLP.SourceSelectForm', 'Are you sure you want to delete ALL user-defined ' 'source input text for this projector?')) msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setDefaultButton(msg.Cancel) ans = msg.exec() if ans == msg.Cancel: return self.projectordb.delete_all_objects(ProjectorSource, ProjectorSource.projector_id == self.projector.db_item.id) self.done(100)
[docs] def accept_me(self): """ Slot to accept 'OK' button """ projector = self.projector.db_item if self.edit: for key in self.button_group: code = key.objectName().split("_")[-1] text = key.text().strip() if key.text().strip().lower() == PJLINK_DEFAULT_CODES[code].strip().lower(): continue item = self.projectordb.get_source_by_code(code=code, projector_id=projector.id) if item is None: log.debug("({ip}) Adding new source text {code}: {text}".format(ip=projector.ip, code=code, text=text)) item = ProjectorSource(projector_id=projector.id, code=code, text=text) else: item.text = text log.debug('({ip}) Updating source code {code} with text="{text}"'.format(ip=projector.ip, code=item.code, text=item.text)) self.projectordb.add_source(item) selected = 0 else: selected = self.button_group.checkedId() log.debug('SourceSelectTabs().accepted() Setting source to {selected}'.format(selected=selected)) self.done(selected)
[docs]class SourceSelectSingle(QtWidgets.QDialog): """ Class for handling selecting the source for the projector to use. Uses single dialog interface. """ def __init__(self, parent, projectordb, edit=False): """ Build the source select dialog. :param projectordb: ProjectorDB session to use """ log.debug('Initializing SourceSelectSingle()') self.projectordb = projectordb super(SourceSelectSingle, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint) self.edit = edit if self.edit: title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text') else: title = translate('OpenLP.SourceSelectForm', 'Select Projector Source') self.setObjectName('source_select_single') self.setWindowIcon(build_icon(':/icon/openlp-log.svg')) self.setModal(True) self.edit = edit
[docs] def exec(self, projector, edit=False): """ Override initial method so we can build the tabs. :param projector: Projector instance to build source list from """ self.projector = projector self.layout = QtWidgets.QFormLayout() if self.edit else QtWidgets.QVBoxLayout() self.layout.setObjectName('source_select_tabs_layout') self.layout.setSpacing(10) self.setLayout(self.layout) self.setMinimumWidth(350) self.button_group = [] if self.edit else QtWidgets.QButtonGroup() self.source_text = self.projectordb.get_source_list(projector=projector) keys = list(self.source_text.keys()) keys.sort() key_count = len(keys) button_list = [] if self.edit: for key in keys: item = QtWidgets.QLineEdit() item.setObjectName('source_key_{key}'.format(key=key)) source_item = self.projectordb.get_source_by_code(code=key, projector_id=self.projector.db_item.id) if source_item is None: item.setText(PJLINK_DEFAULT_CODES[key]) else: item.old_text = item.text() item.setText(source_item.text) self.layout.addRow(PJLINK_DEFAULT_CODES[key], item) self.button_group.append(item) self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Reset | QtWidgets.QDialogButtonBox.Discard | QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) else: for key in keys: source_text = self.projectordb.get_source_by_code(code=key, projector_id=self.projector.db_item.id) text = self.source_text[key] if source_text is None else source_text.text button = QtWidgets.QRadioButton(text) 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 = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel) self.button_box.clicked.connect(self.button_clicked) self.layout.addWidget(self.button_box) self.setMinimumHeight(key_count * 25) set_button_tooltip(self.button_box) selected = super(SourceSelectSingle, self).exec() return selected
[docs] @QtCore.pyqtSlot(QtWidgets.QAbstractButton) def button_clicked(self, button): """ Checks which button was clicked :param button: Button that was clicked :returns: Ok: Saves current edits Delete: Resets text to last-saved text Reset: Reset all text to PJLink default text Cancel: Cancel text edit """ if self.button_box.standardButton(button) == self.button_box.Cancel: self.done(0) elif self.button_box.standardButton(button) == self.button_box.Reset: self.done(100) elif self.button_box.standardButton(button) == self.button_box.Discard: self.delete_sources() elif self.button_box.standardButton(button) == self.button_box.Ok: return self.accept_me() else: return 100
[docs] def delete_sources(self): msg = QtWidgets.QMessageBox() msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector')) msg.setInformativeText(translate('OpenLP.SourceSelectForm', 'Are you sure you want to delete ALL user-defined ' 'source input text for this projector?')) msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setDefaultButton(msg.Cancel) ans = msg.exec() if ans == msg.Cancel: return self.projectordb.delete_all_objects(ProjectorSource, ProjectorSource.projector_id == self.projector.db_item.id) self.done(100)
[docs] @QtCore.pyqtSlot() def accept_me(self): """ Slot to accept 'OK' button """ projector = self.projector.db_item if self.edit: for key in self.button_group: code = key.objectName().split("_")[-1] text = key.text().strip() if key.text().strip().lower() == PJLINK_DEFAULT_CODES[code].strip().lower(): continue item = self.projectordb.get_source_by_code(code=code, projector_id=projector.id) if item is None: log.debug("({ip}) Adding new source text {code}: {text}".format(ip=projector.ip, code=code, text=text)) item = ProjectorSource(projector_id=projector.id, code=code, text=text) else: item.text = text log.debug('({ip}) Updating source code {code} with text="{text}"'.format(ip=projector.ip, code=item.code, text=item.text)) self.projectordb.add_source(item) selected = 0 else: selected = self.button_group.checkedId() log.debug('SourceSelectDialog().accepted() Setting source to {selected}'.format(selected=selected)) self.done(selected)