forked from openlp/openlp
head
This commit is contained in:
commit
d9d0a917fd
|
@ -11,7 +11,7 @@ recursive-include openlp *.ttf
|
|||
recursive-include documentation *
|
||||
recursive-include resources *
|
||||
recursive-include scripts *
|
||||
recursive-include tests/resources *
|
||||
recursive-include tests *
|
||||
include copyright.txt
|
||||
include LICENSE
|
||||
include README.txt
|
||||
|
|
|
@ -19,10 +19,3 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from openlp.core.api.http import register_endpoint, requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.poll import Poller
|
||||
from openlp.core.api.tab import ApiTab
|
||||
|
||||
__all__ = ['Endpoint', 'ApiTab', 'register_endpoint', 'requires_auth']
|
||||
|
|
|
@ -22,4 +22,3 @@
|
|||
"""
|
||||
The Endpoint class, which provides plugins with a way to serve their own portion of the API
|
||||
"""
|
||||
from .pluginhelpers import search, live, service
|
||||
|
|
|
@ -27,7 +27,6 @@ from webob import Response
|
|||
|
||||
from openlp.core.api.http.wsgiapp import WSGIApplication
|
||||
from openlp.core.common.settings import Settings
|
||||
from .errors import NotFound, ServerError, HttpError
|
||||
|
||||
application = WSGIApplication('api')
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ class Poller(RegistryProperties):
|
|||
if self.stage_cache is None:
|
||||
try:
|
||||
page = get_web_page("http://localhost:4316/stage")
|
||||
except:
|
||||
except Exception:
|
||||
page = None
|
||||
if page:
|
||||
self.stage_cache = True
|
||||
|
@ -106,7 +106,7 @@ class Poller(RegistryProperties):
|
|||
if self.live_cache is None:
|
||||
try:
|
||||
page = get_web_page("http://localhost:4316/main")
|
||||
except:
|
||||
except Exception:
|
||||
page = None
|
||||
if page:
|
||||
self.live_cache = True
|
||||
|
@ -122,7 +122,7 @@ class Poller(RegistryProperties):
|
|||
if self.chords_cache is None:
|
||||
try:
|
||||
page = get_web_page("http://localhost:4316/chords")
|
||||
except:
|
||||
except Exception:
|
||||
page = None
|
||||
if page:
|
||||
self.chords_cache = True
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
The :mod:`languages` module provides a list of language names with utility functions.
|
||||
"""
|
||||
import itertools
|
||||
import locale
|
||||
import logging
|
||||
import re
|
||||
from collections import namedtuple
|
||||
|
@ -53,8 +52,7 @@ def translate(context, text, comment=None, qt_translate=QtCore.QCoreApplication.
|
|||
|
||||
|
||||
Language = namedtuple('Language', ['id', 'name', 'code'])
|
||||
ICU_COLLATOR = None
|
||||
DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+')
|
||||
COLLATOR = None
|
||||
LANGUAGES = sorted([
|
||||
Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),
|
||||
Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),
|
||||
|
@ -507,24 +505,19 @@ def format_time(text, local_time):
|
|||
return re.sub(r'\%[a-zA-Z]', match_formatting, text)
|
||||
|
||||
|
||||
def get_locale_key(string):
|
||||
def get_locale_key(string, numeric=False):
|
||||
"""
|
||||
Creates a key for case insensitive, locale aware string sorting.
|
||||
|
||||
:param string: The corresponding string.
|
||||
"""
|
||||
string = string.lower()
|
||||
# ICU is the prefered way to handle locale sort key, we fallback to locale.strxfrm which will work in most cases.
|
||||
global ICU_COLLATOR
|
||||
try:
|
||||
if ICU_COLLATOR is None:
|
||||
import icu
|
||||
language = LanguageManager.get_language()
|
||||
icu_locale = icu.Locale(language)
|
||||
ICU_COLLATOR = icu.Collator.createInstance(icu_locale)
|
||||
return ICU_COLLATOR.getSortKey(string)
|
||||
except:
|
||||
return locale.strxfrm(string).encode()
|
||||
global COLLATOR
|
||||
if COLLATOR is None:
|
||||
language = LanguageManager.get_language()
|
||||
COLLATOR = QtCore.QCollator(QtCore.QLocale(language))
|
||||
COLLATOR.setNumericMode(numeric)
|
||||
return COLLATOR.sortKey(string)
|
||||
|
||||
|
||||
def get_natural_key(string):
|
||||
|
@ -534,13 +527,7 @@ def get_natural_key(string):
|
|||
:param string: string to be sorted by
|
||||
Returns a list of string compare keys and integers.
|
||||
"""
|
||||
key = DIGITS_OR_NONDIGITS.findall(string)
|
||||
key = [int(part) if part.isdigit() else get_locale_key(part) for part in key]
|
||||
# Python 3 does not support comparison of different types anymore. So make sure, that we do not compare str
|
||||
# and int.
|
||||
if string and string[0].isdigit():
|
||||
return [b''] + key
|
||||
return key
|
||||
return get_locale_key(string, True)
|
||||
|
||||
|
||||
def get_language(name):
|
||||
|
|
|
@ -1,769 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2017 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`canvas` module provides the functionality to display screens and play multimedia within OpenLP.
|
||||
|
||||
Some of the code for this form is based on the examples at:
|
||||
|
||||
* `http://www.steveheffernan.com/html5-video-player/demo-video-player.html`_
|
||||
* `http://html5demos.com/two-videos`_
|
||||
"""
|
||||
import html
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtMultimedia, QtWebChannel, QtWebEngineWidgets, QtWidgets
|
||||
|
||||
from openlp.core.common import is_macosx, is_win
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.path import path_to_str
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.display.webengine import WebEngineView
|
||||
from openlp.core.display.window import MediaWatcher
|
||||
from openlp.core.lib import ImageSource, ServiceItem, build_html, expand_tags, image_to_byte
|
||||
from openlp.core.lib.theme import BackgroundType
|
||||
from openlp.core.ui import AlertLocation, DisplayControllerType, HideMode
|
||||
|
||||
|
||||
if is_macosx():
|
||||
from ctypes import pythonapi, c_void_p, c_char_p, py_object
|
||||
|
||||
from sip import voidptr
|
||||
from objc import objc_object
|
||||
from AppKit import NSMainMenuWindowLevel, NSWindowCollectionBehaviorManaged
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
OPAQUE_STYLESHEET = """
|
||||
QWidget {
|
||||
border: 0px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
QGraphicsView {}
|
||||
"""
|
||||
TRANSPARENT_STYLESHEET = """
|
||||
QWidget {
|
||||
border: 0px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
QGraphicsView {
|
||||
background: transparent;
|
||||
border: 0px;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class Canvas(QtWidgets.QGraphicsView):
|
||||
"""
|
||||
This is a general display screen class. Here the general display settings will done. It will be used as
|
||||
specialized classes by Main Display and Preview display.
|
||||
"""
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
self.is_live = False
|
||||
if hasattr(parent, 'is_live') and parent.is_live:
|
||||
self.is_live = True
|
||||
if self.is_live:
|
||||
self.parent = lambda: parent
|
||||
super(Canvas, self).__init__()
|
||||
self.controller = parent
|
||||
self.screen = {}
|
||||
|
||||
def setup(self):
|
||||
"""
|
||||
Set up and build the screen base
|
||||
"""
|
||||
self.setGeometry(self.screen['size'])
|
||||
#self.web_view = QtWebKitWidgets.QWebView(self)
|
||||
#self.web_view.setGeometry(0, 0, self.screen['size'].width(), self.screen['size'].height())
|
||||
#self.web_view.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)
|
||||
#palette = self.web_view.palette()
|
||||
#palette.setBrush(QtGui.QPalette.Base, QtCore.Qt.transparent)
|
||||
#self.web_view.page().setPalette(palette)
|
||||
#self.web_view.setAttribute(QtCore.Qt.WA_OpaquePaintEvent, False)
|
||||
#self.page = self.web_view.page()
|
||||
#self.frame = self.page.mainFrame()
|
||||
#if self.is_live and log.getEffectiveLevel() == logging.DEBUG:
|
||||
# self.web_view.settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
|
||||
|
||||
|
||||
self.webview = WebEngineView(self)
|
||||
self.webview.setGeometry(0, 0, self.screen['size'].width(), self.screen['size'].height())
|
||||
self.webview.settings().setAttribute(QtWebEngineWidgets.QWebEngineSettings.PluginsEnabled, True)
|
||||
self.layout = QtWidgets.QVBoxLayout(self)
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.layout.addWidget(self.webview)
|
||||
self.webview.loadFinished.connect(self.after_loaded)
|
||||
self.set_url(QtCore.QUrl('file://' + os.getcwd() + '/display.html'))
|
||||
self.media_watcher = MediaWatcher(self)
|
||||
self.channel = QtWebChannel.QWebChannel(self)
|
||||
self.channel.registerObject('mediaWatcher', self.media_watcher)
|
||||
self.webview.page().setWebChannel(self.channel)
|
||||
self.webview.loadFinished.connect(self.is_web_loaded)
|
||||
|
||||
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
"""
|
||||
React to resizing of this display
|
||||
|
||||
:param event: The event to be handled
|
||||
"""
|
||||
if hasattr(self, 'web_view'):
|
||||
self.web_view.setGeometry(0, 0, self.width(), self.height())
|
||||
|
||||
def is_web_loaded(self, field=None):
|
||||
"""
|
||||
Called by webView event to show display is fully loaded
|
||||
"""
|
||||
self.web_loaded = True
|
||||
|
||||
def set_url(self, url):
|
||||
"""
|
||||
Set the URL of the webview
|
||||
"""
|
||||
if not isinstance(url, QtCore.QUrl):
|
||||
url = QtCore.QUrl(url)
|
||||
self.webview.setUrl(url)
|
||||
|
||||
def set_html(self, html):
|
||||
"""
|
||||
Set the html
|
||||
"""
|
||||
self.webview.setHtml(html)
|
||||
|
||||
def after_loaded(self, state):
|
||||
"""
|
||||
Add stuff after page initialisation
|
||||
"""
|
||||
self.run_javascript('Display.init();')
|
||||
|
||||
def add_script_source(self, fname, source):
|
||||
"""
|
||||
Add a script of source code
|
||||
"""
|
||||
js = QtWebEngineWidgets.QWebEngineScript()
|
||||
js.setSourceCode(source)
|
||||
js.setName(fname)
|
||||
js.setWorldId(QtWebEngineWidgets.QWebEngineScript.MainWorld)
|
||||
self.webview.page().scripts().insert(js)
|
||||
|
||||
def add_script(self, fname):
|
||||
"""
|
||||
Add a script to the page
|
||||
"""
|
||||
js_file = QtCore.QFile(fname)
|
||||
if not js_file.open(QtCore.QIODevice.ReadOnly):
|
||||
log.warning('Could not open %s: %s', fname, js_file.errorString())
|
||||
return
|
||||
self.add_script_source(os.path.basename(fname), str(bytes(js_file.readAll()), 'utf-8'))
|
||||
|
||||
def run_javascript(self, script, is_sync=False):
|
||||
"""
|
||||
Run some Javascript in the WebView
|
||||
|
||||
:param script: The script to run, a string
|
||||
:param is_sync: Run the script synchronously. Defaults to False
|
||||
"""
|
||||
if not is_sync:
|
||||
self.webview.page().runJavaScript(script)
|
||||
else:
|
||||
self.__script_done = False
|
||||
self.__script_result = None
|
||||
|
||||
def handle_result(result):
|
||||
"""
|
||||
Handle the result from the asynchronous call
|
||||
"""
|
||||
self.__script_done = True
|
||||
self.__script_result = result
|
||||
|
||||
self.webview.page().runJavaScript(script, handle_result)
|
||||
while not self.__script_done:
|
||||
# TODO: Figure out how to break out of a potentially infinite loop
|
||||
QtWidgets.QApplication.instance().processEvents()
|
||||
return self.__script_result
|
||||
|
||||
|
||||
class MainCanvas(OpenLPMixin, Canvas, RegistryProperties):
|
||||
"""
|
||||
This is the display screen as a specialized class from the Display class
|
||||
"""
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
super(MainCanvas, self).__init__(parent)
|
||||
self.screens = ScreenList()
|
||||
self.rebuild_css = False
|
||||
self.hide_mode = None
|
||||
self.override = {}
|
||||
self.retranslate_ui()
|
||||
self.media_object = None
|
||||
if self.is_live:
|
||||
self.audio_player = AudioPlayer(self)
|
||||
else:
|
||||
self.audio_player = None
|
||||
self.first_time = True
|
||||
self.web_loaded = True
|
||||
self.setStyleSheet(OPAQUE_STYLESHEET)
|
||||
window_flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint
|
||||
if Settings().value('advanced/x11 bypass wm'):
|
||||
window_flags |= QtCore.Qt.X11BypassWindowManagerHint
|
||||
# TODO: The following combination of window_flags works correctly
|
||||
# on Mac OS X. For next OpenLP version we should test it on other
|
||||
# platforms. For OpenLP 2.0 keep it only for OS X to not cause any
|
||||
# regressions on other platforms.
|
||||
if is_macosx():
|
||||
window_flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Window | QtCore.Qt.NoDropShadowWindowHint
|
||||
self.setWindowFlags(window_flags)
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
self.set_transparency(False)
|
||||
if is_macosx():
|
||||
if self.is_live:
|
||||
# Get a pointer to the underlying NSView
|
||||
try:
|
||||
nsview_pointer = self.winId().ascapsule()
|
||||
except:
|
||||
nsview_pointer = voidptr(self.winId()).ascapsule()
|
||||
# Set PyCapsule name so pyobjc will accept it
|
||||
pythonapi.PyCapsule_SetName.restype = c_void_p
|
||||
pythonapi.PyCapsule_SetName.argtypes = [py_object, c_char_p]
|
||||
pythonapi.PyCapsule_SetName(nsview_pointer, c_char_p(b"objc.__object__"))
|
||||
# Covert the NSView pointer into a pyobjc NSView object
|
||||
self.pyobjc_nsview = objc_object(cobject=nsview_pointer)
|
||||
# Set the window level so that the MainCanvas is above the menu bar and dock
|
||||
self.pyobjc_nsview.window().setLevel_(NSMainMenuWindowLevel + 2)
|
||||
# Set the collection behavior so the window is visible when Mission Control is activated
|
||||
self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged)
|
||||
if self.screens.current['primary']:
|
||||
# Connect focusWindowChanged signal so we can change the window level when the display is not in
|
||||
# focus on the primary screen
|
||||
self.application.focusWindowChanged.connect(self.change_window_level)
|
||||
if self.is_live:
|
||||
Registry().register_function('live_display_hide', self.hide_display)
|
||||
Registry().register_function('live_display_show', self.show_display)
|
||||
Registry().register_function('update_display_css', self.css_changed)
|
||||
self.close_display = False
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""
|
||||
Catch the close event, and check that the close event is triggered by OpenLP closing the display.
|
||||
On Windows this event can be triggered by pressing ALT+F4, which we want to ignore.
|
||||
|
||||
:param event: The triggered event
|
||||
"""
|
||||
if self.close_display:
|
||||
super().closeEvent(event)
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Remove registered function on close.
|
||||
"""
|
||||
if self.is_live:
|
||||
if is_macosx():
|
||||
# Block signals so signal we are disconnecting can't get called while we disconnect it
|
||||
self.blockSignals(True)
|
||||
if self.screens.current['primary']:
|
||||
self.application.focusWindowChanged.disconnect()
|
||||
self.blockSignals(False)
|
||||
Registry().remove_function('live_display_hide', self.hide_display)
|
||||
Registry().remove_function('live_display_show', self.show_display)
|
||||
Registry().remove_function('update_display_css', self.css_changed)
|
||||
self.close_display = True
|
||||
super().close()
|
||||
|
||||
def set_transparency(self, enabled):
|
||||
"""
|
||||
Set the transparency of the window
|
||||
|
||||
:param enabled: Is transparency enabled
|
||||
"""
|
||||
if enabled:
|
||||
self.setAutoFillBackground(False)
|
||||
self.setStyleSheet(TRANSPARENT_STYLESHEET)
|
||||
else:
|
||||
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False)
|
||||
self.setStyleSheet(OPAQUE_STYLESHEET)
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled)
|
||||
self.repaint()
|
||||
|
||||
def css_changed(self):
|
||||
"""
|
||||
We need to rebuild the CSS on the live display.
|
||||
"""
|
||||
for plugin in self.plugin_manager.plugins:
|
||||
plugin.refresh_css(self.frame)
|
||||
|
||||
def retranslate_ui(self):
|
||||
"""
|
||||
Setup the interface translation strings.
|
||||
"""
|
||||
self.setWindowTitle(translate('OpenLP.MainCanvas', 'OpenLP Display'))
|
||||
|
||||
def setup(self):
|
||||
"""
|
||||
Set up and build the output screen
|
||||
"""
|
||||
self.log_debug('Start MainCanvas setup (live = {islive})'.format(islive=self.is_live))
|
||||
self.screen = self.screens.current
|
||||
self.setVisible(False)
|
||||
Canvas.setup(self)
|
||||
if self.is_live:
|
||||
# Build the initial frame.
|
||||
background_color = QtGui.QColor()
|
||||
background_color.setNamedColor(Settings().value('core/logo background color'))
|
||||
if not background_color.isValid():
|
||||
background_color = QtCore.Qt.white
|
||||
image_file = path_to_str(Settings().value('core/logo file'))
|
||||
splash_image = QtGui.QImage(image_file)
|
||||
self.initial_fame = QtGui.QImage(
|
||||
self.screen['size'].width(),
|
||||
self.screen['size'].height(),
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
painter_image = QtGui.QPainter()
|
||||
painter_image.begin(self.initial_fame)
|
||||
painter_image.fillRect(self.initial_fame.rect(), background_color)
|
||||
painter_image.drawImage(
|
||||
(self.screen['size'].width() - splash_image.width()) // 2,
|
||||
(self.screen['size'].height() - splash_image.height()) // 2,
|
||||
splash_image)
|
||||
service_item = ServiceItem()
|
||||
service_item.bg_image_bytes = image_to_byte(self.initial_fame)
|
||||
self.webview.setHtml(build_html(service_item, self.screen, self.is_live, None,
|
||||
plugins=self.plugin_manager.plugins))
|
||||
self._hide_mouse()
|
||||
|
||||
def text(self, slide, animate=True):
|
||||
"""
|
||||
Add the slide text from slideController
|
||||
|
||||
:param slide: The slide text to be displayed
|
||||
:param animate: Perform transitions if applicable when setting the text
|
||||
"""
|
||||
# Wait for the webview to update before displaying text.
|
||||
while not self.web_loaded:
|
||||
self.application.process_events()
|
||||
self.setGeometry(self.screen['size'])
|
||||
json_verses = json.dumps(slide)
|
||||
self.run_javascript('Display.setTextSlides({verses});'.format(verses=json_verses))
|
||||
#if animate:
|
||||
# # NOTE: Verify this works with ''.format()
|
||||
# _text = slide.replace('\\', '\\\\').replace('\"', '\\\"')
|
||||
# self.frame.runJavaScript('show_text("{text}")'.format(text=_text))
|
||||
#else:
|
||||
# # This exists for https://bugs.launchpad.net/openlp/+bug/1016843
|
||||
# # For unknown reasons if evaluateJavaScript is called
|
||||
# # from the themewizard, then it causes a crash on
|
||||
# # Windows if there are many items in the service to re-render.
|
||||
# # Setting the div elements direct seems to solve the issue
|
||||
# self.frame.findFirstElement("#lyricsmain").setInnerXml(slide)
|
||||
|
||||
def alert(self, text, location):
|
||||
"""
|
||||
Display an alert.
|
||||
|
||||
:param text: The text to be displayed.
|
||||
:param location: Where on the screen is the text to be displayed
|
||||
"""
|
||||
# First we convert <>& marks to html variants, then apply
|
||||
# formattingtags, finally we double all backslashes for JavaScript.
|
||||
text_prepared = expand_tags(html.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"')
|
||||
if self.height() != self.screen['size'].height() or not self.isVisible():
|
||||
shrink = True
|
||||
js = 'show_alert("{text}", "{top}")'.format(text=text_prepared, top='top')
|
||||
else:
|
||||
shrink = False
|
||||
js = 'show_alert("{text}", "")'.format(text=text_prepared)
|
||||
height = self.run_javascript(js)
|
||||
if shrink:
|
||||
if text:
|
||||
alert_height = int(height)
|
||||
self.resize(self.width(), alert_height)
|
||||
self.setVisible(True)
|
||||
if location == AlertLocation.Middle:
|
||||
self.move(self.screen['size'].left(), (self.screen['size'].height() - alert_height) // 2)
|
||||
elif location == AlertLocation.Bottom:
|
||||
self.move(self.screen['size'].left(), self.screen['size'].height() - alert_height)
|
||||
else:
|
||||
self.setVisible(False)
|
||||
self.setGeometry(self.screen['size'])
|
||||
|
||||
def direct_image(self, path, background):
|
||||
"""
|
||||
API for replacement backgrounds so Images are added directly to cache.
|
||||
|
||||
:param path: Path to Image
|
||||
:param background: The background color
|
||||
"""
|
||||
self.image_manager.add_image(path, ImageSource.ImagePlugin, background)
|
||||
if not hasattr(self, 'service_item'):
|
||||
return False
|
||||
self.override['image'] = path
|
||||
self.override['theme'] = path_to_str(self.service_item.theme_data.background_filename)
|
||||
self.image(path)
|
||||
# Update the preview frame.
|
||||
if self.is_live:
|
||||
self.live_controller.update_preview()
|
||||
return True
|
||||
|
||||
def image(self, path):
|
||||
"""
|
||||
Add an image as the background. The image has already been added to the
|
||||
cache.
|
||||
|
||||
:param path: The path to the image to be displayed. **Note**, the path is only passed to identify the image.
|
||||
If the image has changed it has to be re-added to the image manager.
|
||||
"""
|
||||
image = self.image_manager.get_image_bytes(path, ImageSource.ImagePlugin)
|
||||
self.controller.media_controller.media_reset(self.controller)
|
||||
self.display_image(image)
|
||||
|
||||
def display_image(self, image):
|
||||
"""
|
||||
Display an image, as is.
|
||||
|
||||
:param image: The image to be displayed
|
||||
"""
|
||||
self.setGeometry(self.screen['size'])
|
||||
#if image:
|
||||
# self.set_im
|
||||
# js = 'show_image("data:image/png;base64,{image}");'.format(image=image)
|
||||
#else:
|
||||
# js = 'show_image("");'
|
||||
#self.frame.evaluateJavaScript(js)
|
||||
if not image['file'].startswith('file://'):
|
||||
image['file'] = 'file://' + image['file']
|
||||
json_images = json.dumps(images)
|
||||
self.run_javascript('Display.setImageSlides({images});'.format(images=json_images))
|
||||
|
||||
|
||||
def reset_image(self):
|
||||
"""
|
||||
Reset the background image to the service item image. Used after the image plugin has changed the background.
|
||||
"""
|
||||
if hasattr(self, 'service_item'):
|
||||
self.display_image(self.service_item.bg_image_bytes)
|
||||
else:
|
||||
self.display_image(None)
|
||||
# Update the preview frame.
|
||||
if self.is_live:
|
||||
self.live_controller.update_preview()
|
||||
# clear the cache
|
||||
self.override = {}
|
||||
|
||||
def preview(self):
|
||||
"""
|
||||
Generates a preview of the image displayed.
|
||||
"""
|
||||
was_visible = self.isVisible()
|
||||
self.application.process_events()
|
||||
# We must have a service item to preview.
|
||||
if self.is_live and hasattr(self, 'service_item'):
|
||||
# Wait for the fade to finish before geting the preview.
|
||||
# Important otherwise preview will have incorrect text if at all!
|
||||
if self.service_item.theme_data and self.service_item.theme_data.display_slide_transition:
|
||||
while not self.run_javascript('show_text_completed()'):
|
||||
self.application.process_events()
|
||||
# Wait for the webview to update before getting the preview.
|
||||
# Important otherwise first preview will miss the background !
|
||||
while not self.web_loaded:
|
||||
self.application.process_events()
|
||||
# if was hidden keep it hidden
|
||||
if self.is_live:
|
||||
if self.hide_mode:
|
||||
self.hide_display(self.hide_mode)
|
||||
# Only continue if the visibility wasn't changed during method call.
|
||||
elif was_visible == self.isVisible():
|
||||
# Single screen active
|
||||
if self.screens.display_count == 1:
|
||||
# Only make visible if setting enabled.
|
||||
if Settings().value('core/display on monitor'):
|
||||
self.setVisible(True)
|
||||
else:
|
||||
self.setVisible(True)
|
||||
return self.grab()
|
||||
|
||||
def build_html(self, service_item, image_path=''):
|
||||
"""
|
||||
Store the service_item and build the new HTML from it. Add the HTML to the display
|
||||
|
||||
:param service_item: The Service item to be used
|
||||
:param image_path: Where the image resides.
|
||||
"""
|
||||
self.web_loaded = False
|
||||
self.initial_fame = None
|
||||
self.service_item = service_item
|
||||
background = None
|
||||
# We have an image override so keep the image till the theme changes.
|
||||
if self.override:
|
||||
# We have an video override so allow it to be stopped.
|
||||
if 'video' in self.override:
|
||||
Registry().execute('video_background_replaced')
|
||||
self.override = {}
|
||||
# We have a different theme.
|
||||
elif self.override['theme'] != path_to_str(service_item.theme_data.background_filename):
|
||||
Registry().execute('live_theme_changed')
|
||||
self.override = {}
|
||||
else:
|
||||
# replace the background
|
||||
background = self.image_manager.get_image_bytes(self.override['image'], ImageSource.ImagePlugin)
|
||||
self.set_transparency(self.service_item.theme_data.background_type ==
|
||||
BackgroundType.to_string(BackgroundType.Transparent))
|
||||
image_bytes = None
|
||||
if self.service_item.theme_data.background_type == 'image':
|
||||
if self.service_item.theme_data.background_filename:
|
||||
self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
|
||||
path_to_str(self.service_item.theme_data.background_filename), ImageSource.Theme)
|
||||
if image_path:
|
||||
image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
|
||||
created_html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
|
||||
plugins=self.plugin_manager.plugins)
|
||||
self.webview.setHtml(created_html)
|
||||
if service_item.foot_text:
|
||||
self.footer(service_item.foot_text)
|
||||
# if was hidden keep it hidden
|
||||
if self.hide_mode and self.is_live and not service_item.is_media():
|
||||
if Settings().value('core/auto unblank'):
|
||||
Registry().execute('slidecontroller_live_unblank')
|
||||
else:
|
||||
self.hide_display(self.hide_mode)
|
||||
if self.service_item.theme_data.background_type == 'video' and self.is_live:
|
||||
if self.service_item.theme_data.background_filename:
|
||||
service_item = ServiceItem()
|
||||
service_item.title = 'webkit'
|
||||
service_item.processor = 'webkit'
|
||||
path = os.path.join(str(AppLocation.get_section_data_path('themes')),
|
||||
self.service_item.theme_data.theme_name)
|
||||
service_item.add_from_command(path,
|
||||
path_to_str(self.service_item.theme_data.background_filename),
|
||||
':/media/slidecontroller_multimedia.png')
|
||||
self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True)
|
||||
self._hide_mouse()
|
||||
|
||||
def footer(self, text):
|
||||
"""
|
||||
Display the Footer
|
||||
|
||||
:param text: footer text to be displayed
|
||||
"""
|
||||
js = 'show_footer(\'' + text.replace('\\', '\\\\').replace('\'', '\\\'') + '\')'
|
||||
self.run_javascript(js)
|
||||
|
||||
def hide_display(self, mode=HideMode.Screen):
|
||||
"""
|
||||
Hide the display by making all layers transparent Store the images so they can be replaced when required
|
||||
|
||||
:param mode: How the screen is to be hidden
|
||||
"""
|
||||
self.log_debug('hide_display mode = {mode:d}'.format(mode=mode))
|
||||
if self.screens.display_count == 1:
|
||||
# Only make visible if setting enabled.
|
||||
if not Settings().value('core/display on monitor'):
|
||||
return
|
||||
if mode == HideMode.Screen:
|
||||
self.run_javascript('show_blank("desktop");')
|
||||
self.setVisible(False)
|
||||
elif mode == HideMode.Blank or self.initial_fame:
|
||||
self.run_javascript('show_blank("black");')
|
||||
else:
|
||||
self.run_javascript('show_blank("theme");')
|
||||
if mode != HideMode.Screen:
|
||||
if self.isHidden():
|
||||
self.setVisible(True)
|
||||
self.webview.setVisible(True)
|
||||
self.hide_mode = mode
|
||||
|
||||
def show_display(self):
|
||||
"""
|
||||
Show the stored layers so the screen reappears as it was originally.
|
||||
Make the stored images None to release memory.
|
||||
"""
|
||||
if self.screens.display_count == 1:
|
||||
# Only make visible if setting enabled.
|
||||
if not Settings().value('core/display on monitor'):
|
||||
return
|
||||
self.run_javascript('show_blank("show");')
|
||||
# Check if setting for hiding logo on startup is enabled.
|
||||
# If it is, display should remain hidden, otherwise logo is shown. (from def setup)
|
||||
if self.isHidden() and not Settings().value('core/logo hide on startup'):
|
||||
self.setVisible(True)
|
||||
self.hide_mode = None
|
||||
# Trigger actions when display is active again.
|
||||
if self.is_live:
|
||||
Registry().execute('live_display_active')
|
||||
|
||||
def _hide_mouse(self):
|
||||
"""
|
||||
Hide mouse cursor when moved over display.
|
||||
"""
|
||||
if Settings().value('advanced/hide mouse'):
|
||||
self.setCursor(QtCore.Qt.BlankCursor)
|
||||
self.run_javascript('document.body.style.cursor = "none"')
|
||||
else:
|
||||
self.setCursor(QtCore.Qt.ArrowCursor)
|
||||
self.run_javascript('document.body.style.cursor = "auto"')
|
||||
|
||||
def change_window_level(self, window):
|
||||
"""
|
||||
Changes the display window level on Mac OS X so that the main window can be brought into focus but still allow
|
||||
the main display to be above the menu bar and dock when it in focus.
|
||||
|
||||
:param window: Window from our application that focus changed to or None if outside our application
|
||||
"""
|
||||
if is_macosx():
|
||||
if window:
|
||||
# Get different window ids' as int's
|
||||
try:
|
||||
window_id = window.winId().__int__()
|
||||
main_window_id = self.main_window.winId().__int__()
|
||||
self_id = self.winId().__int__()
|
||||
except:
|
||||
return
|
||||
# If the passed window has the same id as our window make sure the display has the proper level and
|
||||
# collection behavior.
|
||||
if window_id == self_id:
|
||||
self.pyobjc_nsview.window().setLevel_(NSMainMenuWindowLevel + 2)
|
||||
self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged)
|
||||
# Else set the displays window level back to normal since we are trying to focus a window other than
|
||||
# the display.
|
||||
else:
|
||||
self.pyobjc_nsview.window().setLevel_(0)
|
||||
self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged)
|
||||
# If we are trying to focus the main window raise it now to complete the focus change.
|
||||
if window_id == main_window_id:
|
||||
self.main_window.raise_()
|
||||
|
||||
|
||||
class AudioPlayer(LogMixin, QtCore.QObject):
|
||||
"""
|
||||
This Class will play audio only allowing components to work with a soundtrack independent of the user interface.
|
||||
"""
|
||||
position_changed = QtCore.pyqtSignal(int)
|
||||
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
The constructor for the display form.
|
||||
|
||||
:param parent: The parent widget.
|
||||
"""
|
||||
super(AudioPlayer, self).__init__(parent)
|
||||
self.player = QtMultimedia.QMediaPlayer()
|
||||
self.playlist = QtMultimedia.QMediaPlaylist(self.player)
|
||||
self.volume_slider = None
|
||||
self.player.setPlaylist(self.playlist)
|
||||
self.player.positionChanged.connect(self._on_position_changed)
|
||||
|
||||
def __del__(self):
|
||||
"""
|
||||
Shutting down so clean up connections
|
||||
"""
|
||||
self.stop()
|
||||
|
||||
def _on_position_changed(self, position):
|
||||
"""
|
||||
Emit a signal when the position of the media player updates
|
||||
"""
|
||||
self.position_changed.emit(position)
|
||||
|
||||
def set_volume_slider(self, slider):
|
||||
"""
|
||||
Connect the volume slider to the media player
|
||||
:param slider:
|
||||
"""
|
||||
self.volume_slider = slider
|
||||
self.volume_slider.setMinimum(0)
|
||||
self.volume_slider.setMaximum(100)
|
||||
self.volume_slider.setValue(self.player.volume())
|
||||
self.volume_slider.valueChanged.connect(self.set_volume)
|
||||
|
||||
def set_volume(self, volume):
|
||||
"""
|
||||
Set the volume of the media player
|
||||
|
||||
:param volume:
|
||||
"""
|
||||
self.player.setVolume(volume)
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Reset the audio player, clearing the playlist and the queue.
|
||||
"""
|
||||
self.stop()
|
||||
self.playlist.clear()
|
||||
|
||||
def play(self):
|
||||
"""
|
||||
We want to play the file so start it
|
||||
"""
|
||||
self.player.play()
|
||||
|
||||
def pause(self):
|
||||
"""
|
||||
Pause the Audio
|
||||
"""
|
||||
self.player.pause()
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the Audio and clean up
|
||||
"""
|
||||
self.player.stop()
|
||||
|
||||
def add_to_playlist(self, file_names):
|
||||
"""
|
||||
Add another file to the playlist.
|
||||
|
||||
:param file_names: A list with files to be added to the playlist.
|
||||
"""
|
||||
if not isinstance(file_names, list):
|
||||
file_names = [file_names]
|
||||
for file_name in file_names:
|
||||
self.playlist.addMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(str(file_name))))
|
||||
|
||||
def next(self):
|
||||
"""
|
||||
Skip forward to the next track in the list
|
||||
"""
|
||||
self.playlist.next()
|
||||
|
||||
def go_to(self, index):
|
||||
"""
|
||||
Go to a particular track in the list
|
||||
|
||||
:param index: The track to go to
|
||||
"""
|
||||
self.playlist.setCurrentIndex(index)
|
||||
if self.player.state() == QtMultimedia.QMediaPlayer.PlayingState:
|
||||
self.player.play()
|
|
@ -321,6 +321,66 @@ var Display = {
|
|||
Display._slides['0'] = 0;
|
||||
Display.reinit();
|
||||
},
|
||||
/**
|
||||
* Set fullscreen image from path
|
||||
* @param {string} bg_color - The background color
|
||||
* @param {string} image - Path to the image
|
||||
*/
|
||||
setFullscreenImage: function(bg_color, image) {
|
||||
Display.clearSlides();
|
||||
var globalBackground = $("#global-background")[0];
|
||||
globalBackground.style.cssText = "";
|
||||
globalBackground.style.setProperty("background", bg_color);
|
||||
var slidesDiv = $(".slides")[0];
|
||||
var section = document.createElement("section");
|
||||
section.setAttribute("id", 0);
|
||||
section.setAttribute("data-background", bg_color);
|
||||
section.setAttribute("style", "height: 100%; width: 100%;");
|
||||
var img = document.createElement('img');
|
||||
img.src = image;
|
||||
img.setAttribute("style", "height: 100%; width: 100%");
|
||||
section.appendChild(img);
|
||||
slidesDiv.appendChild(section);
|
||||
Display._slides['0'] = 0;
|
||||
Display.reinit();
|
||||
},
|
||||
/**
|
||||
* Set fullscreen image from base64 data
|
||||
* @param {string} bg_color - The background color
|
||||
* @param {string} image - Path to the image
|
||||
*/
|
||||
setFullscreenImageFromData: function(bg_color, image_data) {
|
||||
Display.clearSlides();
|
||||
var globalBackground = $("#global-background")[0];
|
||||
globalBackground.style.cssText = "";
|
||||
globalBackground.style.setProperty("background", bg_color);
|
||||
var slidesDiv = $(".slides")[0];
|
||||
var section = document.createElement("section");
|
||||
section.setAttribute("id", 0);
|
||||
section.setAttribute("data-background", bg_color);
|
||||
section.setAttribute("style", "height: 100%; width: 100%;");
|
||||
var img = document.createElement('img');
|
||||
img.src = 'data:image/png;base64,' + image_data;
|
||||
img.setAttribute("style", "height: 100%; width: 100%");
|
||||
section.appendChild(img);
|
||||
slidesDiv.appendChild(section);
|
||||
Display._slides['0'] = 0;
|
||||
Display.reinit();
|
||||
},
|
||||
/**
|
||||
* Display an alert
|
||||
* @param {string} text - The alert text
|
||||
* @param {int} location - The location of the text (top, middle or bottom)
|
||||
*/
|
||||
alert: function (text, location) {
|
||||
console.debug(" alert text: " + text, ", location: " + location);
|
||||
/*
|
||||
* The implementation should show an alert.
|
||||
* It should be able to handle receiving a new alert before a previous one is "finished", basically queueing it.
|
||||
*/
|
||||
return;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a slides. If the slide exists but the HTML is different, update the slide.
|
||||
* @param {string} verse - The verse number, e.g. "v1"
|
||||
|
@ -518,7 +578,7 @@ var Display = {
|
|||
/**
|
||||
* Blank the screen
|
||||
*/
|
||||
blank: function () {
|
||||
blankToBlack: function () {
|
||||
if (!Reveal.isPaused()) {
|
||||
Reveal.togglePause();
|
||||
}
|
||||
|
@ -527,7 +587,7 @@ var Display = {
|
|||
/**
|
||||
* Blank to theme
|
||||
*/
|
||||
theme: function () {
|
||||
blankToTheme: function () {
|
||||
var slidesDiv = $(".slides")[0];
|
||||
slidesDiv.style.visibility = "hidden";
|
||||
if (Reveal.isPaused()) {
|
||||
|
|
|
@ -28,7 +28,7 @@ import math
|
|||
import re
|
||||
import time
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5 import QtWidgets, QtGui
|
||||
|
||||
from openlp.core.lib.formattingtags import FormattingTags
|
||||
from openlp.core.common.registry import Registry, RegistryBase
|
||||
|
@ -469,24 +469,13 @@ class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow):
|
|||
self.set_theme(theme_data)
|
||||
self.theme_height = theme_data.font_main_height
|
||||
slides = self.format_slide(render_tags(VERSE), None)
|
||||
print(slides)
|
||||
verses = dict()
|
||||
verses['title'] = TITLE
|
||||
verses['text'] = slides[0]
|
||||
verses['verse'] = 'V1'
|
||||
self.load_verses([verses])
|
||||
self.force_page = False
|
||||
QtWidgets.QApplication.instance().processEvents()
|
||||
pixmap = self.webview.grab()
|
||||
QtWidgets.QApplication.instance().processEvents()
|
||||
pixmap = self.webview.grab()
|
||||
time.sleep(0.5)
|
||||
self.show()
|
||||
QtWidgets.QApplication.instance().processEvents()
|
||||
pixmap = self.grab()
|
||||
self.hide()
|
||||
#pixmap.save('/tmp/screen-grab.png', 'png')
|
||||
return pixmap
|
||||
return self.save_screenshot()
|
||||
self.force_page = False
|
||||
return None
|
||||
|
||||
|
@ -724,3 +713,23 @@ class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow):
|
|||
self.run_javascript('Display.addTextSlide("v1", "{text}");'.format(text=text), is_sync=True)
|
||||
does_text_fits = self.run_javascript('Display.doesContentFit();', is_sync=True)
|
||||
return does_text_fits
|
||||
|
||||
def save_screenshot(self, fname=None):
|
||||
"""
|
||||
Save a screenshot, either returning it or saving it to file. Do some extra work to actually get a picture.
|
||||
"""
|
||||
self.setVisible(True)
|
||||
pixmap = self.grab()
|
||||
for i in range(0, 4):
|
||||
QtWidgets.QApplication.instance().processEvents()
|
||||
time.sleep(0.05)
|
||||
QtWidgets.QApplication.instance().processEvents()
|
||||
pixmap = self.grab()
|
||||
self.setVisible(False)
|
||||
pixmap = QtGui.QPixmap(self.webview.size())
|
||||
self.webview.render(pixmap)
|
||||
if fname:
|
||||
ext = os.path.splitext(fname)[-1][1:]
|
||||
pixmap.save(fname, ext)
|
||||
else:
|
||||
return pixmap
|
||||
|
|
|
@ -91,10 +91,10 @@ class Screen(object):
|
|||
screen_dict['geometry'] = QtCore.QRect(screen_dict['geometry']['x'], screen_dict['geometry']['y'],
|
||||
screen_dict['geometry']['width'], screen_dict['geometry']['height'])
|
||||
if 'custom_geometry' in screen_dict:
|
||||
screen_dict['display_geometry'] = QtCore.QRect(screen_dict['display_geometry']['x'],
|
||||
screen_dict['display_geometry']['y'],
|
||||
screen_dict['display_geometry']['width'],
|
||||
screen_dict['display_geometry']['height'])
|
||||
screen_dict['custom_geometry'] = QtCore.QRect(screen_dict['custom_geometry']['x'],
|
||||
screen_dict['custom_geometry']['y'],
|
||||
screen_dict['custom_geometry']['width'],
|
||||
screen_dict['custom_geometry']['height'])
|
||||
return cls(**screen_dict)
|
||||
|
||||
def to_dict(self):
|
||||
|
@ -115,12 +115,12 @@ class Screen(object):
|
|||
'is_primary': self.is_primary,
|
||||
'is_display': self.is_display
|
||||
}
|
||||
if self.display_geometry != self.geometry:
|
||||
screen_dict['display_geometry'] = {
|
||||
'x': self.display_geometry.x(),
|
||||
'y': self.display_geometry.y(),
|
||||
'width': self.display_geometry.width(),
|
||||
'height': self.display_geometry.height()
|
||||
if self.custom_geometry is not None:
|
||||
screen_dict['custom_geometry'] = {
|
||||
'x': self.custom_geometry.x(),
|
||||
'y': self.custom_geometry.y(),
|
||||
'width': self.custom_geometry.width(),
|
||||
'height': self.custom_geometry.height()
|
||||
}
|
||||
return screen_dict
|
||||
|
||||
|
@ -135,11 +135,11 @@ class Screen(object):
|
|||
self.is_primary = screen_dict['is_primary']
|
||||
self.geometry = QtCore.QRect(screen_dict['geometry']['x'], screen_dict['geometry']['y'],
|
||||
screen_dict['geometry']['width'], screen_dict['geometry']['height'])
|
||||
if 'display_geometry' in screen_dict:
|
||||
self.display_geometry = QtCore.QRect(screen_dict['display_geometry']['x'],
|
||||
screen_dict['display_geometry']['y'],
|
||||
screen_dict['display_geometry']['width'],
|
||||
screen_dict['display_geometry']['height'])
|
||||
if 'custom_geometry' in screen_dict:
|
||||
self.custom_geometry = QtCore.QRect(screen_dict['custom_geometry']['x'],
|
||||
screen_dict['custom_geometry']['y'],
|
||||
screen_dict['custom_geometry']['width'],
|
||||
screen_dict['custom_geometry']['height'])
|
||||
|
||||
|
||||
class ScreenList(object):
|
||||
|
|
|
@ -53,9 +53,10 @@ class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
|
|||
|
||||
class WebEngineView(QtWebEngineWidgets.QWebEngineView):
|
||||
"""
|
||||
A sub-classed QWebEngineView to handle paint events of OpenGL
|
||||
A sub-classed QWebEngineView to handle paint events of OpenGL (does not seem to work)
|
||||
and set some attributtes.
|
||||
"""
|
||||
_child = None # QtWidgets.QOpenGLWidget
|
||||
_child = None # QtWidgets.QOpenGLWidget or QWidget?
|
||||
delegatePaint = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
|
@ -84,9 +85,9 @@ class WebEngineView(QtWebEngineWidgets.QWebEngineView):
|
|||
Handle events
|
||||
"""
|
||||
if ev.type() == QtCore.QEvent.ChildAdded:
|
||||
# Only use QOpenGLWidget child
|
||||
# Only use QWidget child (used to be QOpenGLWidget)
|
||||
w = ev.child()
|
||||
if w and isinstance(w, QtWidgets.QOpenGLWidget):
|
||||
if w and isinstance(w, QtWidgets.QWidget):
|
||||
self._child = w
|
||||
w.installEventFilter(self)
|
||||
return super(WebEngineView, self).event(ev)
|
||||
|
|
|
@ -31,7 +31,9 @@ from PyQt5 import QtCore, QtWebChannel, QtWidgets
|
|||
|
||||
from openlp.core.common.path import Path, path_to_str
|
||||
from openlp.core.common.settings import Settings
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui import HideMode
|
||||
from openlp.core.display.screens import ScreenList
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
DISPLAY_PATH = Path(__file__).parent / 'html' / 'display.html'
|
||||
|
@ -113,15 +115,15 @@ class DisplayWindow(QtWidgets.QWidget):
|
|||
from openlp.core.display.webengine import WebEngineView
|
||||
self._is_initialised = False
|
||||
self._fbo = None
|
||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool) #| QtCore.Qt.WindowStaysOnTopHint
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground);
|
||||
self.setAutoFillBackground(True);
|
||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint)
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
self.setAutoFillBackground(True)
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||
self.layout = QtWidgets.QVBoxLayout(self)
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.webview = WebEngineView(self)
|
||||
self.webview.setAttribute(QtCore.Qt.WA_TranslucentBackground);
|
||||
self.webview.page().setBackgroundColor(QtCore.Qt.transparent);
|
||||
self.webview.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
self.webview.page().setBackgroundColor(QtCore.Qt.transparent)
|
||||
self.layout.addWidget(self.webview)
|
||||
self.webview.loadFinished.connect(self.after_loaded)
|
||||
self.set_url(QtCore.QUrl.fromLocalFile(path_to_str(DISPLAY_PATH)))
|
||||
|
@ -131,10 +133,15 @@ class DisplayWindow(QtWidgets.QWidget):
|
|||
self.webview.page().setWebChannel(self.channel)
|
||||
self.is_display = False
|
||||
self.scale = 1
|
||||
self.hide_mode = None
|
||||
if screen and screen.is_display:
|
||||
Registry().register_function('live_display_hide', self.hide_display)
|
||||
Registry().register_function('live_display_show', self.show_display)
|
||||
self.update_from_screen(screen)
|
||||
self.is_display = True
|
||||
self.show()
|
||||
# Only make visible on single monitor setup if setting enabled.
|
||||
if len(ScreenList()) > 1 or Settings().value('core/display on monitor'):
|
||||
self.show()
|
||||
|
||||
def update_from_screen(self, screen):
|
||||
"""
|
||||
|
@ -145,12 +152,23 @@ class DisplayWindow(QtWidgets.QWidget):
|
|||
self.setGeometry(screen.display_geometry)
|
||||
self.screen_number = screen.number
|
||||
|
||||
def set_single_image(self, bg_color, image):
|
||||
image_uri = image.as_uri()
|
||||
self.run_javascript('Display.setFullscreenImage("{bg_color}", "{image}");'.format(bg_color=bg_color,
|
||||
image=image_uri))
|
||||
|
||||
def set_single_image_data(self, bg_color, image_data):
|
||||
self.run_javascript('Display.setFullscreenImageFromData("{bg_color}", '
|
||||
'"{image_data}");'.format(bg_color=bg_color, image_data=image_data))
|
||||
|
||||
def set_startup_screen(self):
|
||||
bg_color = Settings().value('core/logo background color')
|
||||
image = Settings().value('core/logo file')
|
||||
if path_to_str(image).startswith(':'):
|
||||
image = OPENLP_SPLASH_SCREEN_PATH
|
||||
self.run_javascript('Display.setStartupSplashScreen("{bg_color}", "{image}");'.format(bg_color=bg_color, image=image))
|
||||
image_uri = image.as_uri()
|
||||
self.run_javascript('Display.setStartupSplashScreen("{bg_color}", "{image}");'.format(bg_color=bg_color,
|
||||
image=image_uri))
|
||||
|
||||
def set_url(self, url):
|
||||
"""
|
||||
|
@ -323,11 +341,58 @@ class DisplayWindow(QtWidgets.QWidget):
|
|||
"""
|
||||
Show the display
|
||||
"""
|
||||
if self.is_display:
|
||||
# Only make visible on single monitor setup if setting enabled.
|
||||
if len(ScreenList()) == 1 and not Settings().value('core/display on monitor'):
|
||||
return
|
||||
self.run_javascript('Display.show();')
|
||||
# Check if setting for hiding logo on startup is enabled.
|
||||
# If it is, display should remain hidden, otherwise logo is shown. (from def setup)
|
||||
if self.isHidden() and not Settings().value('core/logo hide on startup'):
|
||||
self.setVisible(True)
|
||||
self.hide_mode = None
|
||||
# Trigger actions when display is active again.
|
||||
if self.is_display:
|
||||
Registry().execute('live_display_active')
|
||||
|
||||
def blank_to_theme(self):
|
||||
"""
|
||||
Blank to theme
|
||||
"""
|
||||
self.run_javascript('Display.blankToTheme();')
|
||||
|
||||
def hide_display(self, mode=HideMode.Screen):
|
||||
"""
|
||||
Hide the display by making all layers transparent Store the images so they can be replaced when required
|
||||
|
||||
:param mode: How the screen is to be hidden
|
||||
"""
|
||||
log.debug('hide_display mode = {mode:d}'.format(mode=mode))
|
||||
if self.is_display:
|
||||
# Only make visible on single monitor setup if setting enabled.
|
||||
if len(ScreenList()) == 1 and not Settings().value('core/display on monitor'):
|
||||
return
|
||||
if mode == HideMode.Screen:
|
||||
self.setVisible(False)
|
||||
elif mode == HideMode.Blank:
|
||||
self.run_javascript('Display.blankToBlack();')
|
||||
else:
|
||||
self.run_javascript('Display.blankToTheme();')
|
||||
if mode != HideMode.Screen:
|
||||
if self.isHidden():
|
||||
self.setVisible(True)
|
||||
self.webview.setVisible(True)
|
||||
self.hide_mode = mode
|
||||
|
||||
def set_scale(self, scale):
|
||||
"""
|
||||
Set the HTML scale
|
||||
"""
|
||||
self.scale = scale
|
||||
self.run_javascript('Display.setScale({scale});'.format(scale=scale*100))
|
||||
self.run_javascript('Display.setScale({scale});'.format(scale=scale * 100))
|
||||
|
||||
def alert(self, text, location):
|
||||
"""
|
||||
Set an alert
|
||||
"""
|
||||
self.run_javascript('Display.alert({text}, {location});'.format(text=text, location=location))
|
||||
|
|
|
@ -409,7 +409,7 @@ class Manager(object):
|
|||
self.session.rollback()
|
||||
log.exception('Object list save failed')
|
||||
return False
|
||||
except:
|
||||
except Exception:
|
||||
self.session.rollback()
|
||||
raise
|
||||
|
||||
|
@ -439,7 +439,7 @@ class Manager(object):
|
|||
self.session.rollback()
|
||||
log.exception('Object list save failed')
|
||||
return False
|
||||
except:
|
||||
except Exception:
|
||||
self.session.rollback()
|
||||
raise
|
||||
|
||||
|
@ -556,7 +556,7 @@ class Manager(object):
|
|||
self.session.rollback()
|
||||
log.exception('Failed to delete object')
|
||||
return False
|
||||
except:
|
||||
except Exception:
|
||||
self.session.rollback()
|
||||
raise
|
||||
else:
|
||||
|
@ -591,7 +591,7 @@ class Manager(object):
|
|||
self.session.rollback()
|
||||
log.exception('Failed to delete {name} records'.format(name=object_class.__name__))
|
||||
return False
|
||||
except:
|
||||
except Exception:
|
||||
self.session.rollback()
|
||||
raise
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ from openlp.core.common.mixins import RegistryProperties
|
|||
from openlp.core.common.path import Path
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.display.render import remove_tags, render_tags
|
||||
from openlp.core.lib import ImageSource, ItemCapabilities, build_icon
|
||||
from openlp.core.lib import ItemCapabilities
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
|
||||
|
||||
|
@ -163,8 +163,7 @@ class ServiceItem(RegistryProperties):
|
|||
# Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
|
||||
# the dict instead of rendering them again.
|
||||
previous_pages = {}
|
||||
#for slide in self._raw_frames:
|
||||
for raw_slide in self.slides:
|
||||
for index, raw_slide in enumerate(self.slides):
|
||||
verse_tag = raw_slide['verse']
|
||||
if verse_tag in previous_pages and previous_pages[verse_tag][0] == raw_slide:
|
||||
pages = previous_pages[verse_tag][1]
|
||||
|
@ -175,7 +174,7 @@ class ServiceItem(RegistryProperties):
|
|||
rendered_slide = {
|
||||
'title': raw_slide['title'],
|
||||
'text': render_tags(page),
|
||||
'verse': verse_tag,
|
||||
'verse': index,
|
||||
}
|
||||
self._rendered_slides.append(rendered_slide)
|
||||
display_slide = {
|
||||
|
@ -203,60 +202,6 @@ class ServiceItem(RegistryProperties):
|
|||
self._create_slides()
|
||||
return self._display_slides
|
||||
|
||||
# def render(self, provides_own_theme_data=False):
|
||||
# """
|
||||
# The render method is what generates the frames for the screen and obtains the display information from the
|
||||
# renderer. At this point all slides are built for the given display size.
|
||||
#
|
||||
# :param provides_own_theme_data: This switch disables the usage of the item's theme. However, this is
|
||||
# disabled by default. If this is used, it has to be taken care, that
|
||||
# the renderer knows the correct theme data. However, this is needed
|
||||
# for the theme manager.
|
||||
# """
|
||||
# log.debug('Render called')
|
||||
# self._display_frames = []
|
||||
# self.bg_image_bytes = None
|
||||
# # if not provides_own_theme_data:
|
||||
# # self.renderer.set_item_theme(self.theme)
|
||||
# # self.theme_data, self.main, self.footer = self.renderer.pre_render()
|
||||
# if self.service_item_type == ServiceItemType.Text:
|
||||
# can_render_chords = hasattr(self, 'name') and self.name == 'songs' and Settings().value(
|
||||
# 'songs/enable chords')
|
||||
# log.debug('Formatting slides: {title}'.format(title=self.title))
|
||||
# # Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
|
||||
# # the dict instead of rendering them again.
|
||||
# previous_pages = {}
|
||||
# for slide in self._raw_frames:
|
||||
# verse_tag = slide['verseTag']
|
||||
# if verse_tag in previous_pages and previous_pages[verse_tag][0] == slide['raw_slide']:
|
||||
# pages = previous_pages[verse_tag][1]
|
||||
# else:
|
||||
# # pages = self.renderer.format_slide(slide['raw_slide'], self)
|
||||
# previous_pages[verse_tag] = (slide['raw_slide'], pages)
|
||||
# for page in pages:
|
||||
# page = page.replace('<br>', '{br}')
|
||||
# html_data = render_tags(page.rstrip(), can_render_chords)
|
||||
# new_frame = {
|
||||
# 'title': remove_tags(page),
|
||||
# 'text': remove_tags(page.rstrip(), can_render_chords),
|
||||
# 'chords_text': render_chords(remove_tags(page.rstrip(), False)),
|
||||
# 'html': html_data.replace('&nbsp;', ' '),
|
||||
# 'printing_html': render_tags(html.escape(page.rstrip()), can_render_chords, is_printing=True),
|
||||
# 'verseTag': verse_tag,
|
||||
# }
|
||||
# self._display_frames.append(new_frame)
|
||||
# elif self.service_item_type == ServiceItemType.Image or self.service_item_type == ServiceItemType.Command:
|
||||
# pass
|
||||
# else:
|
||||
# log.error('Invalid value renderer: {item}'.format(item=self.service_item_type))
|
||||
# self.title = remove_tags(self.title)
|
||||
# # The footer should never be None, but to be compatible with a few
|
||||
# # nightly builds between 1.9.4 and 1.9.5, we have to correct this to
|
||||
# # avoid tracebacks.
|
||||
# if self.raw_footer is None:
|
||||
# self.raw_footer = []
|
||||
# self.foot_text = '<br>'.join([_f for _f in self.raw_footer if _f])
|
||||
|
||||
def add_from_image(self, filename, title, background=None, thumbnail=None):
|
||||
"""
|
||||
Add an image slide to the service item.
|
||||
|
@ -307,17 +252,17 @@ class ServiceItem(RegistryProperties):
|
|||
# If the item should have a display title but this frame doesn't have one, we make one up
|
||||
if self.is_capable(ItemCapabilities.HasDisplayTitle) and not display_title:
|
||||
display_title = translate('OpenLP.ServiceItem',
|
||||
'[slide {frame:d}]').format(frame=len(self._raw_frames) + 1)
|
||||
'[slide {frame:d}]').format(frame=len(self.slides) + 1)
|
||||
# Update image path to match servicemanager location if file was loaded from service
|
||||
if image and not self.has_original_files and self.name == 'presentations':
|
||||
file_location = os.path.join(path, file_name)
|
||||
file_location_hash = md5_hash(file_location.encode('utf-8'))
|
||||
image = os.path.join(str(AppLocation.get_section_data_path(self.name)), 'thumbnails',
|
||||
file_location_hash, ntpath.basename(image))
|
||||
self.slides.append({'title': file_name, 'image': image, 'path': path,
|
||||
'display_title': display_title, 'notes': notes})
|
||||
if self.is_capable(ItemCapabilities.HasThumbnails):
|
||||
self.image_manager.add_image(image, ImageSource.CommandPlugins, '#000000')
|
||||
self.slides.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title,
|
||||
'notes': notes, 'thumbnail': image})
|
||||
# if self.is_capable(ItemCapabilities.HasThumbnails):
|
||||
# self.image_manager.add_image(image, ImageSource.CommandPlugins, '#000000')
|
||||
self._new_item()
|
||||
|
||||
def get_service_repr(self, lite_save):
|
||||
|
@ -352,15 +297,19 @@ class ServiceItem(RegistryProperties):
|
|||
}
|
||||
service_data = []
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
service_data = [slide for slide in self._raw_frames]
|
||||
for slide in self.slides:
|
||||
data_slide = deepcopy(slide)
|
||||
data_slide['raw_slide'] = data_slide.pop('text')
|
||||
data_slide['verseTag'] = data_slide.pop('verse')
|
||||
service_data.append(data_slide)
|
||||
elif self.service_item_type == ServiceItemType.Image:
|
||||
if lite_save:
|
||||
for slide in self._raw_frames:
|
||||
for slide in self.slides:
|
||||
service_data.append({'title': slide['title'], 'path': slide['path']})
|
||||
else:
|
||||
service_data = [slide['title'] for slide in self._raw_frames]
|
||||
service_data = [slide['title'] for slide in self.slides]
|
||||
elif self.service_item_type == ServiceItemType.Command:
|
||||
for slide in self._raw_frames:
|
||||
for slide in self.slides:
|
||||
service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'],
|
||||
'display_title': slide['display_title'], 'notes': slide['notes']})
|
||||
return {'header': service_header, 'data': service_data}
|
||||
|
@ -412,7 +361,7 @@ class ServiceItem(RegistryProperties):
|
|||
self.theme_overwritten = header.get('theme_overwritten', False)
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
for slide in service_item['serviceitem']['data']:
|
||||
self._raw_frames.append(slide)
|
||||
self.add_from_text(slide['raw_slide'], slide['verseTag'])
|
||||
elif self.service_item_type == ServiceItemType.Image:
|
||||
settings_section = service_item['serviceitem']['header']['name']
|
||||
background = QtGui.QColor(Settings().value(settings_section + '/background color'))
|
||||
|
@ -536,9 +485,9 @@ class ServiceItem(RegistryProperties):
|
|||
Returns the frames for the ServiceItem
|
||||
"""
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
return self._display_frames
|
||||
return self._display_slides
|
||||
else:
|
||||
return self._raw_frames
|
||||
return self.slides
|
||||
|
||||
def get_rendered_frame(self, row):
|
||||
"""
|
||||
|
@ -549,16 +498,16 @@ class ServiceItem(RegistryProperties):
|
|||
if self.service_item_type == ServiceItemType.Text:
|
||||
return self._display_frames[row]['html'].split('\n')[0]
|
||||
elif self.service_item_type == ServiceItemType.Image:
|
||||
return self._raw_frames[row]['path']
|
||||
return self.slides[row]['path']
|
||||
else:
|
||||
return self._raw_frames[row]['image']
|
||||
return self.slides[row]['image']
|
||||
|
||||
def get_frame_title(self, row=0):
|
||||
"""
|
||||
Returns the title of the raw frame
|
||||
"""
|
||||
try:
|
||||
return self._raw_frames[row]['title']
|
||||
return self.slides[row]['title']
|
||||
except IndexError:
|
||||
return ''
|
||||
|
||||
|
@ -568,11 +517,11 @@ class ServiceItem(RegistryProperties):
|
|||
"""
|
||||
if not frame:
|
||||
try:
|
||||
frame = self._raw_frames[row]
|
||||
frame = self.slides[row]
|
||||
except IndexError:
|
||||
return ''
|
||||
if self.is_image() or self.is_capable(ItemCapabilities.IsOptical):
|
||||
path_from = frame['path']
|
||||
path_from = frame['filename']
|
||||
else:
|
||||
path_from = os.path.join(frame['path'], frame['title'])
|
||||
return path_from
|
||||
|
@ -581,8 +530,8 @@ class ServiceItem(RegistryProperties):
|
|||
"""
|
||||
Remove the specified frame from the item
|
||||
"""
|
||||
if frame in self._raw_frames:
|
||||
self._raw_frames.remove(frame)
|
||||
if frame in self.slides:
|
||||
self.slides.remove(frame)
|
||||
|
||||
def get_media_time(self):
|
||||
"""
|
||||
|
@ -631,7 +580,7 @@ class ServiceItem(RegistryProperties):
|
|||
"""
|
||||
Returns if there are any frames in the service item
|
||||
"""
|
||||
return not bool(self._raw_frames)
|
||||
return not bool(self.slides)
|
||||
|
||||
def validate_item(self, suffix_list=None):
|
||||
"""
|
||||
|
|
|
@ -471,7 +471,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
|
|||
projector = list_item.data(QtCore.Qt.UserRole)
|
||||
try:
|
||||
projector.link.set_shutter_closed()
|
||||
except:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def on_doubleclick_item(self, item, opt=None):
|
||||
|
@ -486,7 +486,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
|
|||
try:
|
||||
log.debug('ProjectorManager: Calling connect_to_host() on "{ip}"'.format(ip=projector.link.ip))
|
||||
projector.link.connect_to_host()
|
||||
except:
|
||||
except Exception:
|
||||
log.debug('ProjectorManager: "{ip}" already connected - skipping'.format(ip=projector.link.ip))
|
||||
return
|
||||
|
||||
|
@ -505,7 +505,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
|
|||
projector = list_item.data(QtCore.Qt.UserRole)
|
||||
try:
|
||||
projector.link.connect_to_host()
|
||||
except:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def on_delete_projector(self, opt=None):
|
||||
|
@ -587,7 +587,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
|
|||
projector = list_item.data(QtCore.Qt.UserRole)
|
||||
try:
|
||||
projector.link.disconnect_from_host()
|
||||
except:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def on_edit_projector(self, opt=None):
|
||||
|
@ -620,7 +620,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
|
|||
projector = list_item.data(QtCore.Qt.UserRole)
|
||||
try:
|
||||
projector.link.set_power_off()
|
||||
except:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def on_poweron_projector(self, opt=None):
|
||||
|
@ -638,7 +638,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
|
|||
projector = list_item.data(QtCore.Qt.UserRole)
|
||||
try:
|
||||
projector.link.set_power_on()
|
||||
except:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def on_show_projector(self, opt=None):
|
||||
|
@ -656,7 +656,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
|
|||
projector = list_item.data(QtCore.Qt.UserRole)
|
||||
try:
|
||||
projector.link.set_shutter_open()
|
||||
except:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def on_status_projector(self, opt=None):
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
The GUI widgets of the exception dialog.
|
||||
"""
|
||||
|
||||
from PyQt5 import QtGui, QtWidgets
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.lib.ui import create_button, create_button_box
|
||||
|
|
|
@ -26,8 +26,36 @@ import logging
|
|||
import os
|
||||
import platform
|
||||
import re
|
||||
import bs4
|
||||
import sqlalchemy
|
||||
from PyQt5 import Qt, QtCore, QtGui, QtWidgets
|
||||
from lxml import etree
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
try:
|
||||
import migrate
|
||||
MIGRATE_VERSION = getattr(migrate, '__version__', '< 0.7')
|
||||
except ImportError:
|
||||
MIGRATE_VERSION = '-'
|
||||
try:
|
||||
import chardet
|
||||
CHARDET_VERSION = chardet.__version__
|
||||
except ImportError:
|
||||
CHARDET_VERSION = '-'
|
||||
try:
|
||||
import enchant
|
||||
ENCHANT_VERSION = enchant.__version__
|
||||
except ImportError:
|
||||
ENCHANT_VERSION = '-'
|
||||
try:
|
||||
import mako
|
||||
MAKO_VERSION = mako.__version__
|
||||
except ImportError:
|
||||
MAKO_VERSION = '-'
|
||||
try:
|
||||
from openlp.core.ui.media.vlcplayer import VERSION
|
||||
VLC_VERSION = VERSION
|
||||
except ImportError:
|
||||
VLC_VERSION = '-'
|
||||
|
||||
from openlp.core.common import is_linux
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
|
@ -76,9 +104,15 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
|
|||
description = self.description_text_edit.toPlainText()
|
||||
traceback = self.exception_text_edit.toPlainText()
|
||||
system = translate('OpenLP.ExceptionForm', 'Platform: {platform}\n').format(platform=platform.platform())
|
||||
library_versions = get_library_versions()
|
||||
library_versions['PyUNO'] = self._get_pyuno_version()
|
||||
libraries = '\n'.join(['{}: {}'.format(library, version) for library, version in library_versions.items()])
|
||||
libraries = ('Python: {python}\nQt5: {qt5}\nPyQt5: {pyqt5}\nSQLAlchemy: {sqalchemy}\n'
|
||||
'SQLAlchemy Migrate: {migrate}\nBeautifulSoup: {soup}\nlxml: {etree}\nChardet: {chardet}\n'
|
||||
'PyEnchant: {enchant}\nMako: {mako}\npyUNO bridge: {uno}\n'
|
||||
'VLC: {vlc}\n').format(python=platform.python_version(), qt5=Qt.qVersion(),
|
||||
pyqt5=Qt.PYQT_VERSION_STR,
|
||||
sqalchemy=sqlalchemy.__version__, migrate=MIGRATE_VERSION,
|
||||
soup=bs4.__version__, etree=etree.__version__, chardet=CHARDET_VERSION,
|
||||
enchant=ENCHANT_VERSION, mako=MAKO_VERSION,
|
||||
uno=self._pyuno_import(), vlc=VLC_VERSION)
|
||||
|
||||
if is_linux():
|
||||
if os.environ.get('KDE_FULL_SESSION') == 'true':
|
||||
|
@ -194,5 +228,5 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
|
|||
return node.getByName('ooSetupVersion')
|
||||
except ImportError:
|
||||
return '-'
|
||||
except:
|
||||
except Exception:
|
||||
return '- (Possible non-standard UNO installation)'
|
||||
|
|
|
@ -174,9 +174,9 @@ class UiIcons(object):
|
|||
setattr(self, key, qta.icon(icon))
|
||||
except Exception:
|
||||
import sys
|
||||
log.error("Unexpected error: %s" % sys.exc_info())
|
||||
log.error('Unexpected error: %s' % sys.exc_info())
|
||||
setattr(self, key, qta.icon('fa.plus-circle', color='red'))
|
||||
except:
|
||||
except Exception:
|
||||
setattr(self, key, qta.icon('fa.plus-circle', color='red'))
|
||||
self.main_icon = build_icon(':/icon/openlp-logo.svg')
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ from openlp.core.ui.aboutform import AboutForm
|
|||
from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||
from openlp.core.ui.formattingtagform import FormattingTagForm
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.ui.media import MediaController
|
||||
from openlp.core.ui.media.mediacontroller import MediaController
|
||||
from openlp.core.ui.pluginform import PluginForm
|
||||
from openlp.core.ui.printserviceform import PrintServiceForm
|
||||
from openlp.core.ui.servicemanager import ServiceManager
|
||||
|
|
|
@ -142,10 +142,3 @@ def format_milliseconds(milliseconds):
|
|||
minutes=minutes,
|
||||
seconds=seconds,
|
||||
millis=millis)
|
||||
|
||||
|
||||
from .mediacontroller import MediaController
|
||||
from .playertab import PlayerTab
|
||||
from .endpoint import media_endpoint
|
||||
|
||||
__all__ = ['MediaController', 'PlayerTab']
|
||||
|
|
|
@ -38,7 +38,7 @@ class Track(object):
|
|||
def __getattribute__(self, name):
|
||||
try:
|
||||
return object.__getattribute__(self, name)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
@ -64,14 +64,14 @@ class Track(object):
|
|||
try:
|
||||
primary = o.replace('other_', '')
|
||||
setattr(self, primary, int(getattr(self, primary)))
|
||||
except:
|
||||
except Exception:
|
||||
for v in getattr(self, o):
|
||||
try:
|
||||
current = getattr(self, primary)
|
||||
setattr(self, primary, int(v))
|
||||
getattr(self, o).append(current)
|
||||
break
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
@ -70,7 +70,7 @@ def get_vlc():
|
|||
is_vlc_available = False
|
||||
try:
|
||||
is_vlc_available = bool(sys.modules['openlp.core.ui.media.vendor.vlc'].get_default_instance())
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
if is_vlc_available:
|
||||
return sys.modules['openlp.core.ui.media.vendor.vlc']
|
||||
|
@ -107,7 +107,7 @@ def get_vlc():
|
|||
if is_vlc_available:
|
||||
try:
|
||||
VERSION = vlc.libvlc_get_version().decode('UTF-8')
|
||||
except:
|
||||
except Exception:
|
||||
VERSION = '0.0.0'
|
||||
# LooseVersion does not work when a string contains letter and digits (e. g. 2.0.5 Twoflower).
|
||||
# http://bugs.python.org/issue14894
|
||||
|
@ -131,7 +131,7 @@ if is_linux() and 'nose' not in sys.argv[0] and get_vlc():
|
|||
# If libx11.so.6 was not found, fallback to more generic libx11.so
|
||||
x11 = ctypes.cdll.LoadLibrary('libX11.so')
|
||||
x11.XInitThreads()
|
||||
except:
|
||||
except Exception:
|
||||
log.exception('Failed to run XInitThreads(), VLC might not work properly!')
|
||||
|
||||
|
||||
|
|
|
@ -56,19 +56,22 @@ class ScreensTab(SettingsTab):
|
|||
|
||||
self.screen_selection_widget = ScreenSelectionWidget(self, ScreenList())
|
||||
self.tab_layout.addWidget(self.screen_selection_widget)
|
||||
self.generic_group_box = QtWidgets.QGroupBox(self)
|
||||
self.generic_group_box.setObjectName('generic_group_box')
|
||||
self.generic_group_layout = QtWidgets.QVBoxLayout(self.generic_group_box)
|
||||
self.display_on_monitor_check = QtWidgets.QCheckBox(self.generic_group_box)
|
||||
self.display_on_monitor_check.setObjectName('monitor_combo_box')
|
||||
self.generic_group_layout.addWidget(self.display_on_monitor_check)
|
||||
self.tab_layout.addWidget(self.generic_group_box)
|
||||
|
||||
Registry().register_function('config_screen_changed', self.screen_selection_widget.load)
|
||||
|
||||
self.retranslate_ui()
|
||||
|
||||
def retranslate_ui(self):
|
||||
self.setWindowTitle(translate('self', 'self')) # TODO: ???
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the settings to populate the tab
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
self.screen_selection_widget.load()
|
||||
self.generic_group_box.setTitle(translate('OpenLP.ScreensTab', 'Generic screen settings'))
|
||||
self.display_on_monitor_check.setText(translate('OpenLP.ScreensTab', 'Display if a single screen'))
|
||||
|
||||
def resizeEvent(self, event=None):
|
||||
"""
|
||||
|
@ -78,6 +81,18 @@ class ScreensTab(SettingsTab):
|
|||
"""
|
||||
QtWidgets.QWidget.resizeEvent(self, event)
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the settings to populate the tab
|
||||
"""
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.settings_section)
|
||||
self.screen_selection_widget.load()
|
||||
# Load generic settings
|
||||
self.display_on_monitor_check.setChecked(Settings().value('core/display on monitor'))
|
||||
|
||||
def save(self):
|
||||
self.screen_selection_widget.save()
|
||||
settings.setValue('core/display on monitor', self.display_on_monitor_check.isChecked())
|
||||
# On save update the screens as well
|
||||
self.settings_form.register_post_process('config_screen_changed')
|
||||
|
|
|
@ -51,7 +51,7 @@ class ServiceItemEditForm(QtWidgets.QDialog, Ui_ServiceItemEditDialog, RegistryP
|
|||
self.item_list = []
|
||||
if self.item.is_image():
|
||||
self.data = True
|
||||
self.item_list.extend(self.item._raw_frames)
|
||||
self.item_list.extend(self.item.slides)
|
||||
self.load_data()
|
||||
self.list_widget.setCurrentItem(self.list_widget.currentItem())
|
||||
|
||||
|
@ -60,7 +60,7 @@ class ServiceItemEditForm(QtWidgets.QDialog, Ui_ServiceItemEditDialog, RegistryP
|
|||
Get the modified service item.
|
||||
"""
|
||||
if self.data:
|
||||
self.item._raw_frames = []
|
||||
self.item.slides = []
|
||||
if self.item.is_image():
|
||||
for item in self.item_list:
|
||||
self.item.add_from_image(item['path'], item['title'])
|
||||
|
|
|
@ -704,7 +704,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
|||
Settings().setValue('servicemanager/last file', file_path)
|
||||
else:
|
||||
raise ValidationError(msg='No service data found')
|
||||
except (NameError, OSError, ValidationError, zipfile.BadZipFile) as e:
|
||||
except (NameError, OSError, ValidationError, zipfile.BadZipFile):
|
||||
self.log_exception('Problem loading service file {name}'.format(name=file_path))
|
||||
critical_error_message_box(
|
||||
message=translate('OpenLP.ServiceManager',
|
||||
|
|
|
@ -33,8 +33,9 @@ from openlp.core.lib import build_icon
|
|||
from openlp.core.projectors.tab import ProjectorTab
|
||||
from openlp.core.ui.advancedtab import AdvancedTab
|
||||
from openlp.core.ui.generaltab import GeneralTab
|
||||
from openlp.core.ui.media import PlayerTab
|
||||
from openlp.core.ui.screenstab import ScreensTab
|
||||
from openlp.core.ui.themestab import ThemesTab
|
||||
from openlp.core.ui.media.playertab import PlayerTab
|
||||
from openlp.core.ui.settingsdialog import Ui_SettingsDialog
|
||||
from openlp.core.ui.themestab import ThemesTab
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ from openlp.core.common.registry import Registry, RegistryBase
|
|||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.display.window import DisplayWindow
|
||||
from openlp.core.lib import ImageSource, ServiceItemAction
|
||||
from openlp.core.lib import ServiceItemAction, image_to_byte
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
from openlp.core.lib.ui import create_action
|
||||
from openlp.core.ui import DisplayControllerType, HideMode
|
||||
|
@ -157,7 +157,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||
Initialise the UI elements of the controller
|
||||
"""
|
||||
try:
|
||||
self.ratio = self.screens.current.geometry.width() / self.screens.current.geometry.height()
|
||||
self.ratio = self.screens.current.display_geometry.width() / self.screens.current.display_geometry.height()
|
||||
except ZeroDivisionError:
|
||||
self.ratio = 1
|
||||
self.process_queue_lock = Lock()
|
||||
|
@ -940,7 +940,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||
display_with = 0
|
||||
for screen in self.screens:
|
||||
if screen.is_display:
|
||||
display_with = screen.geometry.width()
|
||||
display_with = screen.display_geometry.width()
|
||||
if display_with == 0:
|
||||
ratio = 0.25
|
||||
else:
|
||||
|
@ -1183,13 +1183,14 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||
else:
|
||||
# If not live, use the slide's thumbnail/icon instead
|
||||
image_path = self.service_item.get_rendered_frame(self.selected_row)
|
||||
if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
|
||||
image = self.image_manager.get_image(image_path, ImageSource.CommandPlugins)
|
||||
self.slide_image = QtGui.QPixmap.fromImage(image)
|
||||
else:
|
||||
self.slide_image = QtGui.QPixmap(image_path)
|
||||
self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())
|
||||
self.slide_preview.setPixmap(self.slide_image)
|
||||
# if self.service_item.is_capable(ItemCapabilities.HasThumbnails):
|
||||
# image = self.image_manager.get_image(image_path, ImageSource.CommandPlugins)
|
||||
# self.slide_image = QtGui.QPixmap.fromImage(image)
|
||||
# else:
|
||||
# self.slide_image = QtGui.QPixmap(image_path)
|
||||
# self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())
|
||||
# self.slide_preview.setPixmap(self.slide_image)
|
||||
self.preview_display.set_single_image('#000', image_path)
|
||||
else:
|
||||
self.preview_display.go_to_slide(self.selected_row)
|
||||
self.slide_count += 1
|
||||
|
@ -1200,11 +1201,13 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||
"""
|
||||
win_id = QtWidgets.QApplication.desktop().winId()
|
||||
screen = QtWidgets.QApplication.primaryScreen()
|
||||
rect = self.screens.current['size']
|
||||
rect = ScreenList().current.display_geometry
|
||||
win_image = screen.grabWindow(win_id, rect.x(), rect.y(), rect.width(), rect.height())
|
||||
win_image.setDevicePixelRatio(self.slide_preview.devicePixelRatio())
|
||||
self.slide_preview.setPixmap(win_image)
|
||||
win_image.setDevicePixelRatio(self.preview_display.devicePixelRatio())
|
||||
# self.slide_preview.setPixmap(win_image)
|
||||
self.slide_image = win_image
|
||||
base64_image = image_to_byte(win_image, True)
|
||||
self.preview_display.set_single_image_data('#000', base64_image)
|
||||
|
||||
def on_slide_selected_next_action(self, checked):
|
||||
"""
|
||||
|
|
|
@ -188,7 +188,7 @@ class ThemesTab(SettingsTab):
|
|||
Set the global default theme
|
||||
"""
|
||||
self.global_theme = self.default_combo_box.currentText()
|
||||
self.renderer.set_global_theme()
|
||||
# self.renderer.set_global_theme()
|
||||
self._preview_global_theme()
|
||||
|
||||
def update_theme_list(self, theme_list):
|
||||
|
|
|
@ -25,11 +25,9 @@ The :mod:`openlp.core.version` module downloads the version details for OpenLP.
|
|||
import logging
|
||||
import platform
|
||||
import sys
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
from datetime import date
|
||||
from distutils.version import LooseVersion
|
||||
from subprocess import PIPE, Popen
|
||||
|
||||
import requests
|
||||
from PyQt5 import QtCore
|
||||
|
@ -54,7 +52,6 @@ LIBRARIES = OrderedDict([
|
|||
('Chardet', ('chardet',)),
|
||||
('PyEnchant', ('enchant',)),
|
||||
('Mako', ('mako',)),
|
||||
('pyICU', ('icu', 'VERSION')),
|
||||
('VLC', ('openlp.core.ui.media.vlcplayer', 'VERSION')),
|
||||
])
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ from openlp.core.common import CONTROL_CHARS
|
|||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.path import Path, path_to_str, str_to_path
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.formattingtags import FormattingTags
|
||||
from openlp.core.lib.ui import create_action, create_widget_action
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
|
|
|
@ -146,7 +146,6 @@ class ListPreviewWidget(QtWidgets.QTableWidget, RegistryProperties):
|
|||
try:
|
||||
self.cellWidget(row, 0).children()[1].setMaximumWidth(new_height * self.screen_ratio)
|
||||
except Exception:
|
||||
# TODO: Figure out what sort of exceptions are thrown
|
||||
return
|
||||
|
||||
def screen_size_changed(self, screen_ratio):
|
||||
|
|
|
@ -250,14 +250,6 @@ class ScreenSelectionWidget(QtWidgets.QWidget):
|
|||
self.layout.addStretch()
|
||||
|
||||
# Signals and slots
|
||||
# self.screen_combo_box.currentIndexChanged.connect(self.on_display_changed)
|
||||
# self.override_radio_button.toggled.connect(self.on_override_radio_button_pressed)
|
||||
# self.custom_height_value_edit.valueChanged.connect(self.on_display_changed)
|
||||
# self.custom_width_value_edit.valueChanged.connect(self.on_display_changed)
|
||||
# self.custom_Y_value_edit.valueChanged.connect(self.on_display_changed)
|
||||
# self.custom_X_value_edit.valueChanged.connect(self.on_display_changed)
|
||||
# Reload the tab, as the screen resolution/count may have changed.
|
||||
# Registry().register_function('config_screen_changed', self.load)
|
||||
self.custom_geometry_button.toggled.connect(self.height_spin_box.setEnabled)
|
||||
self.custom_geometry_button.toggled.connect(self.left_spin_box.setEnabled)
|
||||
self.custom_geometry_button.toggled.connect(self.top_spin_box.setEnabled)
|
||||
|
|
|
@ -31,7 +31,6 @@ from openlp.core.common.i18n import UiStrings, translate
|
|||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.ui import add_welcome_page
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
|
|
|
@ -34,9 +34,10 @@ from openlp.core.lib.theme import VerticalType
|
|||
from openlp.core.lib.ui import create_action
|
||||
from openlp.core.ui import AlertLocation
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.alerts.endpoint import alerts_endpoint, api_alerts_endpoint
|
||||
from openlp.plugins.alerts.forms import AlertForm
|
||||
from openlp.plugins.alerts.lib import AlertsManager, AlertsTab
|
||||
from openlp.plugins.alerts.endpoint import api_alerts_endpoint, alerts_endpoint
|
||||
from openlp.plugins.alerts.forms.alertform import AlertForm
|
||||
from openlp.plugins.alerts.lib.alertsmanager import AlertsManager
|
||||
from openlp.plugins.alerts.lib.alertstab import AlertsTab
|
||||
from openlp.plugins.alerts.lib.db import init_schema
|
||||
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
import json
|
||||
import logging
|
||||
import urllib
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from openlp.core.api.http import requires_auth
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
|
|
|
@ -40,5 +40,3 @@ mentioned above, like so::
|
|||
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality,
|
||||
so that it is easier to recreate the GUI from the .ui files later if necessary.
|
||||
"""
|
||||
|
||||
from .alertform import AlertForm
|
||||
|
|
|
@ -19,6 +19,3 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from .alertsmanager import AlertsManager
|
||||
from .alertstab import AlertsTab
|
||||
|
|
|
@ -29,6 +29,7 @@ from openlp.core.common.i18n import translate
|
|||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.registry import Registry, RegistryBase
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.display.screens import ScreenList
|
||||
|
||||
|
||||
class AlertsManager(QtCore.QObject, RegistryBase, LogMixin, RegistryProperties):
|
||||
|
@ -78,15 +79,15 @@ class AlertsManager(QtCore.QObject, RegistryBase, LogMixin, RegistryProperties):
|
|||
"""
|
||||
Format and request the Alert and start the timer.
|
||||
"""
|
||||
if not self.alert_list or (self.live_controller.display.screens.display_count == 1 and
|
||||
if not self.alert_list or (len(ScreenList()) == 1 and
|
||||
not Settings().value('core/display on monitor')):
|
||||
return
|
||||
text = self.alert_list.pop(0)
|
||||
alert_tab = self.parent().settings_tab
|
||||
self.live_controller.display.alert(text, alert_tab.location)
|
||||
self.live_controller.displays[0].alert(text, alert_tab.location)
|
||||
# Check to see if we have a timer running.
|
||||
if self.timer_id == 0:
|
||||
self.timer_id = self.startTimer(int(alert_tab.timeout) * 1000)
|
||||
#if self.timer_id == 0:
|
||||
# self.timer_id = self.startTimer(int(alert_tab.timeout) * 1000)
|
||||
|
||||
def timerEvent(self, event):
|
||||
"""
|
||||
|
|
|
@ -25,14 +25,14 @@ import logging
|
|||
from openlp.core.api.http import register_endpoint
|
||||
from openlp.core.common.actions import ActionList
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.lib.ui import create_action
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.bibles.endpoint import api_bibles_endpoint, bibles_endpoint
|
||||
from openlp.plugins.bibles.lib import BibleManager, BibleMediaItem, BiblesTab, DisplayStyle, LanguageSelection, \
|
||||
LayoutStyle
|
||||
from openlp.plugins.bibles.lib.mediaitem import BibleSearch
|
||||
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, LanguageSelection
|
||||
from openlp.plugins.bibles.lib.biblestab import BiblesTab
|
||||
from openlp.plugins.bibles.lib.manager import BibleManager
|
||||
from openlp.plugins.bibles.lib.mediaitem import BibleMediaItem, BibleSearch
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -28,6 +28,12 @@ import urllib.error
|
|||
from lxml import etree
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
try:
|
||||
from pysword import modules
|
||||
PYSWORD_AVAILABLE = True
|
||||
except ImportError:
|
||||
PYSWORD_AVAILABLE = False
|
||||
|
||||
from openlp.core.common import trace_error_handler
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import UiStrings, get_locale_key, translate
|
||||
|
@ -42,13 +48,6 @@ from openlp.plugins.bibles.lib.importers.http import BGExtract, BSExtract, CWExt
|
|||
from openlp.plugins.bibles.lib.manager import BibleFormat
|
||||
|
||||
|
||||
try:
|
||||
from pysword import modules
|
||||
PYSWORD_AVAILABLE = True
|
||||
except:
|
||||
PYSWORD_AVAILABLE = False
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -585,7 +584,7 @@ class BibleImportForm(OpenLPWizard):
|
|||
(WebDownload.Bibleserver, BSExtract())):
|
||||
try:
|
||||
bibles = extractor.get_bibles_from_http()
|
||||
except (urllib.error.URLError, ConnectionError) as err:
|
||||
except (urllib.error.URLError, ConnectionError):
|
||||
critical_error_message_box(translate('BiblesPlugin.ImportWizardForm', 'Error during download'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'An error occurred while downloading the list of bibles from %s.'))
|
||||
|
@ -615,7 +614,7 @@ class BibleImportForm(OpenLPWizard):
|
|||
self.sword_bible_combo_box.clear()
|
||||
for key in bible_keys:
|
||||
self.sword_bible_combo_box.addItem(self.pysword_folder_modules_json[key]['description'], key)
|
||||
except:
|
||||
except Exception:
|
||||
self.sword_bible_combo_box.clear()
|
||||
|
||||
def on_sword_zipfile_path_edit_path_changed(self, new_path):
|
||||
|
@ -630,7 +629,7 @@ class BibleImportForm(OpenLPWizard):
|
|||
self.sword_zipbible_combo_box.clear()
|
||||
for key in bible_keys:
|
||||
self.sword_zipbible_combo_box.addItem(self.pysword_zip_modules_json[key]['description'], key)
|
||||
except:
|
||||
except Exception:
|
||||
self.sword_zipbible_combo_box.clear()
|
||||
|
||||
def register_fields(self):
|
||||
|
|
|
@ -424,9 +424,3 @@ class SearchResults(object):
|
|||
Returns whether or not the verse list contains verses.
|
||||
"""
|
||||
return len(self.verse_list) > 0
|
||||
|
||||
|
||||
from .versereferencelist import VerseReferenceList
|
||||
from .manager import BibleManager
|
||||
from .biblestab import BiblesTab
|
||||
from .mediaitem import BibleMediaItem
|
||||
|
|
|
@ -54,7 +54,6 @@ from collections import namedtuple
|
|||
|
||||
from openlp.core.common import get_file_encoding
|
||||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.core.lib.exceptions import ValidationError
|
||||
from openlp.plugins.bibles.lib.bibleimport import BibleImport
|
||||
|
||||
|
|
|
@ -750,7 +750,7 @@ def get_soup_for_bible_ref(reference_url, headers=None, pre_parse_regex=None, pr
|
|||
return None
|
||||
try:
|
||||
page_source = get_web_page(reference_url, headers, update_openlp=True)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
log.exception('Unable to download Bible %s, unknown exception occurred', reference_url)
|
||||
page_source = None
|
||||
if not page_source:
|
||||
|
|
|
@ -40,7 +40,7 @@ from .importers.zefania import ZefaniaBible
|
|||
|
||||
try:
|
||||
from .importers.sword import SwordBible
|
||||
except:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -38,9 +38,9 @@ from openlp.core.ui.icons import UiIcons
|
|||
from openlp.core.widgets.edits import SearchEdit
|
||||
from openlp.plugins.bibles.forms.bibleimportform import BibleImportForm
|
||||
from openlp.plugins.bibles.forms.editbibleform import EditBibleForm
|
||||
from openlp.plugins.bibles.lib import DisplayStyle, LayoutStyle, VerseReferenceList, get_reference_match, \
|
||||
from openlp.plugins.bibles.lib import DisplayStyle, LayoutStyle, get_reference_match, \
|
||||
get_reference_separator
|
||||
|
||||
from openlp.plugins.bibles.lib.versereferencelist import VerseReferenceList
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -33,9 +33,9 @@ from openlp.core.lib.db import Manager
|
|||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.custom.endpoint import api_custom_endpoint, custom_endpoint
|
||||
from openlp.plugins.custom.lib import CustomMediaItem, CustomTab
|
||||
from openlp.plugins.custom.lib.db import CustomSlide, init_schema
|
||||
from openlp.plugins.custom.lib.mediaitem import CustomSearch
|
||||
from openlp.plugins.custom.lib.mediaitem import CustomMediaItem, CustomSearch
|
||||
from openlp.plugins.custom.lib.customtab import CustomTab
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -27,13 +27,11 @@ from PyQt5 import QtCore, QtWidgets
|
|||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.ui import critical_error_message_box, find_and_set_in_combo_box
|
||||
from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser
|
||||
from openlp.plugins.custom.forms.editcustomdialog import Ui_CustomEditDialog
|
||||
from openlp.plugins.custom.forms.editcustomslideform import EditCustomSlideForm
|
||||
from openlp.plugins.custom.lib.customxmlhandler import CustomXMLBuilder, CustomXMLParser
|
||||
from openlp.plugins.custom.lib.db import CustomSlide
|
||||
|
||||
from .editcustomdialog import Ui_CustomEditDialog
|
||||
from .editcustomslideform import EditCustomSlideForm
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,3 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from .customtab import CustomTab
|
||||
from .customxmlhandler import CustomXMLBuilder, CustomXMLParser
|
||||
from .mediaitem import CustomMediaItem
|
||||
|
|
|
@ -35,7 +35,7 @@ from openlp.core.lib.serviceitem import ItemCapabilities
|
|||
from openlp.core.lib.ui import create_widget_action
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.custom.forms.editcustomform import EditCustomForm
|
||||
from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser
|
||||
from openlp.plugins.custom.lib.customxmlhandler import CustomXMLBuilder, CustomXMLParser
|
||||
from openlp.plugins.custom.lib.db import CustomSlide
|
||||
|
||||
|
||||
|
@ -351,7 +351,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||
else:
|
||||
custom.credits = ''
|
||||
custom_xml = CustomXMLBuilder()
|
||||
for (idx, slide) in enumerate(item._raw_frames):
|
||||
for (idx, slide) in enumerate(item.slides):
|
||||
custom_xml.add_verse_to_lyrics('custom', str(idx + 1), slide['raw_slide'])
|
||||
custom.text = str(custom_xml.extract_xml(), 'utf-8')
|
||||
self.plugin.db_manager.save_object(custom)
|
||||
|
|
|
@ -40,6 +40,3 @@ mentioned above, like so::
|
|||
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality,
|
||||
so that it is easier to recreate the GUI from the .ui files later if necessary.
|
||||
"""
|
||||
|
||||
from .addgroupform import AddGroupForm
|
||||
from .choosegroupform import ChooseGroupForm
|
||||
|
|
|
@ -32,7 +32,9 @@ from openlp.core.lib.db import Manager
|
|||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.images.endpoint import api_images_endpoint, images_endpoint
|
||||
from openlp.plugins.images.lib import ImageMediaItem, ImageTab, upgrade
|
||||
from openlp.plugins.images.lib import upgrade
|
||||
from openlp.plugins.images.lib.mediaitem import ImageMediaItem
|
||||
from openlp.plugins.images.lib.imagetab import ImageTab
|
||||
from openlp.plugins.images.lib.db import init_schema
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,3 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from .imagetab import ImageTab
|
||||
from .mediaitem import ImageMediaItem
|
||||
|
|
|
@ -37,7 +37,8 @@ from openlp.core.lib.serviceitem import ItemCapabilities
|
|||
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.widgets.views import TreeWidgetWithDnD
|
||||
from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm
|
||||
from openlp.plugins.images.forms.addgroupform import AddGroupForm
|
||||
from openlp.plugins.images.forms.choosegroupform import ChooseGroupForm
|
||||
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
|
||||
|
||||
|
||||
|
|
|
@ -19,8 +19,3 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from .mediaitem import MediaMediaItem
|
||||
from .mediatab import MediaTab
|
||||
|
||||
__all__ = ['MediaMediaItem']
|
||||
|
|
|
@ -36,7 +36,8 @@ from openlp.core.lib import build_icon
|
|||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
|
||||
from openlp.plugins.media.lib import MediaMediaItem, MediaTab
|
||||
from openlp.plugins.media.lib.mediaitem import MediaMediaItem
|
||||
from openlp.plugins.media.lib.mediatab import MediaTab
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -19,8 +19,3 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from .presentationcontroller import PresentationController
|
||||
from .messagelistener import MessageListener
|
||||
from .mediaitem import PresentationMediaItem
|
||||
from .presentationtab import PresentationTab
|
||||
|
|
|
@ -122,7 +122,7 @@ class ImpressController(PresentationController):
|
|||
while uno_instance is None and loop < 3:
|
||||
try:
|
||||
uno_instance = get_uno_instance(resolver)
|
||||
except:
|
||||
except Exception:
|
||||
log.warning('Unable to find running instance ')
|
||||
self.start_process()
|
||||
loop += 1
|
||||
|
@ -131,7 +131,7 @@ class ImpressController(PresentationController):
|
|||
log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
|
||||
desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
|
||||
return desktop
|
||||
except:
|
||||
except Exception:
|
||||
log.warning('Failed to get UNO desktop')
|
||||
return None
|
||||
|
||||
|
@ -173,7 +173,7 @@ class ImpressController(PresentationController):
|
|||
desktop = self.get_uno_desktop()
|
||||
else:
|
||||
desktop = self.get_com_desktop()
|
||||
except:
|
||||
except Exception:
|
||||
log.warning('Failed to find an OpenOffice desktop to terminate')
|
||||
if not desktop:
|
||||
return
|
||||
|
@ -191,7 +191,7 @@ class ImpressController(PresentationController):
|
|||
try:
|
||||
desktop.terminate()
|
||||
log.debug('OpenOffice killed')
|
||||
except:
|
||||
except Exception:
|
||||
log.warning('Failed to terminate OpenOffice')
|
||||
|
||||
|
||||
|
@ -236,11 +236,11 @@ class ImpressDocument(PresentationDocument):
|
|||
properties = tuple(properties)
|
||||
try:
|
||||
self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties)
|
||||
except:
|
||||
except Exception:
|
||||
log.warning('Failed to load presentation {url}'.format(url=url))
|
||||
return False
|
||||
self.presentation = self.document.getPresentation()
|
||||
self.presentation.Display = ScreenList().current['number'] + 1
|
||||
self.presentation.Display = ScreenList().current.number + 1
|
||||
self.control = None
|
||||
self.create_thumbnails()
|
||||
self.create_titles_and_notes()
|
||||
|
@ -275,7 +275,7 @@ class ImpressDocument(PresentationDocument):
|
|||
delete_file(path)
|
||||
except ErrorCodeIOException as exception:
|
||||
log.exception('ERROR! ErrorCodeIOException {error:d}'.format(error=exception.ErrCode))
|
||||
except:
|
||||
except Exception:
|
||||
log.exception('{path} - Unable to store openoffice preview'.format(path=path))
|
||||
|
||||
def create_property(self, name, value):
|
||||
|
@ -303,7 +303,7 @@ class ImpressDocument(PresentationDocument):
|
|||
self.presentation.end()
|
||||
self.presentation = None
|
||||
self.document.dispose()
|
||||
except:
|
||||
except Exception:
|
||||
log.warning("Closing presentation failed")
|
||||
self.document = None
|
||||
self.controller.remove_doc(self)
|
||||
|
@ -320,7 +320,7 @@ class ImpressDocument(PresentationDocument):
|
|||
if self.document.getPresentation() is None:
|
||||
log.debug("getPresentation failed to find a presentation")
|
||||
return False
|
||||
except:
|
||||
except Exception:
|
||||
log.warning("getPresentation failed to find a presentation")
|
||||
return False
|
||||
return True
|
||||
|
@ -387,7 +387,7 @@ class ImpressDocument(PresentationDocument):
|
|||
self.control.activate()
|
||||
self.goto_slide(1)
|
||||
# Make sure impress doesn't steal focus, unless we're on a single screen setup
|
||||
if len(ScreenList().screen_list) > 1:
|
||||
if len(ScreenList()) > 1:
|
||||
Registry().get('main_window').activateWindow()
|
||||
|
||||
def get_slide_number(self):
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
###############################################################################
|
||||
import logging
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common.i18n import UiStrings, get_natural_key, translate
|
||||
from openlp.core.common.path import path_to_str, str_to_path
|
||||
|
@ -32,7 +32,7 @@ from openlp.core.lib.mediamanageritem import MediaManagerItem
|
|||
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
from openlp.core.lib.ui import create_horizontal_adjusting_combo_box, critical_error_message_box
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.presentations.lib import MessageListener
|
||||
from openlp.plugins.presentations.lib.messagelistener import MessageListener
|
||||
from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
|
||||
|
||||
|
||||
|
|
|
@ -280,7 +280,7 @@ class PdfDocument(PresentationDocument):
|
|||
for image_path in created_files:
|
||||
if image_path.is_file():
|
||||
self.image_files.append(image_path)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
log.exception(runlog)
|
||||
return False
|
||||
self.num_pages = len(self.image_files)
|
||||
|
|
|
@ -196,7 +196,7 @@ class PowerpointDocument(PresentationDocument):
|
|||
self.presentation = None
|
||||
self.controller.remove_doc(self)
|
||||
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
|
||||
if len(ScreenList().screen_list) > 1:
|
||||
if len(ScreenList()) > 1:
|
||||
Registry().get('main_window').activateWindow()
|
||||
|
||||
def is_loaded(self):
|
||||
|
@ -315,7 +315,7 @@ class PowerpointDocument(PresentationDocument):
|
|||
dpi = win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88)
|
||||
except win32ui.error:
|
||||
dpi = 96
|
||||
size = ScreenList().current['size']
|
||||
size = ScreenList().current.display_geometry
|
||||
ppt_window = None
|
||||
try:
|
||||
ppt_window = self.presentation.SlideShowSettings.Run()
|
||||
|
|
|
@ -327,14 +327,14 @@ class PresentationDocument(object):
|
|||
titles_path = self.get_thumbnail_folder() / 'titles.txt'
|
||||
try:
|
||||
titles = titles_path.read_text().splitlines()
|
||||
except:
|
||||
except Exception:
|
||||
log.exception('Failed to open/read existing titles file')
|
||||
titles = []
|
||||
for slide_no, title in enumerate(titles, 1):
|
||||
notes_path = self.get_thumbnail_folder() / 'slideNotes{number:d}.txt'.format(number=slide_no)
|
||||
try:
|
||||
note = notes_path.read_text()
|
||||
except:
|
||||
except Exception:
|
||||
log.exception('Failed to open/read notes file')
|
||||
note = ''
|
||||
notes.append(note)
|
||||
|
|
|
@ -36,7 +36,9 @@ from openlp.core.lib import build_icon
|
|||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.presentations.endpoint import api_presentations_endpoint, presentations_endpoint
|
||||
from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController
|
||||
from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem
|
||||
from openlp.plugins.presentations.lib.presentationtab import PresentationTab
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -45,4 +45,3 @@ This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
|
|||
them separate from the functionality, so that it is easier to recreate the GUI
|
||||
from the .ui files later if necessary.
|
||||
"""
|
||||
from .editsongform import EditSongForm
|
||||
|
|
|
@ -320,7 +320,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||
for verse in multiple:
|
||||
self.song.verse_order = re.sub(r'([' + verse.upper() + verse.lower() + r'])(\W|$)',
|
||||
r'\g<1>1\2', self.song.verse_order)
|
||||
except:
|
||||
except Exception:
|
||||
log.exception('Problem processing song Lyrics \n{xml}'.format(xml=sxml.dump_xml()))
|
||||
raise
|
||||
|
||||
|
@ -1088,7 +1088,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
|
|||
if audio_path not in file_paths:
|
||||
try:
|
||||
audio_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
log.exception('Could not remove file: {audio}'.format(audio=audio_path))
|
||||
if not file_paths:
|
||||
try:
|
||||
|
|
|
@ -33,7 +33,7 @@ from openlp.core.common.applocation import AppLocation
|
|||
from openlp.core.common.i18n import translate
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.core.display.render import remove_tags
|
||||
from openlp.plugins.songs.lib.db import Author, MediaFile, Song, Topic
|
||||
from openlp.plugins.songs.lib.db import Author, MediaFile, Song
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -291,10 +291,8 @@ class CCLIFileImport(SongImport):
|
|||
# verse type, so set flag
|
||||
verse_type = VerseType.tags[VerseType.Other]
|
||||
check_first_verse_line = True
|
||||
verse_number = verse_desc_parts[1]
|
||||
else:
|
||||
verse_type = VerseType.tags[VerseType.Other]
|
||||
verse_number = 1
|
||||
verse_start = True
|
||||
else:
|
||||
# check first line for verse type
|
||||
|
|
|
@ -108,7 +108,7 @@ class EasySlidesImport(SongImport):
|
|||
def _add_authors(self, writer):
|
||||
try:
|
||||
self.parse_author(str(writer))
|
||||
except UnicodeDecodeError as e:
|
||||
except UnicodeDecodeError:
|
||||
log.exception('Unicode decode error while decoding Writer')
|
||||
self._success = False
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ class OpenOfficeImport(SongImport):
|
|||
process = QtCore.QProcess()
|
||||
process.startDetached(cmd)
|
||||
self.process_started = True
|
||||
except:
|
||||
except Exception:
|
||||
log.exception("start_ooo_process failed")
|
||||
|
||||
def open_ooo_file(self, file_path):
|
||||
|
@ -178,7 +178,7 @@ class OpenOfficeImport(SongImport):
|
|||
"""
|
||||
try:
|
||||
self.document.close(True)
|
||||
except:
|
||||
except Exception:
|
||||
log.exception('Exception in close_ooo_file - trying to ignore it.')
|
||||
self.document = None
|
||||
|
||||
|
@ -189,7 +189,7 @@ class OpenOfficeImport(SongImport):
|
|||
if self.process_started:
|
||||
try:
|
||||
self.desktop.terminate()
|
||||
except:
|
||||
except Exception:
|
||||
log.exception('Exception in close_ooo - trying to ignore it.')
|
||||
|
||||
def process_presentation(self):
|
||||
|
|
|
@ -224,5 +224,5 @@ class SongShowPlusImport(SongImport):
|
|||
try:
|
||||
# Don't question this, it works...
|
||||
return data.decode('utf-8').encode('cp1251').decode('cp1251')
|
||||
except:
|
||||
except Exception:
|
||||
return data.decode(retrieve_windows_encoding())
|
||||
|
|
|
@ -194,7 +194,7 @@ class SundayPlusImport(SongImport):
|
|||
while True:
|
||||
try:
|
||||
return blob.decode(self.encoding)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
self.encoding = retrieve_windows_encoding()
|
||||
|
||||
def unescape(self, text):
|
||||
|
|
|
@ -325,12 +325,12 @@ class SongMediaItem(MediaManagerItem):
|
|||
:param search_results: A tuple containing (songbook entry, book name, song title, song id)
|
||||
:return: None
|
||||
"""
|
||||
def get_songbook_key(text_array):
|
||||
def get_songbook_key(text):
|
||||
"""
|
||||
Get the key to sort by
|
||||
:param text_array: the result text to be processed.
|
||||
:param text: the text tuple to be processed.
|
||||
"""
|
||||
return get_natural_key(text_array[1]), get_natural_key(text_array[0]), get_natural_key(text_array[2])
|
||||
return get_natural_key('{0} {1} {2}'.format(text[1], text[0], text[2]))
|
||||
|
||||
log.debug('display results Book')
|
||||
self.list_view.clear()
|
||||
|
|
|
@ -34,7 +34,7 @@ from urllib.request import HTTPCookieProcessor, URLError, build_opener
|
|||
from bs4 import BeautifulSoup, NavigableString
|
||||
|
||||
from openlp.plugins.songs.lib import VerseType, clean_song
|
||||
from openlp.plugins.songs.lib.db import Author, Song, Topic
|
||||
from openlp.plugins.songs.lib.db import Song, Author, Topic
|
||||
from openlp.plugins.songs.lib.openlyricsxml import SongXML
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,3 @@
|
|||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from .songusagedeleteform import SongUsageDeleteForm
|
||||
from .songusagedetailform import SongUsageDetailForm
|
||||
|
|
|
@ -33,7 +33,8 @@ from openlp.core.lib.db import Manager
|
|||
from openlp.core.lib.plugin import Plugin, StringContent
|
||||
from openlp.core.lib.ui import create_action
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.songusage.forms import SongUsageDeleteForm, SongUsageDetailForm
|
||||
from openlp.plugins.songusage.forms.songusagedetailform import SongUsageDetailForm
|
||||
from openlp.plugins.songusage.forms.songusagedeleteform import SongUsageDeleteForm
|
||||
from openlp.plugins.songusage.lib import upgrade
|
||||
from openlp.plugins.songusage.lib.db import SongUsageItem, init_schema
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ def start():
|
|||
"""
|
||||
Instantiate and run the application.
|
||||
"""
|
||||
faulthandler.enable()
|
||||
set_up_fault_handling()
|
||||
# Add support for using multiprocessing from frozen Windows executable (built using PyInstaller),
|
||||
# see https://docs.python.org/3/library/multiprocessing.html#multiprocessing.freeze_support
|
||||
|
|
|
@ -11,17 +11,15 @@ environment:
|
|||
|
||||
install:
|
||||
# Install dependencies from pypi
|
||||
- "%PYTHON%\\python.exe -m pip install sqlalchemy alembic appdirs chardet beautifulsoup4 lxml Mako mysql-connector-python nose mock pyodbc psycopg2 pypiwin32 websockets asyncio waitress six webob requests QtAwesome PyQt5"
|
||||
# Download and install pyicu (originally from http://www.lfd.uci.edu/~gohlke/pythonlibs/)
|
||||
- "%PYTHON%\\python.exe -m pip install https://get.openlp.org/win-sdk/PyICU-2.1-cp37-cp37m-win_amd64.whl"
|
||||
- "%PYTHON%\\python.exe -m pip install sqlalchemy alembic appdirs chardet beautifulsoup4 lxml Mako mysql-connector-python nose mock pyodbc psycopg2 pypiwin32 websockets asyncio waitress six webob requests QtAwesome PyQt5 pymediainfo"
|
||||
# Download and unpack mupdf
|
||||
- appveyor DownloadFile http://mupdf.com/downloads/archive/mupdf-1.9a-windows.zip
|
||||
- 7z x mupdf-1.9a-windows.zip
|
||||
- cp mupdf-1.9a-windows/mupdf.exe openlp-branch/mupdf.exe
|
||||
- appveyor DownloadFile https://mupdf.com/downloads/archive/mupdf-1.14.0-windows.zip
|
||||
- 7z x mupdf-1.14.0-windows.zip
|
||||
- cp mupdf-1.14.0-windows/mupdf.exe openlp-branch/mupdf.exe
|
||||
# Download and unpack mediainfo
|
||||
- appveyor DownloadFile https://mediaarea.net/download/binary/mediainfo/0.7.90/MediaInfo_CLI_0.7.90_Windows_i386.zip
|
||||
- appveyor DownloadFile https://mediaarea.net/download/binary/mediainfo/18.08.1/MediaInfo_CLI_18.08.1_Windows_i386.zip
|
||||
- mkdir MediaInfo
|
||||
- 7z x -oMediaInfo MediaInfo_CLI_0.7.90_Windows_i386.zip
|
||||
- 7z x -oMediaInfo MediaInfo_CLI_18.08.1_Windows_i386.zip
|
||||
- cp MediaInfo\\MediaInfo.exe openlp-branch\\MediaInfo.exe
|
||||
|
||||
build: off
|
||||
|
|
|
@ -40,8 +40,8 @@ IS_MAC = sys.platform.startswith('dar')
|
|||
|
||||
VERS = {
|
||||
'Python': '3.6',
|
||||
'PyQt5': '5.0',
|
||||
'Qt5': '5.0',
|
||||
'PyQt5': '5.5',
|
||||
'Qt5': '5.5',
|
||||
'pymediainfo': '2.2',
|
||||
'sqlalchemy': '0.5',
|
||||
'enchant': '1.6'
|
||||
|
@ -52,7 +52,6 @@ WIN32_MODULES = [
|
|||
'win32com',
|
||||
'win32ui',
|
||||
'pywintypes',
|
||||
'icu',
|
||||
]
|
||||
|
||||
LINUX_MODULES = [
|
||||
|
|
|
@ -60,7 +60,6 @@ from getpass import getpass
|
|||
from lxml import etree, objectify
|
||||
from PyQt5 import QtCore
|
||||
|
||||
|
||||
SERVER_URL = 'http://www.transifex.com/api/2/project/openlp/resource/openlp-26x/'
|
||||
IGNORED_PATHS = ['scripts']
|
||||
IGNORED_FILES = ['setup.py']
|
||||
|
|
|
@ -9,16 +9,13 @@ ignore = E402,E722,W503,W504
|
|||
|
||||
[flake8]
|
||||
exclude=resources.py,vlc.py
|
||||
max-line-length = 120
|
||||
ignore = E402,E722,W503,W504
|
||||
ignore = E402,W503,W504,D
|
||||
|
||||
[pycodestyle]
|
||||
exclude = resources.py,vlc.py
|
||||
max-line-length = 120
|
||||
# Ignoring:
|
||||
# E402...
|
||||
# E722 do not use bare 'except'
|
||||
# W503 line break before binary operator
|
||||
# W504 line break after binary operator
|
||||
ignore = E402,E722,W503,W504
|
||||
|
||||
ignore = E402,W503,W504
|
||||
|
|
12
setup.py
12
setup.py
|
@ -119,7 +119,8 @@ requires = [
|
|||
'chardet',
|
||||
'lxml',
|
||||
'Mako',
|
||||
'PyQt5',
|
||||
'pymediainfo >= 2.2',
|
||||
'PyQt5 >= 5.5',
|
||||
'QtAwesome',
|
||||
'requests',
|
||||
'SQLAlchemy >= 0.5',
|
||||
|
@ -128,10 +129,7 @@ requires = [
|
|||
'websockets'
|
||||
]
|
||||
if sys.platform.startswith('win'):
|
||||
requires.extend([
|
||||
'PyICU',
|
||||
'pywin32'
|
||||
])
|
||||
requires.append('pywin32')
|
||||
elif sys.platform.startswith('darwin'):
|
||||
requires.extend([
|
||||
'pyobjc',
|
||||
|
@ -188,7 +186,7 @@ using a computer and a data projector.""",
|
|||
author_email='raoulsnyman@openlp.org',
|
||||
url='https://openlp.org/',
|
||||
license='GNU General Public License',
|
||||
packages=find_packages(exclude=['ez_setup', 'tests']),
|
||||
packages=find_packages(exclude=['ez_setup', 'tests*']),
|
||||
py_modules=['run_openlp'],
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
|
@ -204,7 +202,7 @@ using a computer and a data projector.""",
|
|||
'jenkins': ['python-jenkins'],
|
||||
'launchpad': ['launchpadlib']
|
||||
},
|
||||
tests_require=['nose2', 'PyICU', 'pylint', 'pyodbc', 'pysword'],
|
||||
tests_require=['nose2', 'pylint', 'pyodbc', 'pysword'],
|
||||
test_suite='nose2.collector.collector',
|
||||
entry_points={'gui_scripts': ['openlp = run_openlp:start']}
|
||||
)
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
# 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 #
|
||||
###############################################################################
|
||||
import sys
|
||||
from unittest import TestCase
|
||||
# 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 #
|
||||
|
@ -26,9 +27,12 @@ from unittest.mock import MagicMock
|
|||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
# Mock QtWebEngineWidgets
|
||||
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
||||
|
||||
from openlp.core.api.endpoint.controller import controller_direction, controller_text
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.display.renderer import Renderer
|
||||
from openlp.core.display.render import Renderer
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.lib.serviceitem import ServiceItem
|
||||
from tests.utils import convert_file_service_item
|
||||
|
|
|
@ -26,8 +26,9 @@ import os
|
|||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from openlp.core.api.http import NotFound, application, register_endpoint
|
||||
from openlp.core.api.http import register_endpoint, application
|
||||
from openlp.core.api.http.endpoint import Endpoint
|
||||
from openlp.core.api.http.errors import NotFound
|
||||
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
|
@ -69,7 +70,9 @@ class TestRouting(TestCase):
|
|||
rqst.method = 'GET'
|
||||
application.dispatch(rqst)
|
||||
# THEN: the not found id called
|
||||
assert 1 == application.route_map['^\\/test\\/image$']['GET'].call_count, \
|
||||
route_key = next(iter(application.route_map))
|
||||
assert '/image' in route_key
|
||||
assert 1 == application.route_map[route_key]['GET'].call_count, \
|
||||
'main_index function should have been called'
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ This module contains tests for the lib submodule of the Remotes plugin.
|
|||
"""
|
||||
import re
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ class TestUtilsDBFunctions(TestCase):
|
|||
if os.path.exists(self.tmp_folder):
|
||||
shutil.rmtree(self.tmp_folder)
|
||||
break
|
||||
except:
|
||||
except Exception:
|
||||
time.sleep(1)
|
||||
retries += 1
|
||||
|
||||
|
|
|
@ -22,12 +22,10 @@
|
|||
"""
|
||||
Package to test the openlp.core.lib.languages package.
|
||||
"""
|
||||
from unittest import skipIf
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.common import is_macosx
|
||||
from openlp.core.common.i18n import LANGUAGES, Language, LanguageManager, UiStrings, get_language, get_locale_key, \
|
||||
get_natural_key, translate
|
||||
from openlp.core.common.i18n import LANGUAGES, Language, UiStrings, get_language, get_locale_key, get_natural_key, \
|
||||
translate, LanguageManager
|
||||
from openlp.core.common.settings import Settings
|
||||
|
||||
|
||||
|
@ -113,7 +111,6 @@ def test_get_language_invalid_with_none():
|
|||
assert language is None
|
||||
|
||||
|
||||
@skipIf(is_macosx(), 'This test doesn\'t work on macOS currently')
|
||||
def test_get_locale_key():
|
||||
"""
|
||||
Test the get_locale_key(string) function
|
||||
|
|
|
@ -397,7 +397,7 @@ class TestPath(TestCase):
|
|||
try:
|
||||
create_paths(mocked_path)
|
||||
assert False, 'create_paths should have thrown an exception'
|
||||
except:
|
||||
except Exception:
|
||||
# THEN: `create_paths` raises an exception
|
||||
pass
|
||||
|
||||
|
|
|
@ -279,7 +279,7 @@ class TestLib(TestCase):
|
|||
# last test.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Only continue when the thumb does not exist.
|
||||
|
@ -297,7 +297,7 @@ class TestLib(TestCase):
|
|||
# Remove the thumb so that the test actually tests if the thumb will be created.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_create_thumb_no_size(self):
|
||||
|
@ -313,7 +313,7 @@ class TestLib(TestCase):
|
|||
# last test.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Only continue when the thumb does not exist.
|
||||
|
@ -331,7 +331,7 @@ class TestLib(TestCase):
|
|||
# Remove the thumb so that the test actually tests if the thumb will be created.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_create_thumb_invalid_size(self):
|
||||
|
@ -348,7 +348,7 @@ class TestLib(TestCase):
|
|||
# last test.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Only continue when the thumb does not exist.
|
||||
|
@ -366,7 +366,7 @@ class TestLib(TestCase):
|
|||
# Remove the thumb so that the test actually tests if the thumb will be created.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_create_thumb_width_only(self):
|
||||
|
@ -383,7 +383,7 @@ class TestLib(TestCase):
|
|||
# last test.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Only continue when the thumb does not exist.
|
||||
|
@ -401,7 +401,7 @@ class TestLib(TestCase):
|
|||
# Remove the thumb so that the test actually tests if the thumb will be created.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_create_thumb_height_only(self):
|
||||
|
@ -418,7 +418,7 @@ class TestLib(TestCase):
|
|||
# last test.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Only continue when the thumb does not exist.
|
||||
|
@ -436,7 +436,7 @@ class TestLib(TestCase):
|
|||
# Remove the thumb so that the test actually tests if the thumb will be created.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_create_thumb_empty_img(self):
|
||||
|
@ -454,7 +454,7 @@ class TestLib(TestCase):
|
|||
# last test.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Only continue when the thumb does not exist.
|
||||
|
@ -484,7 +484,7 @@ class TestLib(TestCase):
|
|||
# Remove the thumb so that the test actually tests if the thumb will be created.
|
||||
try:
|
||||
thumb_path.unlink()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@patch('openlp.core.lib.QtWidgets', MagicMock())
|
||||
|
|
|
@ -25,6 +25,9 @@ from unittest.mock import MagicMock, patch
|
|||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
# Mock QtWebEngineWidgets
|
||||
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
||||
|
||||
from openlp.core.app import OpenLP, parse_options
|
||||
from openlp.core.common.settings import Settings
|
||||
from tests.utils.constants import RESOURCE_PATH
|
||||
|
|
|
@ -412,7 +412,7 @@ class TestVLCPlayer(TestCase, TestMixin):
|
|||
|
||||
# WHEN: An audio CD is loaded into VLC
|
||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
patch.object(vlc_player, 'media_state_wait'):
|
||||
result = vlc_player.load(mocked_display)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
|
@ -457,7 +457,7 @@ class TestVLCPlayer(TestCase, TestMixin):
|
|||
|
||||
# WHEN: An audio CD is loaded into VLC
|
||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
patch.object(vlc_player, 'media_state_wait'):
|
||||
result = vlc_player.load(mocked_display)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
|
@ -501,8 +501,7 @@ class TestVLCPlayer(TestCase, TestMixin):
|
|||
vlc_player = VlcPlayer(None)
|
||||
|
||||
# WHEN: An audio CD is loaded into VLC
|
||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
with patch.object(vlc_player, 'volume'), patch.object(vlc_player, 'media_state_wait'):
|
||||
result = vlc_player.load(mocked_display)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
|
@ -657,7 +656,7 @@ class TestVLCPlayer(TestCase, TestMixin):
|
|||
|
||||
# WHEN: play() is called
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
|
||||
patch.object(vlc_player, 'volume') as mocked_volume:
|
||||
patch.object(vlc_player, 'volume'):
|
||||
mocked_media_state_wait.return_value = False
|
||||
result = vlc_player.play(mocked_display)
|
||||
|
||||
|
@ -690,7 +689,7 @@ class TestVLCPlayer(TestCase, TestMixin):
|
|||
vlc_player.set_state(MediaState.Paused, mocked_display)
|
||||
|
||||
# WHEN: play() is called
|
||||
with patch.object(vlc_player, 'media_state_wait', return_value=True) as mocked_media_state_wait, \
|
||||
with patch.object(vlc_player, 'media_state_wait', return_value=True), \
|
||||
patch.object(vlc_player, 'volume') as mocked_volume, \
|
||||
patch.object(vlc_player, 'get_live_state', return_value=MediaState.Loaded):
|
||||
result = vlc_player.play(mocked_display)
|
||||
|
|
|
@ -34,42 +34,30 @@ from openlp.core.ui import exceptionform
|
|||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
exceptionform.WEBKIT_VERSION = 'Webkit Test'
|
||||
exceptionform.MIGRATE_VERSION = 'Migrate Test'
|
||||
exceptionform.CHARDET_VERSION = 'CHARDET Test'
|
||||
exceptionform.ENCHANT_VERSION = 'Enchant Test'
|
||||
exceptionform.MAKO_VERSION = 'Mako Test'
|
||||
exceptionform.ICU_VERSION = 'ICU Test'
|
||||
exceptionform.VLC_VERSION = 'VLC Test'
|
||||
|
||||
MAIL_ITEM_TEXT = ('**OpenLP Bug Report**\nVersion: Trunk Test\n\n--- Details of the Exception. ---\n\n'
|
||||
'Description Test\n\n --- Exception Traceback ---\nopenlp: Traceback Test\n'
|
||||
'--- System information ---\nPlatform: Nose Test\n\n--- Library Versions ---\n'
|
||||
'Python: Python Test\nQt5: Qt5 Test\nPyQt5: PyQt5 Test\n'
|
||||
'SQLAlchemy: SQLAlchemy Test\nAlembic: Alembic Test\nBeautifulSoup: BeautifulSoup Test\n'
|
||||
'lxml: ETree Test\nChardet: Chardet Test\nPyEnchant: PyEnchant Test\nMako: Mako Test\n'
|
||||
'pyICU: pyICU Test\nVLC: VLC Test\nPyUNO: UNO Bridge Test\n')
|
||||
LIBRARY_VERSIONS = OrderedDict([
|
||||
('Python', 'Python Test'),
|
||||
('Qt5', 'Qt5 Test'),
|
||||
('PyQt5', 'PyQt5 Test'),
|
||||
('SQLAlchemy', 'SQLAlchemy Test'),
|
||||
('Alembic', 'Alembic Test'),
|
||||
('BeautifulSoup', 'BeautifulSoup Test'),
|
||||
('lxml', 'ETree Test'),
|
||||
('Chardet', 'Chardet Test'),
|
||||
('PyEnchant', 'PyEnchant Test'),
|
||||
('Mako', 'Mako Test'),
|
||||
('pyICU', 'pyICU Test'),
|
||||
('VLC', 'VLC Test')
|
||||
])
|
||||
'Python: Python Test\nQt5: Qt5 test\nPyQt5: PyQt5 Test\n'
|
||||
'SQLAlchemy: SqlAlchemy Test\nSQLAlchemy Migrate: Migrate Test\nBeautifulSoup: BeautifulSoup Test\n'
|
||||
'lxml: ETree Test\nChardet: CHARDET Test\nPyEnchant: Enchant Test\nMako: Mako Test\n'
|
||||
'pyUNO bridge: UNO Bridge Test\nVLC: VLC Test\n\n')
|
||||
|
||||
|
||||
@patch('openlp.core.ui.exceptionform.QtGui.QDesktopServices.openUrl')
|
||||
@patch('openlp.core.ui.exceptionform.get_version')
|
||||
@patch('openlp.core.ui.exceptionform.get_library_versions')
|
||||
@patch('openlp.core.ui.exceptionform.is_linux')
|
||||
@patch('openlp.core.ui.exceptionform.platform.platform')
|
||||
@patch("openlp.core.ui.exceptionform.Qt.qVersion")
|
||||
@patch("openlp.core.ui.exceptionform.QtGui.QDesktopServices.openUrl")
|
||||
@patch("openlp.core.ui.exceptionform.get_version")
|
||||
@patch("openlp.core.ui.exceptionform.sqlalchemy")
|
||||
@patch("openlp.core.ui.exceptionform.bs4")
|
||||
@patch("openlp.core.ui.exceptionform.etree")
|
||||
@patch("openlp.core.ui.exceptionform.is_linux")
|
||||
@patch("openlp.core.ui.exceptionform.platform.platform")
|
||||
@patch("openlp.core.ui.exceptionform.platform.python_version")
|
||||
class TestExceptionForm(TestMixin, TestCase):
|
||||
"""
|
||||
Test functionality of exception form functions
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
"""
|
||||
Package to test the openlp.core.ui.icons package.
|
||||
"""
|
||||
from unittest import TestCase, skipUnless
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from PyQt5 import QtGui
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
"""
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
"""
|
||||
import os
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
@ -151,7 +150,8 @@ class TestServiceManager(TestCase):
|
|||
service_item.add_capability(capability)
|
||||
service_item.service_item_type = ServiceItemType.Text
|
||||
service_item.edit_id = 1
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_item._display_slides = []
|
||||
service_item._display_slides.append(MagicMock())
|
||||
service_manager.service_items.insert(1, {'service_item': service_item})
|
||||
service_manager.edit_action = MagicMock()
|
||||
service_manager.rename_action = MagicMock()
|
||||
|
@ -184,7 +184,7 @@ class TestServiceManager(TestCase):
|
|||
assert service_manager.theme_menu.menuAction().setVisible.call_count == 2, \
|
||||
'Should have be called twice'
|
||||
# THEN we add a 2nd display frame
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_item._display_slides.append(MagicMock())
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
assert service_manager.auto_play_slides_menu.menuAction().setVisible.call_count == 2, \
|
||||
|
@ -215,7 +215,8 @@ class TestServiceManager(TestCase):
|
|||
service_item.add_capability(capability)
|
||||
service_item.service_item_type = ServiceItemType.Text
|
||||
service_item.edit_id = 1
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_item._display_slides = []
|
||||
service_item._display_slides.append(MagicMock())
|
||||
service_manager.service_items.insert(1, {'service_item': service_item})
|
||||
service_manager.edit_action = MagicMock()
|
||||
service_manager.rename_action = MagicMock()
|
||||
|
@ -248,7 +249,7 @@ class TestServiceManager(TestCase):
|
|||
assert service_manager.theme_menu.menuAction().setVisible.call_count == 2, \
|
||||
'Should have be called twice'
|
||||
# THEN we add a 2nd display frame
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_item._display_slides.append(MagicMock())
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
assert service_manager.auto_play_slides_menu.menuAction().setVisible.call_count == 2, \
|
||||
|
@ -280,7 +281,8 @@ class TestServiceManager(TestCase):
|
|||
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
|
||||
service_item.service_item_type = ServiceItemType.Text
|
||||
service_item.edit_id = 1
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_item._display_slides = []
|
||||
service_item._display_slides.append(MagicMock())
|
||||
service_manager.service_items.insert(1, {'service_item': service_item})
|
||||
service_manager.edit_action = MagicMock()
|
||||
service_manager.rename_action = MagicMock()
|
||||
|
@ -313,7 +315,7 @@ class TestServiceManager(TestCase):
|
|||
assert service_manager.theme_menu.menuAction().setVisible.call_count == 2, \
|
||||
'Should have be called twice'
|
||||
# THEN we add a 2nd display frame
|
||||
service_item._display_frames.append(MagicMock())
|
||||
service_item._display_slides.append(MagicMock())
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
assert service_manager.auto_play_slides_menu.menuAction().setVisible.call_count == 2, \
|
||||
|
@ -343,7 +345,7 @@ class TestServiceManager(TestCase):
|
|||
service_item.add_capability(ItemCapabilities.CanEditTitle)
|
||||
service_item.service_item_type = ServiceItemType.Image
|
||||
service_item.edit_id = 1
|
||||
service_item._raw_frames.append(MagicMock())
|
||||
service_item.slides.append(MagicMock())
|
||||
service_manager.service_items.insert(1, {'service_item': service_item})
|
||||
service_manager.edit_action = MagicMock()
|
||||
service_manager.rename_action = MagicMock()
|
||||
|
@ -376,7 +378,7 @@ class TestServiceManager(TestCase):
|
|||
assert service_manager.theme_menu.menuAction().setVisible.call_count == 1, \
|
||||
'Should have be called once'
|
||||
# THEN we add a 2nd display frame and regenerate the menu.
|
||||
service_item._raw_frames.append(MagicMock())
|
||||
service_item.slides.append(MagicMock())
|
||||
service_manager.context_menu(1)
|
||||
# THEN the following additional calls should have occurred.
|
||||
assert service_manager.auto_play_slides_menu.menuAction().setVisible.call_count == 2, \
|
||||
|
@ -404,7 +406,7 @@ class TestServiceManager(TestCase):
|
|||
service_item.add_capability(ItemCapabilities.RequiresMedia)
|
||||
service_item.service_item_type = ServiceItemType.Command
|
||||
service_item.edit_id = 1
|
||||
service_item._raw_frames.append(MagicMock())
|
||||
service_item.slides.append(MagicMock())
|
||||
service_manager.service_items.insert(1, {'service_item': service_item})
|
||||
service_manager.edit_action = MagicMock()
|
||||
service_manager.rename_action = MagicMock()
|
||||
|
@ -462,7 +464,7 @@ class TestServiceManager(TestCase):
|
|||
service_item.add_capability(ItemCapabilities.CanAppend)
|
||||
service_item.service_item_type = ServiceItemType.Command
|
||||
service_item.edit_id = 1
|
||||
service_item._raw_frames.append(MagicMock())
|
||||
service_item.slides.append(MagicMock())
|
||||
service_manager.service_items.insert(1, {'service_item': service_item})
|
||||
service_manager.edit_action = MagicMock()
|
||||
service_manager.rename_action = MagicMock()
|
||||
|
@ -512,7 +514,7 @@ class TestServiceManager(TestCase):
|
|||
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
|
||||
service_item.service_item_type = ServiceItemType.Command
|
||||
service_item.edit_id = 1
|
||||
service_item._raw_frames.append(MagicMock())
|
||||
service_item.slides.append(MagicMock())
|
||||
service_manager.service_items.insert(1, {'service_item': service_item})
|
||||
service_manager.edit_action = MagicMock()
|
||||
service_manager.rename_action = MagicMock()
|
||||
|
|
|
@ -25,7 +25,7 @@ This module contains tests for the CSV Bible importer.
|
|||
import csv
|
||||
from collections import namedtuple
|
||||
from unittest import TestCase
|
||||
from unittest.mock import ANY, MagicMock, PropertyMock, call, patch
|
||||
from unittest.mock import MagicMock, PropertyMock, call, patch
|
||||
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.core.lib.exceptions import ValidationError
|
||||
|
@ -132,35 +132,40 @@ class TestCSVImport(TestCase):
|
|||
# GIVEN: A mocked csv.reader which returns an iterator with test data
|
||||
test_data = [['1', 'Line 1', 'Data 1'], ['2', 'Line 2', 'Data 2'], ['3', 'Line 3', 'Data 3']]
|
||||
TestTuple = namedtuple('TestTuple', 'line_no line_description line_data')
|
||||
mocked_csv_file = MagicMock()
|
||||
mocked_enter_file = MagicMock()
|
||||
mocked_csv_file.open.return_value.__enter__.return_value = mocked_enter_file
|
||||
|
||||
with patch('openlp.plugins.bibles.lib.importers.csvbible.get_file_encoding',
|
||||
return_value={'encoding': 'utf-8', 'confidence': 0.99}),\
|
||||
patch('openlp.plugins.bibles.lib.importers.csvbible.Path.open', create=True) as mocked_open,\
|
||||
return_value={'encoding': 'utf-8', 'confidence': 0.99}), \
|
||||
patch('openlp.plugins.bibles.lib.importers.csvbible.csv.reader',
|
||||
return_value=iter(test_data)) as mocked_reader:
|
||||
|
||||
# WHEN: Calling the CSVBible parse_csv_file method with a file name and TestTuple
|
||||
result = CSVBible.parse_csv_file(Path('file.csv'), TestTuple)
|
||||
result = CSVBible.parse_csv_file(mocked_csv_file, TestTuple)
|
||||
|
||||
# THEN: A list of TestTuple instances with the parsed data should be returned
|
||||
assert result == [TestTuple('1', 'Line 1', 'Data 1'), TestTuple('2', 'Line 2', 'Data 2'),
|
||||
TestTuple('3', 'Line 3', 'Data 3')]
|
||||
mocked_open.assert_called_once_with('r', encoding='utf-8', newline='')
|
||||
mocked_reader.assert_called_once_with(ANY, delimiter=',', quotechar='"')
|
||||
mocked_csv_file.open.assert_called_once_with('r', encoding='utf-8', newline='')
|
||||
mocked_reader.assert_called_once_with(mocked_enter_file, delimiter=',', quotechar='"')
|
||||
|
||||
def test_parse_csv_file_oserror(self):
|
||||
"""
|
||||
Test the parse_csv_file() handles an OSError correctly
|
||||
"""
|
||||
# GIVEN: Mocked a mocked open object which raises an OSError
|
||||
mocked_csv_file = MagicMock()
|
||||
mocked_csv_file.__str__.return_value = 'file.csv'
|
||||
mocked_csv_file.open.side_effect = OSError()
|
||||
|
||||
with patch('openlp.plugins.bibles.lib.importers.csvbible.get_file_encoding',
|
||||
return_value={'encoding': 'utf-8', 'confidence': 0.99}),\
|
||||
patch('openlp.plugins.bibles.lib.importers.csvbible.Path.open', side_effect=OSError, create=True):
|
||||
return_value={'encoding': 'utf-8', 'confidence': 0.99}):
|
||||
|
||||
# WHEN: Calling CSVBible.parse_csv_file
|
||||
# THEN: A ValidationError should be raised
|
||||
with self.assertRaises(ValidationError) as context:
|
||||
CSVBible.parse_csv_file(Path('file.csv'), None)
|
||||
CSVBible.parse_csv_file(mocked_csv_file, None)
|
||||
assert context.exception.msg == 'Parsing "file.csv" failed'
|
||||
|
||||
def test_parse_csv_file_csverror(self):
|
||||
|
@ -168,15 +173,17 @@ class TestCSVImport(TestCase):
|
|||
Test the parse_csv_file() handles an csv.Error correctly
|
||||
"""
|
||||
# GIVEN: Mocked a csv.reader which raises an csv.Error
|
||||
mocked_csv_file = MagicMock()
|
||||
mocked_csv_file.__str__.return_value = 'file.csv'
|
||||
|
||||
with patch('openlp.plugins.bibles.lib.importers.csvbible.get_file_encoding',
|
||||
return_value={'encoding': 'utf-8', 'confidence': 0.99}),\
|
||||
patch('openlp.plugins.bibles.lib.importers.csvbible.Path.open', create=True),\
|
||||
patch('openlp.plugins.bibles.lib.importers.csvbible.csv.reader', side_effect=csv.Error):
|
||||
|
||||
# WHEN: Calling CSVBible.parse_csv_file
|
||||
# THEN: A ValidationError should be raised
|
||||
with self.assertRaises(ValidationError) as context:
|
||||
CSVBible.parse_csv_file(Path('file.csv'), None)
|
||||
CSVBible.parse_csv_file(mocked_csv_file, None)
|
||||
assert context.exception.msg == 'Parsing "file.csv" failed'
|
||||
|
||||
def test_process_books_stopped_import(self):
|
||||
|
|
|
@ -55,7 +55,7 @@ class TestLib(TestCase, TestMixin):
|
|||
|
||||
# WHEN: Calling get_reference_separator
|
||||
for key, value in separators.items():
|
||||
_ = lib.get_reference_separator(key)
|
||||
lib.get_reference_separator(key)
|
||||
|
||||
# THEN: get_reference_separator should return the correct separator
|
||||
assert separators[key] == value
|
||||
|
|
|
@ -756,7 +756,7 @@ class TestMediaItem(TestCase, TestMixin):
|
|||
# GIVEN: An instance of :class:`MediaManagerItem` and mocked media_item.settings and select_book_combo_box
|
||||
self.media_item.version_combo_box = MagicMock(**{'currentData.return_value': None})
|
||||
self.media_item.select_book_combo_box = MagicMock()
|
||||
with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible:
|
||||
with patch.object(self.media_item, 'initialise_advanced_bible'):
|
||||
|
||||
# WHEN: Calling on_version_combo_box_index_changed
|
||||
self.media_item.on_version_combo_box_index_changed()
|
||||
|
@ -774,7 +774,7 @@ class TestMediaItem(TestCase, TestMixin):
|
|||
mocked_bible_db.name = 'ABC'
|
||||
self.media_item.version_combo_box = MagicMock(**{'currentData.return_value': mocked_bible_db})
|
||||
self.media_item.select_book_combo_box = MagicMock()
|
||||
with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible:
|
||||
with patch.object(self.media_item, 'initialise_advanced_bible'):
|
||||
|
||||
# WHEN: Calling on_version_combo_box_index_changed
|
||||
self.media_item.on_version_combo_box_index_changed()
|
||||
|
@ -792,7 +792,7 @@ class TestMediaItem(TestCase, TestMixin):
|
|||
self.media_item.list_view = MagicMock(**{'count.return_value': 5})
|
||||
self.media_item.style_combo_box = MagicMock()
|
||||
self.media_item.select_book_combo_box = MagicMock()
|
||||
with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \
|
||||
with patch.object(self.media_item, 'initialise_advanced_bible'), \
|
||||
patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box') \
|
||||
as mocked_critical_error_message_box:
|
||||
|
||||
|
@ -815,7 +815,7 @@ class TestMediaItem(TestCase, TestMixin):
|
|||
self.media_item.list_view = MagicMock(**{'count.return_value': 5})
|
||||
self.media_item.style_combo_box = MagicMock()
|
||||
self.media_item.select_book_combo_box = MagicMock()
|
||||
with patch.object(self.media_item, 'initialise_advanced_bible') as mocked_initialise_advanced_bible, \
|
||||
with patch.object(self.media_item, 'initialise_advanced_bible'), \
|
||||
patch('openlp.plugins.bibles.lib.mediaitem.critical_error_message_box',
|
||||
return_value=QtWidgets.QMessageBox.No) as mocked_critical_error_message_box:
|
||||
|
||||
|
|
|
@ -22,14 +22,12 @@
|
|||
"""
|
||||
This module contains tests for the upgrade submodule of the Bibles plugin.
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from tempfile import mkdtemp
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
from openlp.core.common.settings import ProxyMode
|
||||
|
|
|
@ -30,7 +30,7 @@ from PyQt5 import QtCore
|
|||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.lib.serviceitem import ServiceItem
|
||||
from openlp.plugins.custom.lib import CustomMediaItem
|
||||
from openlp.plugins.custom.lib.mediaitem import CustomMediaItem
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ from PyQt5 import QtWidgets
|
|||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.plugins.images.lib import ImageTab
|
||||
from openlp.plugins.images.lib.imagetab import ImageTab
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ from tempfile import mkdtemp
|
|||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.core.common.settings import Settings
|
||||
from openlp.plugins.presentations.lib.impresscontroller import ImpressController, ImpressDocument, TextType
|
||||
from openlp.plugins.presentations.presentationplugin import __default_settings__
|
||||
|
|
|
@ -313,7 +313,7 @@ class TestLib(TestCase):
|
|||
# WHEN: Transposing it 1 down
|
||||
# THEN: An exception should be raised
|
||||
with self.assertRaises(ValueError) as err:
|
||||
new_chord = transpose_chord(chord, -1, 'english')
|
||||
transpose_chord(chord, -1, 'english')
|
||||
assert err.exception.args[0] == '\'T\' is not in list', \
|
||||
'ValueError exception should have been thrown for invalid chord'
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue