forked from openlp/openlp
HEAD
This commit is contained in:
commit
a4ffbce6c3
@ -24,7 +24,7 @@ The :mod:`common` module contains most of the components and libraries that make
|
||||
OpenLP work.
|
||||
"""
|
||||
import hashlib
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
@ -32,6 +32,7 @@ import sys
|
||||
import traceback
|
||||
from chardet.universaldetector import UniversalDetector
|
||||
from ipaddress import IPv4Address, IPv6Address, AddressValueError
|
||||
from pathlib import Path
|
||||
from shutil import which
|
||||
from subprocess import check_output, CalledProcessError, STDOUT
|
||||
|
||||
@ -79,6 +80,49 @@ def check_directory_exists(directory, do_not_log=False):
|
||||
log.exception('failed to check if directory exists or create directory')
|
||||
|
||||
|
||||
def extension_loader(glob_pattern, excluded_files=[]):
|
||||
"""
|
||||
A utility function to find and load OpenLP extensions, such as plugins, presentation and media controllers and
|
||||
importers.
|
||||
|
||||
:param glob_pattern: A glob pattern used to find the extension(s) to be imported. Should be relative to the
|
||||
application directory. i.e. openlp/plugins/*/*plugin.py
|
||||
:type glob_pattern: str
|
||||
|
||||
:param excluded_files: A list of file names to exclude that the glob pattern may find.
|
||||
:type excluded_files: list of strings
|
||||
|
||||
:return: None
|
||||
:rtype: None
|
||||
"""
|
||||
app_dir = Path(AppLocation.get_directory(AppLocation.AppDir)).parent
|
||||
for extension_path in app_dir.glob(glob_pattern):
|
||||
extension_path = extension_path.relative_to(app_dir)
|
||||
if extension_path.name in excluded_files:
|
||||
continue
|
||||
module_name = path_to_module(extension_path)
|
||||
try:
|
||||
importlib.import_module(module_name)
|
||||
except (ImportError, OSError):
|
||||
# On some platforms importing vlc.py might cause OSError exceptions. (e.g. Mac OS X)
|
||||
log.warning('Failed to import {module_name} on path {extension_path}'
|
||||
.format(module_name=module_name, extension_path=str(extension_path)))
|
||||
|
||||
|
||||
def path_to_module(path):
|
||||
"""
|
||||
Convert a path to a module name (i.e openlp.core.common)
|
||||
|
||||
:param path: The path to convert to a module name.
|
||||
:type path: Path
|
||||
|
||||
:return: The module name.
|
||||
:rtype: str
|
||||
"""
|
||||
module_path = path.with_suffix('')
|
||||
return '.'.join(module_path.parts)
|
||||
|
||||
|
||||
def get_frozen_path(frozen_option, non_frozen_option):
|
||||
"""
|
||||
Return a path based on the system status.
|
||||
@ -398,7 +442,7 @@ def check_binary_exists(program_path):
|
||||
"""
|
||||
Function that checks whether a binary exists.
|
||||
|
||||
:param program_path:The full path to the binary to check.
|
||||
:param program_path: The full path to the binary to check.
|
||||
:return: program output to be parsed
|
||||
"""
|
||||
log.debug('testing program_path: {text}'.format(text=program_path))
|
||||
|
@ -621,5 +621,5 @@ from .imagemanager import ImageManager
|
||||
from .renderer import Renderer
|
||||
from .mediamanageritem import MediaManagerItem
|
||||
from .projector.db import ProjectorDB, Projector
|
||||
from .projector.pjlink1 import PJLink1
|
||||
from .projector.pjlink1 import PJLink
|
||||
from .projector.constants import PJLINK_PORT, ERROR_MSG, ERROR_STRING
|
||||
|
@ -23,10 +23,9 @@
|
||||
Provide plugin management
|
||||
"""
|
||||
import os
|
||||
import imp
|
||||
|
||||
from openlp.core.lib import Plugin, PluginStatus
|
||||
from openlp.core.common import AppLocation, RegistryProperties, OpenLPMixin, RegistryMixin
|
||||
from openlp.core.common import AppLocation, RegistryProperties, OpenLPMixin, RegistryMixin, extension_loader
|
||||
|
||||
|
||||
class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
|
||||
@ -70,32 +69,8 @@ class PluginManager(RegistryMixin, OpenLPMixin, RegistryProperties):
|
||||
"""
|
||||
Scan a directory for objects inheriting from the ``Plugin`` class.
|
||||
"""
|
||||
start_depth = len(os.path.abspath(self.base_path).split(os.sep))
|
||||
present_plugin_dir = os.path.join(self.base_path, 'presentations')
|
||||
self.log_debug('finding plugins in {path} at depth {depth:d}'.format(path=self.base_path, depth=start_depth))
|
||||
for root, dirs, files in os.walk(self.base_path):
|
||||
for name in files:
|
||||
if name.endswith('.py') and not name.startswith('__'):
|
||||
path = os.path.abspath(os.path.join(root, name))
|
||||
this_depth = len(path.split(os.sep))
|
||||
if this_depth - start_depth > 2:
|
||||
# skip anything lower down
|
||||
break
|
||||
module_name = name[:-3]
|
||||
# import the modules
|
||||
self.log_debug('Importing {name} from {root}. Depth {depth:d}'.format(name=module_name,
|
||||
root=root,
|
||||
depth=this_depth))
|
||||
try:
|
||||
# Use the "imp" library to try to get around a problem with the PyUNO library which
|
||||
# monkey-patches the __import__ function to do some magic. This causes issues with our tests.
|
||||
# First, try to find the module we want to import, searching the directory in root
|
||||
fp, path_name, description = imp.find_module(module_name, [root])
|
||||
# Then load the module (do the actual import) using the details from find_module()
|
||||
imp.load_module(module_name, fp, path_name, description)
|
||||
except ImportError as e:
|
||||
self.log_exception('Failed to import module {name} on path {path}: '
|
||||
'{args}'.format(name=module_name, path=path, args=e.args[0]))
|
||||
glob_pattern = os.path.join('openlp', 'plugins', '*', '*plugin.py')
|
||||
extension_loader(glob_pattern)
|
||||
plugin_classes = Plugin.__subclasses__()
|
||||
plugin_objects = []
|
||||
for p in plugin_classes:
|
||||
|
@ -150,11 +150,15 @@ class Projector(CommonBase, Base):
|
||||
name: Column(String(20))
|
||||
location: Column(String(30))
|
||||
notes: Column(String(200))
|
||||
pjlink_name: Column(String(128)) # From projector (future)
|
||||
manufacturer: Column(String(128)) # From projector (future)
|
||||
model: Column(String(128)) # From projector (future)
|
||||
other: Column(String(128)) # From projector (future)
|
||||
sources: Column(String(128)) # From projector (future)
|
||||
pjlink_name: Column(String(128)) # From projector
|
||||
manufacturer: Column(String(128)) # From projector
|
||||
model: Column(String(128)) # From projector
|
||||
other: Column(String(128)) # From projector
|
||||
sources: Column(String(128)) # From projector
|
||||
serial_no: Column(String(30)) # From projector (Class 2)
|
||||
sw_version: Column(String(30)) # From projector (Class 2)
|
||||
model_filter: Column(String(30)) # From projector (Class 2)
|
||||
model_lamp: Column(String(30)) # From projector (Class 2)
|
||||
|
||||
ProjectorSource relates
|
||||
"""
|
||||
@ -164,20 +168,25 @@ class Projector(CommonBase, Base):
|
||||
"""
|
||||
return '< Projector(id="{data}", ip="{ip}", port="{port}", pin="{pin}", name="{name}", ' \
|
||||
'location="{location}", notes="{notes}", pjlink_name="{pjlink_name}", ' \
|
||||
'manufacturer="{manufacturer}", model="{model}", other="{other}", ' \
|
||||
'sources="{sources}", source_list="{source_list}") >'.format(data=self.id,
|
||||
ip=self.ip,
|
||||
port=self.port,
|
||||
pin=self.pin,
|
||||
name=self.name,
|
||||
location=self.location,
|
||||
notes=self.notes,
|
||||
pjlink_name=self.pjlink_name,
|
||||
manufacturer=self.manufacturer,
|
||||
model=self.model,
|
||||
other=self.other,
|
||||
sources=self.sources,
|
||||
source_list=self.source_list)
|
||||
'manufacturer="{manufacturer}", model="{model}", serial_no="{serial}", other="{other}", ' \
|
||||
'sources="{sources}", source_list="{source_list}", model_filter="{mfilter}", ' \
|
||||
'model_lamp="{mlamp}", sw_version="{sw_ver}") >'.format(data=self.id,
|
||||
ip=self.ip,
|
||||
port=self.port,
|
||||
pin=self.pin,
|
||||
name=self.name,
|
||||
location=self.location,
|
||||
notes=self.notes,
|
||||
pjlink_name=self.pjlink_name,
|
||||
manufacturer=self.manufacturer,
|
||||
model=self.model,
|
||||
other=self.other,
|
||||
sources=self.sources,
|
||||
source_list=self.source_list,
|
||||
serial=self.serial_no,
|
||||
mfilter=self.model_filter,
|
||||
mlamp=self.model_lamp,
|
||||
sw_ver=self.sw_version)
|
||||
ip = Column(String(100))
|
||||
port = Column(String(8))
|
||||
pin = Column(String(20))
|
||||
@ -189,6 +198,10 @@ class Projector(CommonBase, Base):
|
||||
model = Column(String(128))
|
||||
other = Column(String(128))
|
||||
sources = Column(String(128))
|
||||
serial_no = Column(String(30))
|
||||
sw_version = Column(String(30))
|
||||
model_filter = Column(String(30))
|
||||
model_lamp = Column(String(30))
|
||||
source_list = relationship('ProjectorSource',
|
||||
order_by='ProjectorSource.code',
|
||||
backref='projector',
|
||||
@ -359,6 +372,10 @@ class ProjectorDB(Manager):
|
||||
old_projector.model = projector.model
|
||||
old_projector.other = projector.other
|
||||
old_projector.sources = projector.sources
|
||||
old_projector.serial_no = projector.serial_no
|
||||
old_projector.sw_version = projector.sw_version
|
||||
old_projector.model_filter = projector.model_filter
|
||||
old_projector.model_lamp = projector.model_lamp
|
||||
return self.save_object(old_projector)
|
||||
|
||||
def delete_projector(self, projector):
|
||||
|
@ -42,7 +42,7 @@ log = logging.getLogger(__name__)
|
||||
|
||||
log.debug('pjlink1 loaded')
|
||||
|
||||
__all__ = ['PJLink1']
|
||||
__all__ = ['PJLink']
|
||||
|
||||
from codecs import decode
|
||||
|
||||
@ -68,7 +68,7 @@ PJLINK_HEADER = '{prefix}{{linkclass}}'.format(prefix=PJLINK_PREFIX)
|
||||
PJLINK_SUFFIX = CR
|
||||
|
||||
|
||||
class PJLink1(QtNetwork.QTcpSocket):
|
||||
class PJLink(QtNetwork.QTcpSocket):
|
||||
"""
|
||||
Socket service for connecting to a PJLink-capable projector.
|
||||
"""
|
||||
@ -129,7 +129,7 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
self.pin = pin
|
||||
super(PJLink1, self).__init__()
|
||||
super(PJLink, self).__init__()
|
||||
self.dbid = None
|
||||
self.location = None
|
||||
self.notes = None
|
||||
@ -162,7 +162,7 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
# Socket timer for some possible brain-dead projectors or network cable pulled
|
||||
self.socket_timer = None
|
||||
# Map command to function
|
||||
self.pjlink1_functions = {
|
||||
self.pjlink_functions = {
|
||||
'AVMT': self.process_avmt,
|
||||
'CLSS': self.process_clss,
|
||||
'ERST': self.process_erst,
|
||||
@ -286,7 +286,7 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
elif status in STATUS_STRING:
|
||||
return STATUS_STRING[status], ERROR_MSG[status]
|
||||
else:
|
||||
return status, translate('OpenLP.PJLink1', 'Unknown status')
|
||||
return status, translate('OpenLP.PJLink', 'Unknown status')
|
||||
|
||||
def change_status(self, status, msg=None):
|
||||
"""
|
||||
@ -296,7 +296,7 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
:param status: Status code
|
||||
:param msg: Optional message
|
||||
"""
|
||||
message = translate('OpenLP.PJLink1', 'No message') if msg is None else msg
|
||||
message = translate('OpenLP.PJLink', 'No message') if msg is None else msg
|
||||
(code, message) = self._get_status(status)
|
||||
if msg is not None:
|
||||
message = msg
|
||||
@ -576,7 +576,7 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
if sent == -1:
|
||||
# Network error?
|
||||
self.change_status(E_NETWORK,
|
||||
translate('OpenLP.PJLink1', 'Error while sending data to projector'))
|
||||
translate('OpenLP.PJLink', 'Error while sending data to projector'))
|
||||
|
||||
def process_command(self, cmd, data):
|
||||
"""
|
||||
@ -625,8 +625,9 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
self.projectorReceivedData.emit()
|
||||
return
|
||||
|
||||
if cmd in self.pjlink1_functions:
|
||||
self.pjlink1_functions[cmd](data)
|
||||
if cmd in self.pjlink_functions:
|
||||
log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd))
|
||||
self.pjlink_functions[cmd](data)
|
||||
else:
|
||||
log.warning('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd))
|
||||
self.send_busy = False
|
||||
@ -662,6 +663,7 @@ class PJLink1(QtNetwork.QTcpSocket):
|
||||
|
||||
:param data: Power status
|
||||
"""
|
||||
log.debug('({ip}: Processing POWR command'.format(ip=self.ip))
|
||||
if data in PJLINK_POWR_STATUS:
|
||||
power = PJLINK_POWR_STATUS[data]
|
||||
update_icons = self.power != power
|
||||
|
@ -164,7 +164,7 @@ class ThemeXML(object):
|
||||
jsn = get_text_file_string(json_file)
|
||||
jsn = json.loads(jsn)
|
||||
self.expand_json(jsn)
|
||||
self.background_filename = None
|
||||
self.background_filename = ''
|
||||
|
||||
def expand_json(self, var, prev=None):
|
||||
"""
|
||||
|
@ -25,13 +25,13 @@ The :mod:`advancedtab` provides an advanced settings facility.
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common import AppLocation, Settings, SlideLimits, UiStrings, translate
|
||||
from openlp.core.lib import SettingsTab, build_icon
|
||||
from openlp.core.common.languagemanager import format_time
|
||||
from openlp.core.lib import SettingsTab, build_icon
|
||||
from openlp.core.ui.lib import PathEdit, PathType
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -153,32 +153,17 @@ class AdvancedTab(SettingsTab):
|
||||
self.data_directory_group_box.setObjectName('data_directory_group_box')
|
||||
self.data_directory_layout = QtWidgets.QFormLayout(self.data_directory_group_box)
|
||||
self.data_directory_layout.setObjectName('data_directory_layout')
|
||||
self.data_directory_current_label = QtWidgets.QLabel(self.data_directory_group_box)
|
||||
self.data_directory_current_label.setObjectName('data_directory_current_label')
|
||||
self.data_directory_label = QtWidgets.QLabel(self.data_directory_group_box)
|
||||
self.data_directory_label.setObjectName('data_directory_label')
|
||||
self.data_directory_new_label = QtWidgets.QLabel(self.data_directory_group_box)
|
||||
self.data_directory_new_label.setObjectName('data_directory_current_label')
|
||||
self.new_data_directory_edit = QtWidgets.QLineEdit(self.data_directory_group_box)
|
||||
self.new_data_directory_edit.setObjectName('new_data_directory_edit')
|
||||
self.new_data_directory_edit.setReadOnly(True)
|
||||
self.data_directory_path_edit = PathEdit(self.data_directory_group_box, path_type=PathType.Directories,
|
||||
default_path=AppLocation.get_directory(AppLocation.DataDir))
|
||||
self.data_directory_layout.addRow(self.data_directory_new_label, self.data_directory_path_edit)
|
||||
self.new_data_directory_has_files_label = QtWidgets.QLabel(self.data_directory_group_box)
|
||||
self.new_data_directory_has_files_label.setObjectName('new_data_directory_has_files_label')
|
||||
self.new_data_directory_has_files_label.setWordWrap(True)
|
||||
self.data_directory_browse_button = QtWidgets.QToolButton(self.data_directory_group_box)
|
||||
self.data_directory_browse_button.setObjectName('data_directory_browse_button')
|
||||
self.data_directory_browse_button.setIcon(build_icon(':/general/general_open.png'))
|
||||
self.data_directory_default_button = QtWidgets.QToolButton(self.data_directory_group_box)
|
||||
self.data_directory_default_button.setObjectName('data_directory_default_button')
|
||||
self.data_directory_default_button.setIcon(build_icon(':/general/general_revert.png'))
|
||||
self.data_directory_cancel_button = QtWidgets.QToolButton(self.data_directory_group_box)
|
||||
self.data_directory_cancel_button.setObjectName('data_directory_cancel_button')
|
||||
self.data_directory_cancel_button.setIcon(build_icon(':/general/general_delete.png'))
|
||||
self.new_data_directory_label_layout = QtWidgets.QHBoxLayout()
|
||||
self.new_data_directory_label_layout.setObjectName('new_data_directory_label_layout')
|
||||
self.new_data_directory_label_layout.addWidget(self.new_data_directory_edit)
|
||||
self.new_data_directory_label_layout.addWidget(self.data_directory_browse_button)
|
||||
self.new_data_directory_label_layout.addWidget(self.data_directory_default_button)
|
||||
self.data_directory_copy_check_layout = QtWidgets.QHBoxLayout()
|
||||
self.data_directory_copy_check_layout.setObjectName('data_directory_copy_check_layout')
|
||||
self.data_directory_copy_check_box = QtWidgets.QCheckBox(self.data_directory_group_box)
|
||||
@ -186,8 +171,6 @@ class AdvancedTab(SettingsTab):
|
||||
self.data_directory_copy_check_layout.addWidget(self.data_directory_copy_check_box)
|
||||
self.data_directory_copy_check_layout.addStretch()
|
||||
self.data_directory_copy_check_layout.addWidget(self.data_directory_cancel_button)
|
||||
self.data_directory_layout.addRow(self.data_directory_current_label, self.data_directory_label)
|
||||
self.data_directory_layout.addRow(self.data_directory_new_label, self.new_data_directory_label_layout)
|
||||
self.data_directory_layout.addRow(self.data_directory_copy_check_layout)
|
||||
self.data_directory_layout.addRow(self.new_data_directory_has_files_label)
|
||||
self.left_layout.addWidget(self.data_directory_group_box)
|
||||
@ -239,8 +222,7 @@ class AdvancedTab(SettingsTab):
|
||||
self.service_name_edit.textChanged.connect(self.update_service_name_example)
|
||||
self.service_name_revert_button.clicked.connect(self.on_service_name_revert_button_clicked)
|
||||
self.alternate_rows_check_box.toggled.connect(self.on_alternate_rows_check_box_toggled)
|
||||
self.data_directory_browse_button.clicked.connect(self.on_data_directory_browse_button_clicked)
|
||||
self.data_directory_default_button.clicked.connect(self.on_data_directory_default_button_clicked)
|
||||
self.data_directory_path_edit.pathChanged.connect(self.on_data_directory_path_edit_path_changed)
|
||||
self.data_directory_cancel_button.clicked.connect(self.on_data_directory_cancel_button_clicked)
|
||||
self.data_directory_copy_check_box.toggled.connect(self.on_data_directory_copy_check_box_toggled)
|
||||
self.end_slide_radio_button.clicked.connect(self.on_end_slide_button_clicked)
|
||||
@ -317,12 +299,7 @@ class AdvancedTab(SettingsTab):
|
||||
self.service_name_example_label.setText(translate('OpenLP.AdvancedTab', 'Example:'))
|
||||
self.hide_mouse_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Mouse Cursor'))
|
||||
self.hide_mouse_check_box.setText(translate('OpenLP.AdvancedTab', 'Hide mouse cursor when over display window'))
|
||||
self.data_directory_current_label.setText(translate('OpenLP.AdvancedTab', 'Current path:'))
|
||||
self.data_directory_new_label.setText(translate('OpenLP.AdvancedTab', 'Custom path:'))
|
||||
self.data_directory_browse_button.setToolTip(translate('OpenLP.AdvancedTab',
|
||||
'Browse for new data file location.'))
|
||||
self.data_directory_default_button.setToolTip(
|
||||
translate('OpenLP.AdvancedTab', 'Set the data location to the default.'))
|
||||
self.data_directory_new_label.setText(translate('OpenLP.AdvancedTab', 'Path:'))
|
||||
self.data_directory_cancel_button.setText(translate('OpenLP.AdvancedTab', 'Cancel'))
|
||||
self.data_directory_cancel_button.setToolTip(
|
||||
translate('OpenLP.AdvancedTab', 'Cancel OpenLP data directory location change.'))
|
||||
@ -396,8 +373,7 @@ class AdvancedTab(SettingsTab):
|
||||
self.new_data_directory_has_files_label.hide()
|
||||
self.data_directory_cancel_button.hide()
|
||||
# Since data location can be changed, make sure the path is present.
|
||||
self.current_data_path = AppLocation.get_data_path()
|
||||
self.data_directory_label.setText(os.path.abspath(self.current_data_path))
|
||||
self.data_directory_path_edit.path = AppLocation.get_data_path()
|
||||
# Don't allow data directory move if running portable.
|
||||
if settings.value('advanced/is portable'):
|
||||
self.data_directory_group_box.hide()
|
||||
@ -509,24 +485,10 @@ class AdvancedTab(SettingsTab):
|
||||
self.service_name_edit.setText(UiStrings().DefaultServiceName)
|
||||
self.service_name_edit.setFocus()
|
||||
|
||||
def on_data_directory_browse_button_clicked(self):
|
||||
def on_data_directory_path_edit_path_changed(self, new_data_path):
|
||||
"""
|
||||
Browse for a new data directory location.
|
||||
"""
|
||||
old_root_path = str(self.data_directory_label.text())
|
||||
# Get the new directory location.
|
||||
new_data_path = QtWidgets.QFileDialog.getExistingDirectory(self, translate('OpenLP.AdvancedTab',
|
||||
'Select Data Directory Location'),
|
||||
old_root_path,
|
||||
options=QtWidgets.QFileDialog.ShowDirsOnly)
|
||||
# Set the new data path.
|
||||
if new_data_path:
|
||||
new_data_path = os.path.normpath(new_data_path)
|
||||
if self.current_data_path.lower() == new_data_path.lower():
|
||||
self.on_data_directory_cancel_button_clicked()
|
||||
return
|
||||
else:
|
||||
return
|
||||
# Make sure they want to change the data.
|
||||
answer = QtWidgets.QMessageBox.question(self, translate('OpenLP.AdvancedTab', 'Confirm Data Directory Change'),
|
||||
translate('OpenLP.AdvancedTab', 'Are you sure you want to change the '
|
||||
@ -535,40 +497,14 @@ class AdvancedTab(SettingsTab):
|
||||
'closed.').format(path=new_data_path),
|
||||
defaultButton=QtWidgets.QMessageBox.No)
|
||||
if answer != QtWidgets.QMessageBox.Yes:
|
||||
self.data_directory_path_edit.path = AppLocation.get_data_path()
|
||||
return
|
||||
# Check if data already exists here.
|
||||
self.check_data_overwrite(new_data_path)
|
||||
# Save the new location.
|
||||
self.main_window.set_new_data_path(new_data_path)
|
||||
self.new_data_directory_edit.setText(new_data_path)
|
||||
self.data_directory_cancel_button.show()
|
||||
|
||||
def on_data_directory_default_button_clicked(self):
|
||||
"""
|
||||
Re-set the data directory location to the 'default' location.
|
||||
"""
|
||||
new_data_path = AppLocation.get_directory(AppLocation.DataDir)
|
||||
if self.current_data_path.lower() != new_data_path.lower():
|
||||
# Make sure they want to change the data location back to the
|
||||
# default.
|
||||
answer = QtWidgets.QMessageBox.question(self, translate('OpenLP.AdvancedTab', 'Reset Data Directory'),
|
||||
translate('OpenLP.AdvancedTab', 'Are you sure you want to change '
|
||||
'the location of the OpenLP data '
|
||||
'directory to the default location?'
|
||||
'\n\nThis location will be used '
|
||||
'after OpenLP is closed.'),
|
||||
defaultButton=QtWidgets.QMessageBox.No)
|
||||
if answer != QtWidgets.QMessageBox.Yes:
|
||||
return
|
||||
self.check_data_overwrite(new_data_path)
|
||||
# Save the new location.
|
||||
self.main_window.set_new_data_path(new_data_path)
|
||||
self.new_data_directory_edit.setText(os.path.abspath(new_data_path))
|
||||
self.data_directory_cancel_button.show()
|
||||
else:
|
||||
# We cancel the change in case user changed their mind.
|
||||
self.on_data_directory_cancel_button_clicked()
|
||||
|
||||
def on_data_directory_copy_check_box_toggled(self):
|
||||
"""
|
||||
Copy existing data when you change your data directory.
|
||||
@ -585,7 +521,6 @@ class AdvancedTab(SettingsTab):
|
||||
Check if there's already data in the target directory.
|
||||
"""
|
||||
test_path = os.path.join(data_path, 'songs')
|
||||
self.data_directory_copy_check_box.show()
|
||||
if os.path.exists(test_path):
|
||||
self.data_exists = True
|
||||
# Check is they want to replace existing data.
|
||||
@ -599,6 +534,7 @@ class AdvancedTab(SettingsTab):
|
||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
|
||||
QtWidgets.QMessageBox.No),
|
||||
QtWidgets.QMessageBox.No)
|
||||
self.data_directory_copy_check_box.show()
|
||||
if answer == QtWidgets.QMessageBox.Yes:
|
||||
self.data_directory_copy_check_box.setChecked(True)
|
||||
self.new_data_directory_has_files_label.show()
|
||||
@ -614,7 +550,7 @@ class AdvancedTab(SettingsTab):
|
||||
"""
|
||||
Cancel the data directory location change
|
||||
"""
|
||||
self.new_data_directory_edit.clear()
|
||||
self.data_directory_path_edit.path = AppLocation.get_data_path()
|
||||
self.data_directory_copy_check_box.setChecked(False)
|
||||
self.main_window.set_new_data_path(None)
|
||||
self.main_window.set_copy_data(False)
|
||||
|
@ -27,8 +27,8 @@ import logging
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from openlp.core.common import Registry, Settings, UiStrings, translate, get_images_filter
|
||||
from openlp.core.lib import SettingsTab, ScreenList, build_icon
|
||||
from openlp.core.ui.lib.colorbutton import ColorButton
|
||||
from openlp.core.lib import SettingsTab, ScreenList
|
||||
from openlp.core.ui.lib import ColorButton, PathEdit
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -172,20 +172,8 @@ class GeneralTab(SettingsTab):
|
||||
self.logo_layout.setObjectName('logo_layout')
|
||||
self.logo_file_label = QtWidgets.QLabel(self.logo_group_box)
|
||||
self.logo_file_label.setObjectName('logo_file_label')
|
||||
self.logo_file_edit = QtWidgets.QLineEdit(self.logo_group_box)
|
||||
self.logo_file_edit.setObjectName('logo_file_edit')
|
||||
self.logo_browse_button = QtWidgets.QToolButton(self.logo_group_box)
|
||||
self.logo_browse_button.setObjectName('logo_browse_button')
|
||||
self.logo_browse_button.setIcon(build_icon(':/general/general_open.png'))
|
||||
self.logo_revert_button = QtWidgets.QToolButton(self.logo_group_box)
|
||||
self.logo_revert_button.setObjectName('logo_revert_button')
|
||||
self.logo_revert_button.setIcon(build_icon(':/general/general_revert.png'))
|
||||
self.logo_file_layout = QtWidgets.QHBoxLayout()
|
||||
self.logo_file_layout.setObjectName('logo_file_layout')
|
||||
self.logo_file_layout.addWidget(self.logo_file_edit)
|
||||
self.logo_file_layout.addWidget(self.logo_browse_button)
|
||||
self.logo_file_layout.addWidget(self.logo_revert_button)
|
||||
self.logo_layout.addRow(self.logo_file_label, self.logo_file_layout)
|
||||
self.logo_file_path_edit = PathEdit(self.logo_group_box, default_path=':/graphics/openlp-splash-screen.png')
|
||||
self.logo_layout.addRow(self.logo_file_label, self.logo_file_path_edit)
|
||||
self.logo_color_label = QtWidgets.QLabel(self.logo_group_box)
|
||||
self.logo_color_label.setObjectName('logo_color_label')
|
||||
self.logo_color_button = ColorButton(self.logo_group_box)
|
||||
@ -196,8 +184,6 @@ class GeneralTab(SettingsTab):
|
||||
self.logo_layout.addRow(self.logo_hide_on_startup_check_box)
|
||||
self.right_layout.addWidget(self.logo_group_box)
|
||||
self.logo_color_button.colorChanged.connect(self.on_logo_background_color_changed)
|
||||
self.logo_browse_button.clicked.connect(self.on_logo_browse_button_clicked)
|
||||
self.logo_revert_button.clicked.connect(self.on_logo_revert_button_clicked)
|
||||
# Application Settings
|
||||
self.settings_group_box = QtWidgets.QGroupBox(self.right_column)
|
||||
self.settings_group_box.setObjectName('settings_group_box')
|
||||
@ -254,8 +240,6 @@ class GeneralTab(SettingsTab):
|
||||
self.logo_group_box.setTitle(translate('OpenLP.GeneralTab', 'Logo'))
|
||||
self.logo_color_label.setText(UiStrings().BackgroundColorColon)
|
||||
self.logo_file_label.setText(translate('OpenLP.GeneralTab', 'Logo file:'))
|
||||
self.logo_browse_button.setToolTip(translate('OpenLP.GeneralTab', 'Browse for an image file to display.'))
|
||||
self.logo_revert_button.setToolTip(translate('OpenLP.GeneralTab', 'Revert to the default OpenLP logo.'))
|
||||
self.logo_hide_on_startup_check_box.setText(translate('OpenLP.GeneralTab', 'Don\'t show logo on startup'))
|
||||
self.check_for_updates_check_box.setText(translate('OpenLP.GeneralTab', 'Check for updates to OpenLP'))
|
||||
self.settings_group_box.setTitle(translate('OpenLP.GeneralTab', 'Application Settings'))
|
||||
@ -282,6 +266,9 @@ class GeneralTab(SettingsTab):
|
||||
self.audio_group_box.setTitle(translate('OpenLP.GeneralTab', 'Background Audio'))
|
||||
self.start_paused_check_box.setText(translate('OpenLP.GeneralTab', 'Start background audio paused'))
|
||||
self.repeat_list_check_box.setText(translate('OpenLP.GeneralTab', 'Repeat track list'))
|
||||
self.logo_file_path_edit.dialog_caption = dialog_caption = translate('OpenLP.AdvancedTab', 'Select Logo File')
|
||||
self.logo_file_path_edit.filters = '{text};;{names} (*)'.format(
|
||||
text=get_images_filter(), names=UiStrings().AllFiles)
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
@ -304,7 +291,7 @@ class GeneralTab(SettingsTab):
|
||||
self.auto_open_check_box.setChecked(settings.value('auto open'))
|
||||
self.show_splash_check_box.setChecked(settings.value('show splash'))
|
||||
self.logo_background_color = settings.value('logo background color')
|
||||
self.logo_file_edit.setText(settings.value('logo file'))
|
||||
self.logo_file_path_edit.path = settings.value('logo file')
|
||||
self.logo_hide_on_startup_check_box.setChecked(settings.value('logo hide on startup'))
|
||||
self.logo_color_button.color = self.logo_background_color
|
||||
self.check_for_updates_check_box.setChecked(settings.value('update check'))
|
||||
@ -338,7 +325,7 @@ class GeneralTab(SettingsTab):
|
||||
settings.setValue('auto open', self.auto_open_check_box.isChecked())
|
||||
settings.setValue('show splash', self.show_splash_check_box.isChecked())
|
||||
settings.setValue('logo background color', self.logo_background_color)
|
||||
settings.setValue('logo file', self.logo_file_edit.text())
|
||||
settings.setValue('logo file', self.logo_file_path_edit.path)
|
||||
settings.setValue('logo hide on startup', self.logo_hide_on_startup_check_box.isChecked())
|
||||
settings.setValue('update check', self.check_for_updates_check_box.isChecked())
|
||||
settings.setValue('save prompt', self.save_check_service_check_box.isChecked())
|
||||
@ -404,25 +391,6 @@ class GeneralTab(SettingsTab):
|
||||
"""
|
||||
self.display_changed = True
|
||||
|
||||
def on_logo_browse_button_clicked(self):
|
||||
"""
|
||||
Select the logo file
|
||||
"""
|
||||
file_filters = '{text};;{names} (*.*)'.format(text=get_images_filter(), names=UiStrings().AllFiles)
|
||||
filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(self,
|
||||
translate('OpenLP.AdvancedTab', 'Open File'), '',
|
||||
file_filters)
|
||||
if filename:
|
||||
self.logo_file_edit.setText(filename)
|
||||
self.logo_file_edit.setFocus()
|
||||
|
||||
def on_logo_revert_button_clicked(self):
|
||||
"""
|
||||
Revert the logo file back to the default setting.
|
||||
"""
|
||||
self.logo_file_edit.setText(':/graphics/openlp-splash-screen.png')
|
||||
self.logo_file_edit.setFocus()
|
||||
|
||||
def on_logo_background_color_changed(self, color):
|
||||
"""
|
||||
Select the background color for logo.
|
||||
|
@ -21,14 +21,16 @@
|
||||
###############################################################################
|
||||
|
||||
from .colorbutton import ColorButton
|
||||
from .listwidgetwithdnd import ListWidgetWithDnD
|
||||
from .treewidgetwithdnd import TreeWidgetWithDnD
|
||||
from .toolbar import OpenLPToolbar
|
||||
from .dockwidget import OpenLPDockWidget
|
||||
from .wizard import OpenLPWizard, WizardStrings
|
||||
from .mediadockmanager import MediaDockManager
|
||||
from .listpreviewwidget import ListPreviewWidget
|
||||
from .listwidgetwithdnd import ListWidgetWithDnD
|
||||
from .mediadockmanager import MediaDockManager
|
||||
from .dockwidget import OpenLPDockWidget
|
||||
from .toolbar import OpenLPToolbar
|
||||
from .wizard import OpenLPWizard, WizardStrings
|
||||
from .pathedit import PathEdit, PathType
|
||||
from .spelltextedit import SpellTextEdit
|
||||
from .treewidgetwithdnd import TreeWidgetWithDnD
|
||||
|
||||
__all__ = ['ColorButton', 'ListPreviewWidget', 'ListWidgetWithDnD', 'OpenLPToolbar', 'OpenLPDockWidget',
|
||||
'OpenLPWizard', 'WizardStrings', 'MediaDockManager', 'ListPreviewWidget', 'SpellTextEdit']
|
||||
__all__ = ['ColorButton', 'ListPreviewWidget', 'ListWidgetWithDnD', 'MediaDockManager', 'OpenLPDockWidget',
|
||||
'OpenLPToolbar', 'OpenLPWizard', 'PathEdit', 'PathType', 'SpellTextEdit', 'TreeWidgetWithDnD',
|
||||
'WizardStrings']
|
||||
|
@ -39,7 +39,7 @@ class ColorButton(QtWidgets.QPushButton):
|
||||
"""
|
||||
Initialise the ColorButton
|
||||
"""
|
||||
super(ColorButton, self).__init__()
|
||||
super().__init__(parent)
|
||||
self.parent = parent
|
||||
self.change_color('#ffffff')
|
||||
self.setToolTip(translate('OpenLP.ColorButton', 'Click to select a color.'))
|
||||
|
205
openlp/core/ui/lib/pathedit.py
Executable file
205
openlp/core/ui/lib/pathedit.py
Executable file
@ -0,0 +1,205 @@
|
||||
# -*- 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 #
|
||||
###############################################################################
|
||||
from enum import Enum
|
||||
import os.path
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common import UiStrings, translate
|
||||
from openlp.core.lib import build_icon
|
||||
|
||||
|
||||
class PathType(Enum):
|
||||
Files = 1
|
||||
Directories = 2
|
||||
|
||||
|
||||
class PathEdit(QtWidgets.QWidget):
|
||||
"""
|
||||
The :class:`~openlp.core.ui.lib.pathedit.PathEdit` class subclasses QWidget to create a custom widget for use when
|
||||
a file or directory needs to be selected.
|
||||
"""
|
||||
pathChanged = QtCore.pyqtSignal(str)
|
||||
|
||||
def __init__(self, parent=None, path_type=PathType.Files, default_path=None, dialog_caption=None, show_revert=True):
|
||||
"""
|
||||
Initalise the PathEdit widget
|
||||
|
||||
:param parent: The parent of the widget. This is just passed to the super method.
|
||||
:type parent: QWidget or None
|
||||
|
||||
:param dialog_caption: Used to customise the caption in the QFileDialog.
|
||||
:param dialog_caption: str
|
||||
|
||||
:param default_path: The default path. This is set as the path when the revert button is clicked
|
||||
:type default_path: str
|
||||
|
||||
:param show_revert: Used to determin if the 'revert button' should be visible.
|
||||
:type show_revert: bool
|
||||
|
||||
:return: None
|
||||
:rtype: None
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.default_path = default_path
|
||||
self.dialog_caption = dialog_caption
|
||||
self._path_type = path_type
|
||||
self._path = None
|
||||
self.filters = '{all_files} (*)'.format(all_files=UiStrings().AllFiles)
|
||||
self._setup(show_revert)
|
||||
|
||||
def _setup(self, show_revert):
|
||||
"""
|
||||
Set up the widget
|
||||
:param show_revert: Show or hide the revert button
|
||||
:type show_revert: bool
|
||||
|
||||
:return: None
|
||||
:rtype: None
|
||||
"""
|
||||
widget_layout = QtWidgets.QHBoxLayout()
|
||||
widget_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.line_edit = QtWidgets.QLineEdit(self)
|
||||
self.line_edit.setText(self._path)
|
||||
widget_layout.addWidget(self.line_edit)
|
||||
self.browse_button = QtWidgets.QToolButton(self)
|
||||
self.browse_button.setIcon(build_icon(':/general/general_open.png'))
|
||||
widget_layout.addWidget(self.browse_button)
|
||||
self.revert_button = QtWidgets.QToolButton(self)
|
||||
self.revert_button.setIcon(build_icon(':/general/general_revert.png'))
|
||||
self.revert_button.setVisible(show_revert)
|
||||
widget_layout.addWidget(self.revert_button)
|
||||
self.setLayout(widget_layout)
|
||||
# Signals and Slots
|
||||
self.browse_button.clicked.connect(self.on_browse_button_clicked)
|
||||
self.revert_button.clicked.connect(self.on_revert_button_clicked)
|
||||
self.line_edit.editingFinished.connect(self.on_line_edit_editing_finished)
|
||||
self.update_button_tool_tips()
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""
|
||||
A property getter method to return the selected path.
|
||||
|
||||
:return: The selected path
|
||||
:rtype: str
|
||||
"""
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, path):
|
||||
"""
|
||||
A Property setter method to set the selected path
|
||||
|
||||
:param path: The path to set the widget to
|
||||
:type path: str
|
||||
"""
|
||||
self._path = path
|
||||
self.line_edit.setText(path)
|
||||
self.line_edit.setToolTip(path)
|
||||
|
||||
@property
|
||||
def path_type(self):
|
||||
"""
|
||||
A property getter method to return the path_type. Path type allows you to sepecify if the user is restricted to
|
||||
selecting a file or directory.
|
||||
|
||||
:return: The type selected
|
||||
:rtype: Enum of PathEdit
|
||||
"""
|
||||
return self._path_type
|
||||
|
||||
@path_type.setter
|
||||
def path_type(self, path_type):
|
||||
"""
|
||||
A Property setter method to set the path type
|
||||
|
||||
:param path: The type of path to select
|
||||
:type path: Enum of PathEdit
|
||||
"""
|
||||
self._path_type = path_type
|
||||
self.update_button_tool_tips()
|
||||
|
||||
def update_button_tool_tips(self):
|
||||
"""
|
||||
Called to update the tooltips on the buttons. This is changing path types, and when the widget is initalised
|
||||
:return: None
|
||||
"""
|
||||
if self._path_type == PathType.Directories:
|
||||
self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for directory.'))
|
||||
self.revert_button.setToolTip(translate('OpenLP.PathEdit', 'Revert to default directory.'))
|
||||
else:
|
||||
self.browse_button.setToolTip(translate('OpenLP.PathEdit', 'Browse for file.'))
|
||||
self.revert_button.setToolTip(translate('OpenLP.PathEdit', 'Revert to default file.'))
|
||||
|
||||
def on_browse_button_clicked(self):
|
||||
"""
|
||||
A handler to handle a click on the browse button.
|
||||
|
||||
Show the QFileDialog and process the input from the user
|
||||
:return: None
|
||||
"""
|
||||
caption = self.dialog_caption
|
||||
path = ''
|
||||
if self._path_type == PathType.Directories:
|
||||
if not caption:
|
||||
caption = translate('OpenLP.PathEdit', 'Select Directory')
|
||||
path = QtWidgets.QFileDialog.getExistingDirectory(self, caption,
|
||||
self._path, QtWidgets.QFileDialog.ShowDirsOnly)
|
||||
elif self._path_type == PathType.Files:
|
||||
if not caption:
|
||||
caption = self.dialog_caption = translate('OpenLP.PathEdit', 'Select File')
|
||||
path, filter_used = QtWidgets.QFileDialog.getOpenFileName(self, caption, self._path, self.filters)
|
||||
if path:
|
||||
path = os.path.normpath(path)
|
||||
self.on_new_path(path)
|
||||
|
||||
def on_revert_button_clicked(self):
|
||||
"""
|
||||
A handler to handle a click on the revert button.
|
||||
|
||||
Set the new path to the value of the default_path instance variable.
|
||||
:return: None
|
||||
"""
|
||||
self.on_new_path(self.default_path)
|
||||
|
||||
def on_line_edit_editing_finished(self):
|
||||
"""
|
||||
A handler to handle when the line edit has finished being edited.
|
||||
:return: None
|
||||
"""
|
||||
self.on_new_path(self.line_edit.text())
|
||||
|
||||
def on_new_path(self, path):
|
||||
"""
|
||||
A method called to validate and set a new path.
|
||||
|
||||
Emits the pathChanged Signal
|
||||
|
||||
:param path: The new path
|
||||
:type path: str
|
||||
|
||||
:return: None
|
||||
"""
|
||||
if self._path != path:
|
||||
self.path = path
|
||||
self.pathChanged.emit(path)
|
@ -28,7 +28,8 @@ import os
|
||||
import datetime
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from openlp.core.common import OpenLPMixin, Registry, RegistryMixin, RegistryProperties, Settings, UiStrings, translate
|
||||
from openlp.core.common import OpenLPMixin, Registry, RegistryMixin, RegistryProperties, Settings, UiStrings, \
|
||||
extension_loader, translate
|
||||
from openlp.core.lib import ItemCapabilities
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.common import AppLocation
|
||||
@ -39,6 +40,7 @@ from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_pla
|
||||
parse_optical_path
|
||||
from openlp.core.ui.lib.toolbar import OpenLPToolbar
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
TICK_TIME = 200
|
||||
@ -172,19 +174,9 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
|
||||
Check to see if we have any media Player's available.
|
||||
"""
|
||||
log.debug('_check_available_media_players')
|
||||
controller_dir = os.path.join(AppLocation.get_directory(AppLocation.AppDir), 'core', 'ui', 'media')
|
||||
for filename in os.listdir(controller_dir):
|
||||
if filename.endswith('player.py') and filename != 'mediaplayer.py':
|
||||
path = os.path.join(controller_dir, filename)
|
||||
if os.path.isfile(path):
|
||||
module_name = 'openlp.core.ui.media.' + os.path.splitext(filename)[0]
|
||||
log.debug('Importing controller %s', module_name)
|
||||
try:
|
||||
__import__(module_name, globals(), locals(), [])
|
||||
# On some platforms importing vlc.py might cause
|
||||
# also OSError exceptions. (e.g. Mac OS X)
|
||||
except (ImportError, OSError):
|
||||
log.warning('Failed to import %s on path %s', module_name, path)
|
||||
controller_dir = os.path.join('openlp', 'core', 'ui', 'media')
|
||||
glob_pattern = os.path.join(controller_dir, '*player.py')
|
||||
extension_loader(glob_pattern, ['mediaplayer.py'])
|
||||
player_classes = MediaPlayer.__subclasses__()
|
||||
for player_class in player_classes:
|
||||
self.register_players(player_class(self))
|
||||
|
@ -38,7 +38,7 @@ from openlp.core.lib.projector.constants import ERROR_MSG, ERROR_STRING, E_AUTHE
|
||||
E_NETWORK, E_NOT_CONNECTED, E_UNKNOWN_SOCKET_ERROR, STATUS_STRING, S_CONNECTED, S_CONNECTING, S_COOLDOWN, \
|
||||
S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP
|
||||
from openlp.core.lib.projector.db import ProjectorDB
|
||||
from openlp.core.lib.projector.pjlink1 import PJLink1
|
||||
from openlp.core.lib.projector.pjlink1 import PJLink
|
||||
from openlp.core.ui.projector.editform import ProjectorEditForm
|
||||
from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle
|
||||
|
||||
@ -690,19 +690,19 @@ class ProjectorManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, UiProjecto
|
||||
Helper app to build a projector instance
|
||||
|
||||
:param projector: Dict of projector database information
|
||||
:returns: PJLink1() instance
|
||||
:returns: PJLink() instance
|
||||
"""
|
||||
log.debug('_add_projector()')
|
||||
return PJLink1(dbid=projector.id,
|
||||
ip=projector.ip,
|
||||
port=int(projector.port),
|
||||
name=projector.name,
|
||||
location=projector.location,
|
||||
notes=projector.notes,
|
||||
pin=None if projector.pin == '' else projector.pin,
|
||||
poll_time=self.poll_time,
|
||||
socket_timeout=self.socket_timeout
|
||||
)
|
||||
return PJLink(dbid=projector.id,
|
||||
ip=projector.ip,
|
||||
port=int(projector.port),
|
||||
name=projector.name,
|
||||
location=projector.location,
|
||||
notes=projector.notes,
|
||||
pin=None if projector.pin == '' else projector.pin,
|
||||
poll_time=self.poll_time,
|
||||
socket_timeout=self.socket_timeout
|
||||
)
|
||||
|
||||
def add_projector(self, projector, start=False):
|
||||
"""
|
||||
@ -961,7 +961,7 @@ class ProjectorItem(QtCore.QObject):
|
||||
"""
|
||||
Initialization for ProjectorItem instance
|
||||
|
||||
:param link: PJLink1 instance for QListWidgetItem
|
||||
:param link: PJLink instance for QListWidgetItem
|
||||
"""
|
||||
self.link = link
|
||||
self.thread = None
|
||||
|
@ -69,10 +69,16 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
||||
self.video_color_button.colorChanged.connect(self.on_video_color_changed)
|
||||
self.gradient_start_button.colorChanged.connect(self.on_gradient_start_color_changed)
|
||||
self.gradient_end_button.colorChanged.connect(self.on_gradient_end_color_changed)
|
||||
self.image_browse_button.clicked.connect(self.on_image_browse_button_clicked)
|
||||
self.image_file_edit.editingFinished.connect(self.on_image_file_edit_editing_finished)
|
||||
self.video_browse_button.clicked.connect(self.on_video_browse_button_clicked)
|
||||
self.video_file_edit.editingFinished.connect(self.on_video_file_edit_editing_finished)
|
||||
self.image_path_edit.filters = \
|
||||
'{name};;{text} (*)'.format(name=get_images_filter(), text=UiStrings().AllFiles)
|
||||
self.image_path_edit.pathChanged.connect(self.on_image_path_edit_path_changed)
|
||||
# TODO: Should work
|
||||
visible_formats = '({name})'.format(name='; '.join(VIDEO_EXT))
|
||||
actual_formats = '({name})'.format(name=' '.join(VIDEO_EXT))
|
||||
video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),
|
||||
visible=visible_formats, actual=actual_formats)
|
||||
self.video_path_edit.filters = '{video};;{ui} (*)'.format(video=video_filter, ui=UiStrings().AllFiles)
|
||||
self.video_path_edit.pathChanged.connect(self.on_video_path_edit_path_changed)
|
||||
self.main_color_button.colorChanged.connect(self.on_main_color_changed)
|
||||
self.outline_color_button.colorChanged.connect(self.on_outline_color_changed)
|
||||
self.shadow_color_button.colorChanged.connect(self.on_shadow_color_changed)
|
||||
@ -112,7 +118,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
||||
self.background_page.registerField('color', self.color_button)
|
||||
self.background_page.registerField('gradient_start', self.gradient_start_button)
|
||||
self.background_page.registerField('gradient_end', self.gradient_end_button)
|
||||
self.background_page.registerField('background_image', self.image_file_edit)
|
||||
self.background_page.registerField('background_image', self.image_path_edit,
|
||||
'path', self.image_path_edit.pathChanged)
|
||||
self.background_page.registerField('gradient', self.gradient_combo_box)
|
||||
self.main_area_page.registerField('main_color_button', self.main_color_button)
|
||||
self.main_area_page.registerField('main_size_spin_box', self.main_size_spin_box)
|
||||
@ -309,11 +316,11 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
||||
self.setField('background_type', 1)
|
||||
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
|
||||
self.image_color_button.color = self.theme.background_border_color
|
||||
self.image_file_edit.setText(self.theme.background_filename)
|
||||
self.image_path_edit.path = self.theme.background_filename
|
||||
self.setField('background_type', 2)
|
||||
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
|
||||
self.video_color_button.color = self.theme.background_border_color
|
||||
self.video_file_edit.setText(self.theme.background_filename)
|
||||
self.video_path_edit.path = self.theme.background_filename
|
||||
self.setField('background_type', 4)
|
||||
elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
|
||||
self.setField('background_type', 3)
|
||||
@ -441,47 +448,20 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
|
||||
"""
|
||||
self.theme.background_end_color = color
|
||||
|
||||
def on_image_browse_button_clicked(self):
|
||||
def on_image_path_edit_path_changed(self, filename):
|
||||
"""
|
||||
Background Image button pushed.
|
||||
"""
|
||||
images_filter = get_images_filter()
|
||||
images_filter = '{name};;{text} (*.*)'.format(name=images_filter, text=UiStrings().AllFiles)
|
||||
filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self, translate('OpenLP.ThemeWizard', 'Select Image'),
|
||||
self.image_file_edit.text(), images_filter)
|
||||
if filename:
|
||||
self.theme.background_filename = filename
|
||||
self.theme.background_filename = filename
|
||||
self.set_background_page_values()
|
||||
|
||||
def on_image_file_edit_editing_finished(self):
|
||||
"""
|
||||
Background image path edited
|
||||
"""
|
||||
self.theme.background_filename = str(self.image_file_edit.text())
|
||||
|
||||
def on_video_browse_button_clicked(self):
|
||||
def on_video_path_edit_path_changed(self, filename):
|
||||
"""
|
||||
Background video button pushed.
|
||||
"""
|
||||
visible_formats = '({name})'.format(name='; '.join(VIDEO_EXT))
|
||||
actual_formats = '({name})'.format(name=' '.join(VIDEO_EXT))
|
||||
video_filter = '{trans} {visible} {actual}'.format(trans=translate('OpenLP', 'Video Files'),
|
||||
visible=visible_formats, actual=actual_formats)
|
||||
video_filter = '{video};;{ui} (*.*)'.format(video=video_filter, ui=UiStrings().AllFiles)
|
||||
filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self, translate('OpenLP.ThemeWizard', 'Select Video'),
|
||||
self.video_file_edit.text(), video_filter)
|
||||
if filename:
|
||||
self.theme.background_filename = filename
|
||||
self.theme.background_filename = filename
|
||||
self.set_background_page_values()
|
||||
|
||||
def on_video_file_edit_editing_finished(self):
|
||||
"""
|
||||
Background video path edited
|
||||
"""
|
||||
self.theme.background_filename = str(self.image_file_edit.text())
|
||||
|
||||
def on_main_color_changed(self, color):
|
||||
"""
|
||||
Set the main colour value
|
||||
|
@ -28,7 +28,7 @@ from openlp.core.common import UiStrings, translate, is_macosx
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType
|
||||
from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets
|
||||
from openlp.core.ui.lib.colorbutton import ColorButton
|
||||
from openlp.core.ui.lib import ColorButton, PathEdit
|
||||
|
||||
|
||||
class Ui_ThemeWizard(object):
|
||||
@ -116,16 +116,10 @@ class Ui_ThemeWizard(object):
|
||||
self.image_layout.addRow(self.image_color_label, self.image_color_button)
|
||||
self.image_label = QtWidgets.QLabel(self.image_widget)
|
||||
self.image_label.setObjectName('image_label')
|
||||
self.image_file_layout = QtWidgets.QHBoxLayout()
|
||||
self.image_file_layout.setObjectName('image_file_layout')
|
||||
self.image_file_edit = QtWidgets.QLineEdit(self.image_widget)
|
||||
self.image_file_edit.setObjectName('image_file_edit')
|
||||
self.image_file_layout.addWidget(self.image_file_edit)
|
||||
self.image_browse_button = QtWidgets.QToolButton(self.image_widget)
|
||||
self.image_browse_button.setObjectName('image_browse_button')
|
||||
self.image_browse_button.setIcon(build_icon(':/general/general_open.png'))
|
||||
self.image_file_layout.addWidget(self.image_browse_button)
|
||||
self.image_layout.addRow(self.image_label, self.image_file_layout)
|
||||
self.image_path_edit = PathEdit(self.image_widget,
|
||||
dialog_caption=translate('OpenLP.ThemeWizard', 'Select Image'),
|
||||
show_revert=False)
|
||||
self.image_layout.addRow(self.image_label, self.image_path_edit)
|
||||
self.image_layout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||
self.background_stack.addWidget(self.image_widget)
|
||||
self.transparent_widget = QtWidgets.QWidget(self.background_page)
|
||||
@ -147,16 +141,10 @@ class Ui_ThemeWizard(object):
|
||||
self.video_layout.addRow(self.video_color_label, self.video_color_button)
|
||||
self.video_label = QtWidgets.QLabel(self.video_widget)
|
||||
self.video_label.setObjectName('video_label')
|
||||
self.video_file_layout = QtWidgets.QHBoxLayout()
|
||||
self.video_file_layout.setObjectName('video_file_layout')
|
||||
self.video_file_edit = QtWidgets.QLineEdit(self.video_widget)
|
||||
self.video_file_edit.setObjectName('video_file_edit')
|
||||
self.video_file_layout.addWidget(self.video_file_edit)
|
||||
self.video_browse_button = QtWidgets.QToolButton(self.video_widget)
|
||||
self.video_browse_button.setObjectName('video_browse_button')
|
||||
self.video_browse_button.setIcon(build_icon(':/general/general_open.png'))
|
||||
self.video_file_layout.addWidget(self.video_browse_button)
|
||||
self.video_layout.addRow(self.video_label, self.video_file_layout)
|
||||
self.video_path_edit = PathEdit(self.video_widget,
|
||||
dialog_caption=translate('OpenLP.ThemeWizard', 'Select Video'),
|
||||
show_revert=False)
|
||||
self.video_layout.addRow(self.video_label, self.video_path_edit)
|
||||
self.video_layout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||
self.background_stack.addWidget(self.video_widget)
|
||||
theme_wizard.addPage(self.background_page)
|
||||
|
@ -135,7 +135,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
Add the bible import specific wizard pages.
|
||||
"""
|
||||
# Select Page
|
||||
self.spacers = []
|
||||
self.select_page = QtWidgets.QWizardPage()
|
||||
self.select_page.setObjectName('SelectPage')
|
||||
self.select_page_layout = QtWidgets.QVBoxLayout(self.select_page)
|
||||
@ -148,8 +147,8 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.format_combo_box.addItems(['', '', '', '', '', '', ''])
|
||||
self.format_combo_box.setObjectName('FormatComboBox')
|
||||
self.format_layout.addRow(self.format_label, self.format_combo_box)
|
||||
self.spacers.append(QtWidgets.QSpacerItem(10, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum))
|
||||
self.format_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacers[-1])
|
||||
self.spacer = QtWidgets.QSpacerItem(10, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
|
||||
self.format_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||
self.select_page_layout.addLayout(self.format_layout)
|
||||
self.select_stack = QtWidgets.QStackedLayout()
|
||||
self.select_stack.setObjectName('SelectStack')
|
||||
@ -171,8 +170,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.osis_browse_button.setObjectName('OsisBrowseButton')
|
||||
self.osis_file_layout.addWidget(self.osis_browse_button)
|
||||
self.osis_layout.addRow(self.osis_file_label, self.osis_file_layout)
|
||||
self.spacers.append(QtWidgets.QSpacerItem(10, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum))
|
||||
self.osis_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacers[-1])
|
||||
self.osis_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||
self.select_stack.addWidget(self.osis_widget)
|
||||
self.csv_widget = QtWidgets.QWidget(self.select_page)
|
||||
self.csv_widget.setObjectName('CsvWidget')
|
||||
@ -205,8 +203,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.csv_verses_button.setObjectName('CsvVersesButton')
|
||||
self.csv_verses_layout.addWidget(self.csv_verses_button)
|
||||
self.csv_layout.addRow(self.csv_verses_label, self.csv_verses_layout)
|
||||
self.spacers.append(QtWidgets.QSpacerItem(10, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum))
|
||||
self.csv_layout.setItem(2, QtWidgets.QFormLayout.LabelRole, self.spacers[-1])
|
||||
self.csv_layout.setItem(3, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||
self.select_stack.addWidget(self.csv_widget)
|
||||
self.open_song_widget = QtWidgets.QWidget(self.select_page)
|
||||
self.open_song_widget.setObjectName('OpenSongWidget')
|
||||
@ -226,8 +223,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.open_song_browse_button.setObjectName('OpenSongBrowseButton')
|
||||
self.open_song_file_layout.addWidget(self.open_song_browse_button)
|
||||
self.open_song_layout.addRow(self.open_song_file_label, self.open_song_file_layout)
|
||||
self.spacers.append(QtWidgets.QSpacerItem(10, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum))
|
||||
self.open_song_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacers[-1])
|
||||
self.open_song_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||
self.select_stack.addWidget(self.open_song_widget)
|
||||
self.web_tab_widget = QtWidgets.QTabWidget(self.select_page)
|
||||
self.web_tab_widget.setObjectName('WebTabWidget')
|
||||
@ -304,8 +300,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.zefania_browse_button.setObjectName('ZefaniaBrowseButton')
|
||||
self.zefania_file_layout.addWidget(self.zefania_browse_button)
|
||||
self.zefania_layout.addRow(self.zefania_file_label, self.zefania_file_layout)
|
||||
self.spacers.append(QtWidgets.QSpacerItem(10, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum))
|
||||
self.zefania_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacers[-1])
|
||||
self.zefania_layout.setItem(5, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||
self.select_stack.addWidget(self.zefania_widget)
|
||||
self.sword_widget = QtWidgets.QWidget(self.select_page)
|
||||
self.sword_widget.setObjectName('SwordWidget')
|
||||
@ -386,8 +381,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.wordproject_browse_button.setObjectName('WordProjectBrowseButton')
|
||||
self.wordproject_file_layout.addWidget(self.wordproject_browse_button)
|
||||
self.wordproject_layout.addRow(self.wordproject_file_label, self.wordproject_file_layout)
|
||||
self.spacers.append(QtWidgets.QSpacerItem(10, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum))
|
||||
self.wordproject_layout.setItem(1, QtWidgets.QFormLayout.LabelRole, self.spacers[-1])
|
||||
self.wordproject_layout.setItem(5, QtWidgets.QFormLayout.LabelRole, self.spacer)
|
||||
self.select_stack.addWidget(self.wordproject_widget)
|
||||
self.select_page_layout.addLayout(self.select_stack)
|
||||
self.addPage(self.select_page)
|
||||
@ -505,8 +499,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.csv_verses_label.minimumSizeHint().width(),
|
||||
self.open_song_file_label.minimumSizeHint().width(),
|
||||
self.zefania_file_label.minimumSizeHint().width())
|
||||
for spacer in self.spacers:
|
||||
spacer.changeSize(label_width, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
self.spacer.changeSize(label_width, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
|
||||
def validateCurrentPage(self):
|
||||
"""
|
||||
|
@ -58,7 +58,8 @@ from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.lib import ScreenList
|
||||
from openlp.core.common import get_uno_command, get_uno_instance
|
||||
from .presentationcontroller import PresentationController, PresentationDocument, TextType
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
|
||||
TextType
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -29,7 +29,7 @@ from subprocess import check_output, CalledProcessError
|
||||
from openlp.core.common import AppLocation, check_binary_exists
|
||||
from openlp.core.common import Settings, is_win
|
||||
from openlp.core.lib import ScreenList
|
||||
from .presentationcontroller import PresentationController, PresentationDocument
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
if is_win():
|
||||
from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
|
||||
|
@ -43,7 +43,7 @@ if is_win():
|
||||
from openlp.core.lib import ScreenList
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate
|
||||
from openlp.core.common import trace_error_handler, Registry
|
||||
from .presentationcontroller import PresentationController, PresentationDocument
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -35,7 +35,7 @@ if is_win():
|
||||
|
||||
from openlp.core.common import AppLocation
|
||||
from openlp.core.lib import ScreenList
|
||||
from .presentationcontroller import PresentationController, PresentationDocument
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -25,7 +25,8 @@ from PyQt5 import QtGui, QtWidgets
|
||||
from openlp.core.common import Settings, UiStrings, translate
|
||||
from openlp.core.lib import SettingsTab, build_icon
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from .pdfcontroller import PdfController
|
||||
from openlp.core.ui.lib import PathEdit
|
||||
from openlp.plugins.presentations.lib.pdfcontroller import PdfController
|
||||
|
||||
|
||||
class PresentationTab(SettingsTab):
|
||||
@ -88,26 +89,15 @@ class PresentationTab(SettingsTab):
|
||||
self.pdf_program_check_box = QtWidgets.QCheckBox(self.pdf_group_box)
|
||||
self.pdf_program_check_box.setObjectName('pdf_program_check_box')
|
||||
self.pdf_layout.addRow(self.pdf_program_check_box)
|
||||
self.pdf_program_path_layout = QtWidgets.QHBoxLayout()
|
||||
self.pdf_program_path_layout.setObjectName('pdf_program_path_layout')
|
||||
self.pdf_program_path = QtWidgets.QLineEdit(self.pdf_group_box)
|
||||
self.pdf_program_path.setObjectName('pdf_program_path')
|
||||
self.pdf_program_path.setReadOnly(True)
|
||||
self.pdf_program_path.setPalette(self.get_grey_text_palette(True))
|
||||
self.pdf_program_path_layout.addWidget(self.pdf_program_path)
|
||||
self.pdf_program_browse_button = QtWidgets.QToolButton(self.pdf_group_box)
|
||||
self.pdf_program_browse_button.setObjectName('pdf_program_browse_button')
|
||||
self.pdf_program_browse_button.setIcon(build_icon(':/general/general_open.png'))
|
||||
self.pdf_program_browse_button.setEnabled(False)
|
||||
self.pdf_program_path_layout.addWidget(self.pdf_program_browse_button)
|
||||
self.pdf_layout.addRow(self.pdf_program_path_layout)
|
||||
self.program_path_edit = PathEdit(self.pdf_group_box)
|
||||
self.pdf_layout.addRow(self.program_path_edit)
|
||||
self.left_layout.addWidget(self.pdf_group_box)
|
||||
self.left_layout.addStretch()
|
||||
self.right_column.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
|
||||
self.right_layout.addStretch()
|
||||
# Signals and slots
|
||||
self.pdf_program_browse_button.clicked.connect(self.on_pdf_program_browse_button_clicked)
|
||||
self.pdf_program_check_box.clicked.connect(self.on_pdf_program_check_box_clicked)
|
||||
self.program_path_edit.pathChanged.connect(self.on_program_path_edit_path_changed)
|
||||
self.pdf_program_check_box.clicked.connect(self.program_path_edit.setEnabled)
|
||||
|
||||
def retranslateUi(self):
|
||||
"""
|
||||
@ -132,6 +122,8 @@ class PresentationTab(SettingsTab):
|
||||
'(This may fix PowerPoint scaling issues in Windows 8 and 10)'))
|
||||
self.pdf_program_check_box.setText(
|
||||
translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:'))
|
||||
self.program_path_edit.dialog_caption = translate('PresentationPlugin.PresentationTab',
|
||||
'Select mudraw or ghostscript binary')
|
||||
|
||||
def set_controller_text(self, checkbox, controller):
|
||||
if checkbox.isEnabled():
|
||||
@ -161,11 +153,10 @@ class PresentationTab(SettingsTab):
|
||||
# load pdf-program settings
|
||||
enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program')
|
||||
self.pdf_program_check_box.setChecked(enable_pdf_program)
|
||||
self.pdf_program_path.setPalette(self.get_grey_text_palette(not enable_pdf_program))
|
||||
self.pdf_program_browse_button.setEnabled(enable_pdf_program)
|
||||
self.program_path_edit.setEnabled(enable_pdf_program)
|
||||
pdf_program = Settings().value(self.settings_section + '/pdf_program')
|
||||
if pdf_program:
|
||||
self.pdf_program_path.setText(pdf_program)
|
||||
self.program_path_edit.path = pdf_program
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
@ -201,7 +192,7 @@ class PresentationTab(SettingsTab):
|
||||
Settings().setValue(setting_key, self.ppt_window_check_box.checkState())
|
||||
changed = True
|
||||
# Save pdf-settings
|
||||
pdf_program = self.pdf_program_path.text()
|
||||
pdf_program = self.program_path_edit.path
|
||||
enable_pdf_program = self.pdf_program_check_box.checkState()
|
||||
# If the given program is blank disable using the program
|
||||
if pdf_program == '':
|
||||
@ -228,42 +219,12 @@ class PresentationTab(SettingsTab):
|
||||
checkbox.setEnabled(controller.is_available())
|
||||
self.set_controller_text(checkbox, controller)
|
||||
|
||||
def on_pdf_program_browse_button_clicked(self):
|
||||
def on_program_path_edit_path_changed(self, filename):
|
||||
"""
|
||||
Select the mudraw or ghostscript binary that should be used.
|
||||
"""
|
||||
filename, filter_used = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self, translate('PresentationPlugin.PresentationTab', 'Select mudraw or ghostscript binary.'),
|
||||
self.pdf_program_path.text())
|
||||
if filename:
|
||||
program_type = PdfController.process_check_binary(filename)
|
||||
if not program_type:
|
||||
if not PdfController.process_check_binary(filename):
|
||||
critical_error_message_box(UiStrings().Error,
|
||||
translate('PresentationPlugin.PresentationTab',
|
||||
'The program is not ghostscript or mudraw which is required.'))
|
||||
else:
|
||||
self.pdf_program_path.setText(filename)
|
||||
|
||||
def on_pdf_program_check_box_clicked(self, checked):
|
||||
"""
|
||||
When checkbox for manual entering pdf-program is clicked,
|
||||
enable or disable the textbox for the programpath and the browse-button.
|
||||
|
||||
:param checked: If the box is checked or not.
|
||||
"""
|
||||
self.pdf_program_path.setPalette(self.get_grey_text_palette(not checked))
|
||||
self.pdf_program_browse_button.setEnabled(checked)
|
||||
|
||||
def get_grey_text_palette(self, greyed):
|
||||
"""
|
||||
Returns a QPalette with greyed out text as used for placeholderText.
|
||||
|
||||
:param greyed: Determines whether the palette should be grayed.
|
||||
:return: The created palette.
|
||||
"""
|
||||
palette = QtGui.QPalette()
|
||||
color = self.palette().color(QtGui.QPalette.Active, QtGui.QPalette.Text)
|
||||
if greyed:
|
||||
color.setAlpha(128)
|
||||
palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, color)
|
||||
return palette
|
||||
|
@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
@ -20,19 +20,18 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`presentationplugin` module provides the ability for OpenLP to display presentations from a variety of document
|
||||
formats.
|
||||
The :mod:`openlp.plugins.presentations.presentationplugin` module provides the ability for OpenLP to display
|
||||
presentations from a variety of document formats.
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.common import AppLocation, translate
|
||||
from openlp.core.common import AppLocation, extension_loader, translate
|
||||
from openlp.core.lib import Plugin, StringContent, build_icon
|
||||
from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -122,17 +121,9 @@ class PresentationPlugin(Plugin):
|
||||
Check to see if we have any presentation software available. If not do not install the plugin.
|
||||
"""
|
||||
log.debug('check_pre_conditions')
|
||||
controller_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'presentations', 'lib')
|
||||
for filename in os.listdir(controller_dir):
|
||||
if filename.endswith('controller.py') and filename != 'presentationcontroller.py':
|
||||
path = os.path.join(controller_dir, filename)
|
||||
if os.path.isfile(path):
|
||||
module_name = 'openlp.plugins.presentations.lib.' + os.path.splitext(filename)[0]
|
||||
log.debug('Importing controller {name}'.format(name=module_name))
|
||||
try:
|
||||
__import__(module_name, globals(), locals(), [])
|
||||
except ImportError:
|
||||
log.warning('Failed to import {name} on path {path}'.format(name=module_name, path=path))
|
||||
controller_dir = os.path.join('openlp', 'plugins', 'presentations', 'lib')
|
||||
glob_pattern = os.path.join(controller_dir, '*controller.py')
|
||||
extension_loader(glob_pattern, ['presentationcontroller.py'])
|
||||
controller_classes = PresentationController.__subclasses__()
|
||||
for controller_class in controller_classes:
|
||||
controller = controller_class(self)
|
||||
|
@ -512,10 +512,13 @@ def strip_rtf(text, default_encoding=None):
|
||||
elif not ignorable:
|
||||
ebytes.append(int(hex_, 16))
|
||||
elif tchar:
|
||||
if curskip > 0:
|
||||
curskip -= 1
|
||||
elif not ignorable:
|
||||
if not ignorable:
|
||||
ebytes += tchar.encode()
|
||||
if len(ebytes) >= curskip:
|
||||
ebytes = ebytes[curskip:]
|
||||
else:
|
||||
curskip -= len(ebytes)
|
||||
ebytes = ""
|
||||
text = ''.join(out)
|
||||
return text, default_encoding
|
||||
|
||||
|
@ -160,26 +160,27 @@ class SongFormat(object):
|
||||
DreamBeam = 5
|
||||
EasySlides = 6
|
||||
EasyWorshipDB = 7
|
||||
EasyWorshipService = 8
|
||||
FoilPresenter = 9
|
||||
Lyrix = 10
|
||||
MediaShout = 11
|
||||
OpenSong = 12
|
||||
OPSPro = 13
|
||||
PowerPraise = 14
|
||||
PowerSong = 15
|
||||
PresentationManager = 16
|
||||
ProPresenter = 17
|
||||
SongBeamer = 18
|
||||
SongPro = 19
|
||||
SongShowPlus = 20
|
||||
SongsOfFellowship = 21
|
||||
SundayPlus = 22
|
||||
VideoPsalm = 23
|
||||
WordsOfWorship = 24
|
||||
WorshipAssistant = 25
|
||||
WorshipCenterPro = 26
|
||||
ZionWorx = 27
|
||||
EasyWorshipSqliteDB = 8
|
||||
EasyWorshipService = 9
|
||||
FoilPresenter = 10
|
||||
Lyrix = 11
|
||||
MediaShout = 12
|
||||
OpenSong = 13
|
||||
OPSPro = 14
|
||||
PowerPraise = 15
|
||||
PowerSong = 16
|
||||
PresentationManager = 17
|
||||
ProPresenter = 18
|
||||
SongBeamer = 19
|
||||
SongPro = 20
|
||||
SongShowPlus = 21
|
||||
SongsOfFellowship = 22
|
||||
SundayPlus = 23
|
||||
VideoPsalm = 24
|
||||
WordsOfWorship = 25
|
||||
WorshipAssistant = 26
|
||||
WorshipCenterPro = 27
|
||||
ZionWorx = 28
|
||||
|
||||
# Set optional attribute defaults
|
||||
__defaults__ = {
|
||||
@ -251,9 +252,17 @@ class SongFormat(object):
|
||||
'name': 'EasyWorship Song Database',
|
||||
'prefix': 'ew',
|
||||
'selectMode': SongFormatSelect.SingleFile,
|
||||
'filter': '{text} (*.db)'.format(text=translate('SongsPlugin.ImportWizardForm',
|
||||
'filter': '{text} (*.DB)'.format(text=translate('SongsPlugin.ImportWizardForm',
|
||||
'EasyWorship Song Database'))
|
||||
},
|
||||
EasyWorshipSqliteDB: {
|
||||
'class': EasyWorshipSongImport,
|
||||
'name': 'EasyWorship 6 Song Database',
|
||||
'prefix': 'ew',
|
||||
'selectMode': SongFormatSelect.SingleFolder,
|
||||
'filter': '{text} (*.db)'.format(text=translate('SongsPlugin.ImportWizardForm',
|
||||
'EasyWorship 6 Song Data Directory'))
|
||||
},
|
||||
EasyWorshipService: {
|
||||
'class': EasyWorshipSongImport,
|
||||
'name': 'EasyWorship Service',
|
||||
@ -440,6 +449,7 @@ class SongFormat(object):
|
||||
SongFormat.DreamBeam,
|
||||
SongFormat.EasySlides,
|
||||
SongFormat.EasyWorshipDB,
|
||||
SongFormat.EasyWorshipSqliteDB,
|
||||
SongFormat.EasyWorshipService,
|
||||
SongFormat.FoilPresenter,
|
||||
SongFormat.Lyrix,
|
||||
|
@ -28,6 +28,7 @@ import struct
|
||||
import re
|
||||
import zlib
|
||||
import logging
|
||||
import sqlite3
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
@ -77,8 +78,10 @@ class EasyWorshipSongImport(SongImport):
|
||||
"""
|
||||
if self.import_source.lower().endswith('ews'):
|
||||
self.import_ews()
|
||||
else:
|
||||
elif self.import_source.endswith('DB'):
|
||||
self.import_db()
|
||||
else:
|
||||
self.import_sqlite_db()
|
||||
|
||||
def import_ews(self):
|
||||
"""
|
||||
@ -125,8 +128,8 @@ class EasyWorshipSongImport(SongImport):
|
||||
else:
|
||||
log.debug('Given ews file is of unknown version.')
|
||||
return
|
||||
entry_count = self.get_i32(file_pos)
|
||||
entry_length = self.get_i16(file_pos + 4)
|
||||
entry_count = self.ews_get_i32(file_pos)
|
||||
entry_length = self.ews_get_i16(file_pos + 4)
|
||||
file_pos += 6
|
||||
self.import_wizard.progress_bar.setMaximum(entry_count)
|
||||
# Loop over songs
|
||||
@ -144,13 +147,13 @@ class EasyWorshipSongImport(SongImport):
|
||||
# 0x08 = Audio, 0x09 = Web
|
||||
# 1410 Song number cstring 10
|
||||
self.set_defaults()
|
||||
self.title = self.get_string(file_pos + 0, 50)
|
||||
authors = self.get_string(file_pos + 307, 50)
|
||||
copyright = self.get_string(file_pos + 358, 100)
|
||||
admin = self.get_string(file_pos + 459, 50)
|
||||
cont_ptr = self.get_i32(file_pos + 800)
|
||||
cont_type = self.get_i32(file_pos + 820)
|
||||
self.ccli_number = self.get_string(file_pos + 1410, 10)
|
||||
self.title = self.ews_get_string(file_pos + 0, 50)
|
||||
authors = self.ews_get_string(file_pos + 307, 50)
|
||||
copyright = self.ews_get_string(file_pos + 358, 100)
|
||||
admin = self.ews_get_string(file_pos + 459, 50)
|
||||
cont_ptr = self.ews_get_i32(file_pos + 800)
|
||||
cont_type = self.ews_get_i32(file_pos + 820)
|
||||
self.ccli_number = self.ews_get_string(file_pos + 1410, 10)
|
||||
# Only handle content type 1 (songs)
|
||||
if cont_type != 1:
|
||||
file_pos += entry_length
|
||||
@ -164,9 +167,9 @@ class EasyWorshipSongImport(SongImport):
|
||||
# Checksum int32be 4 Alder-32 checksum.
|
||||
# (unknown) 4 0x51 0x4b 0x03 0x04
|
||||
# Content length int32le 4 Length of content after decompression
|
||||
content_length = self.get_i32(cont_ptr)
|
||||
deflated_content = self.get_bytes(cont_ptr + 4, content_length - 10)
|
||||
deflated_length = self.get_i32(cont_ptr + 4 + content_length - 6)
|
||||
content_length = self.ews_get_i32(cont_ptr)
|
||||
deflated_content = self.ews_get_bytes(cont_ptr + 4, content_length - 10)
|
||||
deflated_length = self.ews_get_i32(cont_ptr + 4 + content_length - 6)
|
||||
inflated_content = zlib.decompress(deflated_content, 15, deflated_length)
|
||||
if copyright:
|
||||
self.copyright = copyright
|
||||
@ -196,7 +199,7 @@ class EasyWorshipSongImport(SongImport):
|
||||
Import the songs from the database
|
||||
"""
|
||||
# Open the DB and MB files if they exist
|
||||
import_source_mb = self.import_source.replace('.DB', '.MB').replace('.db', '.mb')
|
||||
import_source_mb = self.import_source.replace('.DB', '.MB')
|
||||
if not os.path.isfile(self.import_source):
|
||||
self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
|
||||
'This file does not exist.'))
|
||||
@ -260,16 +263,16 @@ class EasyWorshipSongImport(SongImport):
|
||||
for i, field_name in enumerate(field_names):
|
||||
field_type, field_size = struct.unpack_from('BB', field_info, i * 2)
|
||||
field_descriptions.append(FieldDescEntry(field_name, field_type, field_size))
|
||||
self.set_record_struct(field_descriptions)
|
||||
self.db_set_record_struct(field_descriptions)
|
||||
# Pick out the field description indexes we will need
|
||||
try:
|
||||
success = True
|
||||
fi_title = self.find_field(b'Title')
|
||||
fi_author = self.find_field(b'Author')
|
||||
fi_copy = self.find_field(b'Copyright')
|
||||
fi_admin = self.find_field(b'Administrator')
|
||||
fi_words = self.find_field(b'Words')
|
||||
fi_ccli = self.find_field(b'Song Number')
|
||||
fi_title = self.db_find_field(b'Title')
|
||||
fi_author = self.db_find_field(b'Author')
|
||||
fi_copy = self.db_find_field(b'Copyright')
|
||||
fi_admin = self.db_find_field(b'Administrator')
|
||||
fi_words = self.db_find_field(b'Words')
|
||||
fi_ccli = self.db_find_field(b'Song Number')
|
||||
except IndexError:
|
||||
# This is the wrong table
|
||||
success = False
|
||||
@ -297,13 +300,13 @@ class EasyWorshipSongImport(SongImport):
|
||||
raw_record = db_file.read(record_size)
|
||||
self.fields = self.record_structure.unpack(raw_record)
|
||||
self.set_defaults()
|
||||
self.title = self.get_field(fi_title).decode(self.encoding)
|
||||
self.title = self.db_get_field(fi_title).decode(self.encoding)
|
||||
# Get remaining fields.
|
||||
copy = self.get_field(fi_copy)
|
||||
admin = self.get_field(fi_admin)
|
||||
ccli = self.get_field(fi_ccli)
|
||||
authors = self.get_field(fi_author)
|
||||
words = self.get_field(fi_words)
|
||||
copy = self.db_get_field(fi_copy)
|
||||
admin = self.db_get_field(fi_admin)
|
||||
ccli = self.db_get_field(fi_ccli)
|
||||
authors = self.db_get_field(fi_author)
|
||||
words = self.db_get_field(fi_words)
|
||||
if copy:
|
||||
self.copyright = copy.decode(self.encoding)
|
||||
if admin:
|
||||
@ -337,6 +340,82 @@ class EasyWorshipSongImport(SongImport):
|
||||
db_file.close()
|
||||
self.memo_file.close()
|
||||
|
||||
def _find_file(self, base_path, path_list):
|
||||
"""
|
||||
Find the specified file, with the option of the file being at any level in the specified directory structure.
|
||||
|
||||
:param base_path: the location search in
|
||||
:param path_list: the targeted file, preceded by directories that may be their parents relative to the base_path
|
||||
:return: path for targeted file
|
||||
"""
|
||||
target_file = ''
|
||||
while len(path_list) > 0:
|
||||
target_file = os.path.join(path_list[-1], target_file)
|
||||
path_list = path_list[:len(path_list) - 1]
|
||||
full_path = os.path.join(base_path, target_file)
|
||||
full_path = full_path[:len(full_path) - 1]
|
||||
if os.path.isfile(full_path):
|
||||
return full_path
|
||||
return ''
|
||||
|
||||
def import_sqlite_db(self):
|
||||
"""
|
||||
Import the songs from an EasyWorship 6 SQLite database
|
||||
"""
|
||||
songs_db_path = self._find_file(self.import_source, ["Databases", "Data", "Songs.db"])
|
||||
song_words_db_path = self._find_file(self.import_source, ["Databases", "Data", "SongWords.db"])
|
||||
invalid_dir_msg = 'This does not appear to be a valid Easy Worship 6 database directory.'
|
||||
# check to see if needed files are there
|
||||
if not os.path.isfile(songs_db_path):
|
||||
self.log_error(songs_db_path, translate('SongsPlugin.EasyWorshipSongImport', invalid_dir_msg))
|
||||
return
|
||||
if not os.path.isfile(song_words_db_path):
|
||||
self.log_error(song_words_db_path, translate('SongsPlugin.EasyWorshipSongImport', invalid_dir_msg))
|
||||
return
|
||||
# get database handles
|
||||
songs_conn = sqlite3.connect(songs_db_path)
|
||||
words_conn = sqlite3.connect(song_words_db_path)
|
||||
if songs_conn is None or words_conn is None:
|
||||
self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
|
||||
'This is not a valid Easy Worship 6 database.'))
|
||||
songs_conn.close()
|
||||
words_conn.close()
|
||||
return
|
||||
songs_db = songs_conn.cursor()
|
||||
words_db = words_conn.cursor()
|
||||
if songs_conn is None or words_conn is None:
|
||||
self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
|
||||
'This is not a valid Easy Worship 6 database.'))
|
||||
songs_conn.close()
|
||||
words_conn.close()
|
||||
return
|
||||
# Take a stab at how text is encoded
|
||||
self.encoding = 'cp1252'
|
||||
self.encoding = retrieve_windows_encoding(self.encoding)
|
||||
if not self.encoding:
|
||||
log.debug('No encoding set.')
|
||||
return
|
||||
# import songs
|
||||
songs = songs_db.execute('SELECT rowid,title,author,copyright,vendor_id FROM song;')
|
||||
for song in songs:
|
||||
song_id = song[0]
|
||||
# keep extra copy of title for error message because error check clears it
|
||||
self.title = title = song[1]
|
||||
self.author = song[2]
|
||||
self.copyright = song[3]
|
||||
self.ccli_number = song[4]
|
||||
words = words_db.execute('SELECT words FROM word WHERE song_id = ?;', (song_id,))
|
||||
self.set_song_import_object(self.author, words.fetchone()[0].encode())
|
||||
if not self.finish():
|
||||
self.log_error(self.import_source,
|
||||
translate('SongsPlugin.EasyWorshipSongImport',
|
||||
'"{title}" could not be imported. {entry}').
|
||||
format(title=title, entry=self.entry_error_log))
|
||||
# close database handles
|
||||
songs_conn.close()
|
||||
words_conn.close()
|
||||
return
|
||||
|
||||
def set_song_import_object(self, authors, words):
|
||||
"""
|
||||
Set the SongImport object members.
|
||||
@ -409,7 +488,7 @@ class EasyWorshipSongImport(SongImport):
|
||||
self.comments += str(translate('SongsPlugin.EasyWorshipSongImport',
|
||||
'\n[above are Song Tags with notes imported from EasyWorship]'))
|
||||
|
||||
def find_field(self, field_name):
|
||||
def db_find_field(self, field_name):
|
||||
"""
|
||||
Find a field in the descriptions
|
||||
|
||||
@ -417,7 +496,7 @@ class EasyWorshipSongImport(SongImport):
|
||||
"""
|
||||
return [i for i, x in enumerate(self.field_descriptions) if x.name == field_name][0]
|
||||
|
||||
def set_record_struct(self, field_descriptions):
|
||||
def db_set_record_struct(self, field_descriptions):
|
||||
"""
|
||||
Save the record structure
|
||||
|
||||
@ -445,7 +524,7 @@ class EasyWorshipSongImport(SongImport):
|
||||
self.record_structure = struct.Struct(''.join(fsl))
|
||||
self.field_descriptions = field_descriptions
|
||||
|
||||
def get_field(self, field_desc_index):
|
||||
def db_get_field(self, field_desc_index):
|
||||
"""
|
||||
Extract the field
|
||||
|
||||
@ -489,7 +568,7 @@ class EasyWorshipSongImport(SongImport):
|
||||
else:
|
||||
return 0
|
||||
|
||||
def get_bytes(self, pos, length):
|
||||
def ews_get_bytes(self, pos, length):
|
||||
"""
|
||||
Get bytes from ews_file
|
||||
|
||||
@ -500,7 +579,7 @@ class EasyWorshipSongImport(SongImport):
|
||||
self.ews_file.seek(pos)
|
||||
return self.ews_file.read(length)
|
||||
|
||||
def get_string(self, pos, length):
|
||||
def ews_get_string(self, pos, length):
|
||||
"""
|
||||
Get string from ews_file
|
||||
|
||||
@ -508,12 +587,12 @@ class EasyWorshipSongImport(SongImport):
|
||||
:param length: Characters to read
|
||||
:return: String read
|
||||
"""
|
||||
bytes = self.get_bytes(pos, length)
|
||||
bytes = self.ews_get_bytes(pos, length)
|
||||
mask = '<' + str(length) + 's'
|
||||
byte_str, = struct.unpack(mask, bytes)
|
||||
return byte_str.decode(self.encoding).replace('\0', '').strip()
|
||||
|
||||
def get_i16(self, pos):
|
||||
def ews_get_i16(self, pos):
|
||||
"""
|
||||
Get short int from ews_file
|
||||
|
||||
@ -521,19 +600,19 @@ class EasyWorshipSongImport(SongImport):
|
||||
:return: Short integer read
|
||||
"""
|
||||
|
||||
bytes = self.get_bytes(pos, 2)
|
||||
bytes = self.ews_get_bytes(pos, 2)
|
||||
mask = '<h'
|
||||
number, = struct.unpack(mask, bytes)
|
||||
return number
|
||||
|
||||
def get_i32(self, pos):
|
||||
def ews_get_i32(self, pos):
|
||||
"""
|
||||
Get long int from ews_file
|
||||
|
||||
:param pos: Position to read from
|
||||
:return: Long integer read
|
||||
"""
|
||||
bytes = self.get_bytes(pos, 4)
|
||||
bytes = self.ews_get_bytes(pos, 4)
|
||||
mask = '<i'
|
||||
number, = struct.unpack(mask, bytes)
|
||||
return number
|
||||
|
@ -25,6 +25,7 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from openlp.core.common import translate
|
||||
from openlp.core.lib import build_icon
|
||||
from openlp.core.lib.ui import create_button_box
|
||||
from openlp.core.ui.lib import PathEdit, PathType
|
||||
|
||||
|
||||
class Ui_SongUsageDetailDialog(object):
|
||||
@ -68,20 +69,13 @@ class Ui_SongUsageDetailDialog(object):
|
||||
self.file_horizontal_layout.setSpacing(8)
|
||||
self.file_horizontal_layout.setContentsMargins(8, 8, 8, 8)
|
||||
self.file_horizontal_layout.setObjectName('file_horizontal_layout')
|
||||
self.file_line_edit = QtWidgets.QLineEdit(self.file_group_box)
|
||||
self.file_line_edit.setObjectName('file_line_edit')
|
||||
self.file_line_edit.setReadOnly(True)
|
||||
self.file_horizontal_layout.addWidget(self.file_line_edit)
|
||||
self.save_file_push_button = QtWidgets.QPushButton(self.file_group_box)
|
||||
self.save_file_push_button.setMaximumWidth(self.save_file_push_button.size().height())
|
||||
self.save_file_push_button.setIcon(build_icon(':/general/general_open.png'))
|
||||
self.save_file_push_button.setObjectName('save_file_push_button')
|
||||
self.file_horizontal_layout.addWidget(self.save_file_push_button)
|
||||
self.report_path_edit = PathEdit(self.file_group_box, path_type = PathType.Directories, show_revert=False)
|
||||
self.file_horizontal_layout.addWidget(self.report_path_edit)
|
||||
self.vertical_layout.addWidget(self.file_group_box)
|
||||
self.button_box = create_button_box(song_usage_detail_dialog, 'button_box', ['cancel', 'ok'])
|
||||
self.vertical_layout.addWidget(self.button_box)
|
||||
self.retranslateUi(song_usage_detail_dialog)
|
||||
self.save_file_push_button.clicked.connect(song_usage_detail_dialog.define_output_location)
|
||||
self.report_path_edit.pathChanged.connect(song_usage_detail_dialog.on_report_path_edit_path_changed)
|
||||
|
||||
def retranslateUi(self, song_usage_detail_dialog):
|
||||
"""
|
||||
|
@ -54,25 +54,20 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP
|
||||
"""
|
||||
self.from_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/from date'))
|
||||
self.to_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/to date'))
|
||||
self.file_line_edit.setText(Settings().value(self.plugin.settings_section + '/last directory export'))
|
||||
self.report_path_edit.path = Settings().value(self.plugin.settings_section + '/last directory export')
|
||||
|
||||
def define_output_location(self):
|
||||
def on_report_path_edit_path_changed(self, file_path):
|
||||
"""
|
||||
Triggered when the Directory selection button is clicked
|
||||
"""
|
||||
path = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
self, translate('SongUsagePlugin.SongUsageDetailForm', 'Output File Location'),
|
||||
Settings().value(self.plugin.settings_section + '/last directory export'))
|
||||
if path:
|
||||
Settings().setValue(self.plugin.settings_section + '/last directory export', path)
|
||||
self.file_line_edit.setText(path)
|
||||
Settings().setValue(self.plugin.settings_section + '/last directory export', file_path)
|
||||
|
||||
def accept(self):
|
||||
"""
|
||||
Ok was triggered so lets save the data and run the report
|
||||
"""
|
||||
log.debug('accept')
|
||||
path = self.file_line_edit.text()
|
||||
path = self.report_path_edit.path
|
||||
if not path:
|
||||
self.main_window.error_message(
|
||||
translate('SongUsagePlugin.SongUsageDetailForm', 'Output Path Not Selected'),
|
||||
|
@ -22,11 +22,13 @@
|
||||
"""
|
||||
Functional tests to test the AppLocation class and related methods.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from openlp.core.common import check_directory_exists, de_hump, trace_error_handler, translate, is_win, is_macosx, \
|
||||
is_linux, clean_button_text
|
||||
from openlp.core import common
|
||||
from openlp.core.common import check_directory_exists, clean_button_text, de_hump, extension_loader, is_macosx, \
|
||||
is_linux, is_win, path_to_module, trace_error_handler, translate
|
||||
|
||||
|
||||
class TestCommonFunctions(TestCase):
|
||||
@ -72,6 +74,72 @@ class TestCommonFunctions(TestCase):
|
||||
mocked_exists.assert_called_with(directory_to_check)
|
||||
self.assertRaises(ValueError, check_directory_exists, directory_to_check)
|
||||
|
||||
def test_extension_loader_no_files_found(self):
|
||||
"""
|
||||
Test the `extension_loader` function when no files are found
|
||||
"""
|
||||
# GIVEN: A mocked `Path.glob` method which does not match any files
|
||||
with patch('openlp.core.common.AppLocation.get_directory', return_value='/app/dir/openlp'), \
|
||||
patch.object(common.Path, 'glob', return_value=[]), \
|
||||
patch('openlp.core.common.importlib.import_module') as mocked_import_module:
|
||||
|
||||
# WHEN: Calling `extension_loader`
|
||||
extension_loader('glob', ['file2.py', 'file3.py'])
|
||||
|
||||
# THEN: `extension_loader` should not try to import any files
|
||||
self.assertFalse(mocked_import_module.called)
|
||||
|
||||
def test_extension_loader_files_found(self):
|
||||
"""
|
||||
Test the `extension_loader` function when it successfully finds and loads some files
|
||||
"""
|
||||
# GIVEN: A mocked `Path.glob` method which returns a list of files
|
||||
with patch('openlp.core.common.AppLocation.get_directory', return_value='/app/dir/openlp'), \
|
||||
patch.object(common.Path, 'glob', return_value=[Path('/app/dir/openlp/import_dir/file1.py'),
|
||||
Path('/app/dir/openlp/import_dir/file2.py'),
|
||||
Path('/app/dir/openlp/import_dir/file3.py'),
|
||||
Path('/app/dir/openlp/import_dir/file4.py')]), \
|
||||
patch('openlp.core.common.importlib.import_module') as mocked_import_module:
|
||||
|
||||
# WHEN: Calling `extension_loader` with a list of files to exclude
|
||||
extension_loader('glob', ['file2.py', 'file3.py'])
|
||||
|
||||
# THEN: `extension_loader` should only try to import the files that are matched by the blob, excluding the
|
||||
# files listed in the `excluded_files` argument
|
||||
mocked_import_module.assert_has_calls([call('openlp.import_dir.file1'), call('openlp.import_dir.file4')])
|
||||
|
||||
def test_extension_loader_import_error(self):
|
||||
"""
|
||||
Test the `extension_loader` function when `SourceFileLoader` raises a `ImportError`
|
||||
"""
|
||||
# GIVEN: A mocked `import_module` which raises an `ImportError`
|
||||
with patch('openlp.core.common.AppLocation.get_directory', return_value='/app/dir/openlp'), \
|
||||
patch.object(common.Path, 'glob', return_value=[Path('/app/dir/openlp/import_dir/file1.py')]), \
|
||||
patch('openlp.core.common.importlib.import_module', side_effect=ImportError()), \
|
||||
patch('openlp.core.common.log') as mocked_logger:
|
||||
|
||||
# WHEN: Calling `extension_loader`
|
||||
extension_loader('glob')
|
||||
|
||||
# THEN: The `ImportError` should be caught and logged
|
||||
self.assertTrue(mocked_logger.warning.called)
|
||||
|
||||
def test_extension_loader_os_error(self):
|
||||
"""
|
||||
Test the `extension_loader` function when `import_module` raises a `ImportError`
|
||||
"""
|
||||
# GIVEN: A mocked `SourceFileLoader` which raises an `OSError`
|
||||
with patch('openlp.core.common.AppLocation.get_directory', return_value='/app/dir/openlp'), \
|
||||
patch.object(common.Path, 'glob', return_value=[Path('/app/dir/openlp/import_dir/file1.py')]), \
|
||||
patch('openlp.core.common.importlib.import_module', side_effect=OSError()), \
|
||||
patch('openlp.core.common.log') as mocked_logger:
|
||||
|
||||
# WHEN: Calling `extension_loader`
|
||||
extension_loader('glob')
|
||||
|
||||
# THEN: The `OSError` should be caught and logged
|
||||
self.assertTrue(mocked_logger.warning.called)
|
||||
|
||||
def test_de_hump_conversion(self):
|
||||
"""
|
||||
Test the de_hump function with a class name
|
||||
@ -83,7 +151,7 @@ class TestCommonFunctions(TestCase):
|
||||
new_string = de_hump(string)
|
||||
|
||||
# THEN: the new string should be converted to python format
|
||||
self.assertTrue(new_string == "my_class", 'The class name should have been converted')
|
||||
self.assertEqual(new_string, "my_class", 'The class name should have been converted')
|
||||
|
||||
def test_de_hump_static(self):
|
||||
"""
|
||||
@ -96,7 +164,20 @@ class TestCommonFunctions(TestCase):
|
||||
new_string = de_hump(string)
|
||||
|
||||
# THEN: the new string should be converted to python format
|
||||
self.assertTrue(new_string == "my_class", 'The class name should have been preserved')
|
||||
self.assertEqual(new_string, "my_class", 'The class name should have been preserved')
|
||||
|
||||
def test_path_to_module(self):
|
||||
"""
|
||||
Test `path_to_module` when supplied with a `Path` object
|
||||
"""
|
||||
# GIVEN: A `Path` object
|
||||
path = Path('openlp/core/ui/media/webkitplayer.py')
|
||||
|
||||
# WHEN: Calling path_to_module with the `Path` object
|
||||
result = path_to_module(path)
|
||||
|
||||
# THEN: path_to_module should return the module name
|
||||
self.assertEqual(result, 'openlp.core.ui.media.webkitplayer')
|
||||
|
||||
def test_trace_error_handler(self):
|
||||
"""
|
||||
|
@ -29,7 +29,6 @@ class TestProjectorConstants(TestCase):
|
||||
"""
|
||||
Test specific functions in the projector constants module.
|
||||
"""
|
||||
@skip('Waiting for merge of ~alisonken1/openlp/pjlink2-resource-data')
|
||||
def build_pjlink_video_label_test(self):
|
||||
"""
|
||||
Test building PJLINK_DEFAULT_CODES dictionary
|
||||
|
@ -25,13 +25,13 @@ Package to test the openlp.core.lib.projector.pjlink1 package.
|
||||
from unittest import TestCase
|
||||
from unittest.mock import call, patch, MagicMock
|
||||
|
||||
from openlp.core.lib.projector.pjlink1 import PJLink1
|
||||
from openlp.core.lib.projector.pjlink1 import PJLink
|
||||
from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_ON, \
|
||||
PJLINK_POWR_STATUS, S_CONNECTED
|
||||
|
||||
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH
|
||||
|
||||
pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True)
|
||||
pjlink_test = PJLink(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True)
|
||||
|
||||
|
||||
class TestPJLink(TestCase):
|
||||
@ -164,23 +164,36 @@ class TestPJLink(TestCase):
|
||||
'Lamp 3 hours should have been set to 33333')
|
||||
|
||||
@patch.object(pjlink_test, 'projectorReceivedData')
|
||||
def test_projector_process_power_on(self, mock_projectorReceivedData):
|
||||
@patch.object(pjlink_test, 'projectorUpdateIcons')
|
||||
@patch.object(pjlink_test, 'send_command')
|
||||
@patch.object(pjlink_test, 'change_status')
|
||||
def test_projector_process_power_on(self, mock_change_status,
|
||||
mock_send_command,
|
||||
mock_UpdateIcons,
|
||||
mock_ReceivedData):
|
||||
"""
|
||||
Test status power to ON
|
||||
"""
|
||||
# GIVEN: Test object and preset
|
||||
pjlink = pjlink_test
|
||||
pjlink.power = S_STANDBY
|
||||
pjlink.socket_timer = MagicMock()
|
||||
|
||||
# WHEN: Call process_command with turn power on command
|
||||
pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON])
|
||||
|
||||
# THEN: Power should be set to ON
|
||||
self.assertEquals(pjlink.power, S_ON, 'Power should have been set to ON')
|
||||
mock_send_command.assert_called_once_with('INST')
|
||||
self.assertEquals(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called')
|
||||
|
||||
@patch.object(pjlink_test, 'projectorReceivedData')
|
||||
def test_projector_process_power_off(self, mock_projectorReceivedData):
|
||||
@patch.object(pjlink_test, 'projectorUpdateIcons')
|
||||
@patch.object(pjlink_test, 'send_command')
|
||||
@patch.object(pjlink_test, 'change_status')
|
||||
def test_projector_process_power_off(self, mock_change_status,
|
||||
mock_send_command,
|
||||
mock_UpdateIcons,
|
||||
mock_ReceivedData):
|
||||
"""
|
||||
Test status power to STANDBY
|
||||
"""
|
||||
@ -193,6 +206,8 @@ class TestPJLink(TestCase):
|
||||
|
||||
# THEN: Power should be set to STANDBY
|
||||
self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')
|
||||
self.assertEquals(mock_send_command.called, False, 'send_command should not have been called')
|
||||
self.assertEquals(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called')
|
||||
|
||||
@patch.object(pjlink_test, 'projectorUpdateIcons')
|
||||
def test_projector_process_avmt_closed_unmuted(self, mock_projectorReceivedData):
|
||||
@ -372,7 +387,7 @@ class TestPJLink(TestCase):
|
||||
@patch.object(pjlink_test, '_not_implemented')
|
||||
def not_implemented_test(self, mock_not_implemented):
|
||||
"""
|
||||
Test pjlink1._not_implemented method being called
|
||||
Test PJLink._not_implemented method being called
|
||||
"""
|
||||
# GIVEN: test object
|
||||
pjlink = pjlink_test
|
||||
@ -381,13 +396,13 @@ class TestPJLink(TestCase):
|
||||
# WHEN: A future command is called that is not implemented yet
|
||||
pjlink.process_command(test_cmd, "Garbage data for test only")
|
||||
|
||||
# THEN: pjlink1.__not_implemented should have been called with test_cmd
|
||||
# THEN: PJLink.__not_implemented should have been called with test_cmd
|
||||
mock_not_implemented.assert_called_with(test_cmd)
|
||||
|
||||
@patch.object(pjlink_test, 'disconnect_from_host')
|
||||
def socket_abort_test(self, mock_disconnect):
|
||||
"""
|
||||
Test PJLink1.socket_abort calls disconnect_from_host
|
||||
Test PJLink.socket_abort calls disconnect_from_host
|
||||
"""
|
||||
# GIVEN: Test object
|
||||
pjlink = pjlink_test
|
||||
@ -400,7 +415,7 @@ class TestPJLink(TestCase):
|
||||
|
||||
def poll_loop_not_connected_test(self):
|
||||
"""
|
||||
Test PJLink1.poll_loop not connected return
|
||||
Test PJLink.poll_loop not connected return
|
||||
"""
|
||||
# GIVEN: Test object and mocks
|
||||
pjlink = pjlink_test
|
||||
@ -409,7 +424,7 @@ class TestPJLink(TestCase):
|
||||
pjlink.state.return_value = False
|
||||
pjlink.ConnectedState = True
|
||||
|
||||
# WHEN: PJLink1.poll_loop called
|
||||
# WHEN: PJLink.poll_loop called
|
||||
pjlink.poll_loop()
|
||||
|
||||
# THEN: poll_loop should exit without calling any other method
|
||||
@ -418,7 +433,7 @@ class TestPJLink(TestCase):
|
||||
@patch.object(pjlink_test, 'send_command')
|
||||
def poll_loop_start_test(self, mock_send_command):
|
||||
"""
|
||||
Test PJLink1.poll_loop makes correct calls
|
||||
Test PJLink.poll_loop makes correct calls
|
||||
"""
|
||||
# GIVEN: test object and test data
|
||||
pjlink = pjlink_test
|
||||
@ -450,7 +465,7 @@ class TestPJLink(TestCase):
|
||||
call('NAME', queue=True),
|
||||
]
|
||||
|
||||
# WHEN: PJLink1.poll_loop is called
|
||||
# WHEN: PJLink.poll_loop is called
|
||||
pjlink.poll_loop()
|
||||
|
||||
# THEN: proper calls were made to retrieve projector data
|
||||
|
@ -26,13 +26,15 @@ record functions.
|
||||
PREREQUISITE: add_record() and get_all() functions validated.
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.lib.projector.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source
|
||||
from openlp.core.lib.projector.constants import PJLINK_PORT
|
||||
|
||||
from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
|
||||
from tests.resources.projector.data import TEST_DB_PJLINK1, TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
|
||||
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||
|
||||
|
||||
def compare_data(one, two):
|
||||
@ -45,7 +47,11 @@ def compare_data(one, two):
|
||||
one.port == two.port and \
|
||||
one.name == two.name and \
|
||||
one.location == two.location and \
|
||||
one.notes == two.notes
|
||||
one.notes == two.notes and \
|
||||
one.sw_version == two.sw_version and \
|
||||
one.serial_no == two.serial_no and \
|
||||
one.model_filter == two.model_filter and \
|
||||
one.model_lamp == two.model_lamp
|
||||
|
||||
|
||||
def compare_source(one, two):
|
||||
@ -168,6 +174,10 @@ class TestProjectorDB(TestCase):
|
||||
record.name = TEST3_DATA['name']
|
||||
record.location = TEST3_DATA['location']
|
||||
record.notes = TEST3_DATA['notes']
|
||||
record.sw_version = TEST3_DATA['sw_version']
|
||||
record.serial_no = TEST3_DATA['serial_no']
|
||||
record.model_filter = TEST3_DATA['model_filter']
|
||||
record.model_lamp = TEST3_DATA['model_lamp']
|
||||
updated = self.projector.update_projector(record)
|
||||
self.assertTrue(updated, 'Save updated record should have returned True')
|
||||
record = self.projector.get_projector_by_ip(TEST3_DATA['ip'])
|
||||
@ -246,7 +256,8 @@ class TestProjectorDB(TestCase):
|
||||
projector = Projector()
|
||||
|
||||
# WHEN: projector() is populated
|
||||
# NOTE: projector.pin, projector.other, projector.sources should all return None
|
||||
# NOTE: projector.[pin, other, sources, sw_version, serial_no, sw_version, model_lamp, model_filter]
|
||||
# should all return None.
|
||||
# projector.source_list should return an empty list
|
||||
projector.id = 0
|
||||
projector.ip = '127.0.0.1'
|
||||
@ -262,8 +273,9 @@ class TestProjectorDB(TestCase):
|
||||
self.assertEqual(str(projector),
|
||||
'< Projector(id="0", ip="127.0.0.1", port="4352", pin="None", name="Test One", '
|
||||
'location="Somewhere over the rainbow", notes="Not again", pjlink_name="TEST", '
|
||||
'manufacturer="IN YOUR DREAMS", model="OpenLP", other="None", sources="None", '
|
||||
'source_list="[]") >',
|
||||
'manufacturer="IN YOUR DREAMS", model="OpenLP", serial_no="None", other="None", '
|
||||
'sources="None", source_list="[]", model_filter="None", model_lamp="None", '
|
||||
'sw_version="None") >',
|
||||
'Projector.__repr__() should have returned a proper representation string')
|
||||
|
||||
def test_projectorsource_repr(self):
|
||||
|
@ -32,60 +32,21 @@ class TestThemeManager(TestCase):
|
||||
"""
|
||||
Test the functions in the ThemeManager Class
|
||||
"""
|
||||
def test_select_image_file_dialog_cancelled(self):
|
||||
"""
|
||||
Test the select image file dialog when the user presses cancel
|
||||
"""
|
||||
# GIVEN: An instance of Theme Form and mocked QFileDialog which returns an empty string (similating a user
|
||||
# pressing cancel)
|
||||
with patch('openlp.core.ui.ThemeForm._setup'),\
|
||||
patch('openlp.core.ui.themeform.get_images_filter',
|
||||
**{'return_value': 'Image Files (*.bmp; *.gif)(*.bmp *.gif)'}),\
|
||||
patch('openlp.core.ui.themeform.QtWidgets.QFileDialog.getOpenFileName',
|
||||
**{'return_value': ('', '')}) as mocked_get_open_file_name,\
|
||||
patch('openlp.core.ui.themeform.translate', **{'return_value': 'Translated String'}),\
|
||||
patch('openlp.core.ui.ThemeForm.set_background_page_values') as mocked_set_background_page_values:
|
||||
instance = ThemeForm(None)
|
||||
mocked_image_file_edit = MagicMock()
|
||||
mocked_image_file_edit.text.return_value = '/original_path/file.ext'
|
||||
instance.image_file_edit = mocked_image_file_edit
|
||||
def setUp(self):
|
||||
with patch('openlp.core.ui.ThemeForm._setup'):
|
||||
self.instance = ThemeForm(None)
|
||||
|
||||
# WHEN: on_image_browse_button is clicked
|
||||
instance.on_image_browse_button_clicked()
|
||||
def test_on_image_path_edit_path_changed(self):
|
||||
"""
|
||||
Test the `image_path_edit.pathChanged` handler
|
||||
"""
|
||||
# GIVEN: An instance of Theme Form
|
||||
with patch.object(self.instance, 'set_background_page_values') as mocked_set_background_page_values:
|
||||
self.instance.theme = MagicMock()
|
||||
|
||||
# THEN: The QFileDialog getOpenFileName and set_background_page_values moethods should have been called
|
||||
# with known arguments
|
||||
mocked_get_open_file_name.assert_called_once_with(instance, 'Translated String', '/original_path/file.ext',
|
||||
'Image Files (*.bmp; *.gif)(*.bmp *.gif);;'
|
||||
'All Files (*.*)')
|
||||
# WHEN: `on_image_path_edit_path_changed` is clicked
|
||||
self.instance.on_image_path_edit_path_changed('/new/pat.h')
|
||||
|
||||
# THEN: The theme background file should be set and `set_background_page_values` should have been called
|
||||
self.assertEqual(self.instance.theme.background_filename, '/new/pat.h')
|
||||
mocked_set_background_page_values.assert_called_once_with()
|
||||
|
||||
def test_select_image_file_dialog_new_file(self):
|
||||
"""
|
||||
Test the select image file dialog when the user presses ok
|
||||
"""
|
||||
# GIVEN: An instance of Theme Form and mocked QFileDialog which returns a file path
|
||||
with patch('openlp.core.ui.ThemeForm._setup'),\
|
||||
patch('openlp.core.ui.themeform.get_images_filter',
|
||||
**{'return_value': 'Image Files (*.bmp; *.gif)(*.bmp *.gif)'}),\
|
||||
patch('openlp.core.ui.themeform.QtWidgets.QFileDialog.getOpenFileName',
|
||||
**{'return_value': ('/new_path/file.ext', '')}) as mocked_get_open_file_name,\
|
||||
patch('openlp.core.ui.themeform.translate', **{'return_value': 'Translated String'}),\
|
||||
patch('openlp.core.ui.ThemeForm.set_background_page_values') as mocked_background_page_values:
|
||||
instance = ThemeForm(None)
|
||||
mocked_image_file_edit = MagicMock()
|
||||
mocked_image_file_edit.text.return_value = '/original_path/file.ext'
|
||||
instance.image_file_edit = mocked_image_file_edit
|
||||
instance.theme = MagicMock()
|
||||
|
||||
# WHEN: on_image_browse_button is clicked
|
||||
instance.on_image_browse_button_clicked()
|
||||
|
||||
# THEN: The QFileDialog getOpenFileName and set_background_page_values moethods should have been called
|
||||
# with known arguments and theme.background_filename should be set
|
||||
mocked_get_open_file_name.assert_called_once_with(instance, 'Translated String', '/original_path/file.ext',
|
||||
'Image Files (*.bmp; *.gif)(*.bmp *.gif);;'
|
||||
'All Files (*.*)')
|
||||
self.assertEqual(instance.theme.background_filename, '/new_path/file.ext',
|
||||
'theme.background_filename should be set to the path that the file dialog returns')
|
||||
mocked_background_page_values.assert_called_once_with()
|
||||
|
@ -20,12 +20,12 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
This module contains tests for the openlp.core.lib.filedialog module
|
||||
This module contains tests for the openlp.core.ui.lib.colorbutton module
|
||||
"""
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, call, patch
|
||||
|
||||
from openlp.core.ui.lib.colorbutton import ColorButton
|
||||
from openlp.core.ui.lib import ColorButton
|
||||
|
||||
|
||||
class TestColorDialog(TestCase):
|
||||
@ -148,11 +148,10 @@ class TestColorDialog(TestCase):
|
||||
widget.on_clicked()
|
||||
|
||||
# THEN: change_color should not have been called and the colorChanged signal should not have been emitted
|
||||
self.assertEqual(
|
||||
self.mocked_change_color.call_count, 0, 'change_color should not have been called with an invalid color')
|
||||
self.assertEqual(
|
||||
self.mocked_color_changed.emit.call_count, 0,
|
||||
'colorChange signal should not have been emitted with an invalid color')
|
||||
self.assertFalse(self.mocked_change_color.called,
|
||||
'change_color should not have been called with an invalid color')
|
||||
self.assertFalse(self.mocked_color_changed.emit.called,
|
||||
'colorChange signal should not have been emitted with an invalid color')
|
||||
|
||||
def test_on_clicked_same_color(self):
|
||||
"""
|
||||
@ -171,12 +170,10 @@ class TestColorDialog(TestCase):
|
||||
widget.on_clicked()
|
||||
|
||||
# THEN: change_color should not have been called and the colorChanged signal should not have been emitted
|
||||
self.assertEqual(
|
||||
self.mocked_change_color.call_count, 0,
|
||||
'change_color should not have been called when the color has not changed')
|
||||
self.assertEqual(
|
||||
self.mocked_color_changed.emit.call_count, 0,
|
||||
'colorChange signal should not have been emitted when the color has not changed')
|
||||
self.assertFalse(self.mocked_change_color.called,
|
||||
'change_color should not have been called when the color has not changed')
|
||||
self.assertFalse(self.mocked_color_changed.emit.called,
|
||||
'colorChange signal should not have been emitted when the color has not changed')
|
||||
|
||||
def test_on_clicked_new_color(self):
|
||||
"""
|
||||
|
311
tests/functional/openlp_core_ui_lib/test_path_edit.py
Executable file
311
tests/functional/openlp_core_ui_lib/test_path_edit.py
Executable file
@ -0,0 +1,311 @@
|
||||
# -*- 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 #
|
||||
###############################################################################
|
||||
"""
|
||||
This module contains tests for the openlp.core.ui.lib.pathedit module
|
||||
"""
|
||||
from unittest import TestCase
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.ui.lib import PathEdit, PathType
|
||||
from unittest.mock import MagicMock, PropertyMock, patch
|
||||
|
||||
|
||||
class TestPathEdit(TestCase):
|
||||
"""
|
||||
Test the :class:`~openlp.core.lib.pathedit.PathEdit` class
|
||||
"""
|
||||
def setUp(self):
|
||||
with patch('openlp.core.ui.lib.pathedit.PathEdit._setup'):
|
||||
self.widget = PathEdit()
|
||||
|
||||
def test_path_getter(self):
|
||||
"""
|
||||
Test the `path` property getter.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with the `_path` instance variable set
|
||||
self.widget._path = 'getter/test/pat.h'
|
||||
|
||||
# WHEN: Reading the `path` property
|
||||
# THEN: The value that we set should be returned
|
||||
self.assertEqual(self.widget.path, 'getter/test/pat.h')
|
||||
|
||||
def test_path_setter(self):
|
||||
"""
|
||||
Test the `path` property setter.
|
||||
"""
|
||||
# GIVEN: An instance of the PathEdit object and a mocked `line_edit`
|
||||
self.widget.line_edit = MagicMock()
|
||||
|
||||
# WHEN: Writing to the `path` property
|
||||
self.widget.path = 'setter/test/pat.h'
|
||||
|
||||
# THEN: The `_path` instance variable should be set with the test data. The `line_edit` text and tooltip
|
||||
# should have also been set.
|
||||
self.assertEqual(self.widget._path, 'setter/test/pat.h')
|
||||
self.widget.line_edit.setToolTip.assert_called_once_with('setter/test/pat.h')
|
||||
self.widget.line_edit.setText.assert_called_once_with('setter/test/pat.h')
|
||||
|
||||
def test_path_type_getter(self):
|
||||
"""
|
||||
Test the `path_type` property getter.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit
|
||||
# WHEN: Reading the `path` property
|
||||
# THEN: The default value should be returned
|
||||
self.assertEqual(self.widget.path_type, PathType.Files)
|
||||
|
||||
def test_path_type_setter(self):
|
||||
"""
|
||||
Test the `path_type` property setter.
|
||||
"""
|
||||
# GIVEN: An instance of the PathEdit object and a mocked `update_button_tool_tips` method.
|
||||
with patch.object(self.widget, 'update_button_tool_tips') as mocked_update_button_tool_tips:
|
||||
|
||||
# WHEN: Writing to a different value than default to the `path_type` property
|
||||
self.widget.path_type = PathType.Directories
|
||||
|
||||
# THEN: The `_path_type` instance variable should be set with the test data and not the default. The
|
||||
# update_button_tool_tips should have been called.
|
||||
self.assertEqual(self.widget._path_type, PathType.Directories)
|
||||
mocked_update_button_tool_tips.assert_called_once_with()
|
||||
|
||||
def test_update_button_tool_tips_directories(self):
|
||||
"""
|
||||
Test the `update_button_tool_tips` method.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with the `path_type` set to `Directories`
|
||||
self.widget.browse_button = MagicMock()
|
||||
self.widget.revert_button = MagicMock()
|
||||
self.widget._path_type = PathType.Directories
|
||||
|
||||
# WHEN: Calling update_button_tool_tips
|
||||
self.widget.update_button_tool_tips()
|
||||
|
||||
self.widget.browse_button.setToolTip.assert_called_once_with('Browse for directory.')
|
||||
self.widget.revert_button.setToolTip.assert_called_once_with('Revert to default directory.')
|
||||
|
||||
def test_update_button_tool_tips_files(self):
|
||||
"""
|
||||
Test the `update_button_tool_tips` method.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with the `path_type` set to `Files`
|
||||
self.widget.browse_button = MagicMock()
|
||||
self.widget.revert_button = MagicMock()
|
||||
self.widget._path_type = PathType.Files
|
||||
|
||||
# WHEN: Calling update_button_tool_tips
|
||||
self.widget.update_button_tool_tips()
|
||||
|
||||
self.widget.browse_button.setToolTip.assert_called_once_with('Browse for file.')
|
||||
self.widget.revert_button.setToolTip.assert_called_once_with('Revert to default file.')
|
||||
|
||||
def test_on_browse_button_clicked_directory(self):
|
||||
"""
|
||||
Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Directories.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked
|
||||
# QFileDialog.getExistingDirectory
|
||||
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory', return_value='') as \
|
||||
mocked_get_existing_directory, \
|
||||
patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName') as \
|
||||
mocked_get_open_file_name, \
|
||||
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
|
||||
self.widget._path_type = PathType.Directories
|
||||
self.widget._path = 'test/path/'
|
||||
|
||||
# WHEN: Calling on_browse_button_clicked
|
||||
self.widget.on_browse_button_clicked()
|
||||
|
||||
# THEN: The FileDialog.getExistingDirectory should have been called with the default caption
|
||||
mocked_get_existing_directory.assert_called_once_with(self.widget, 'Select Directory', 'test/path/',
|
||||
QtWidgets.QFileDialog.ShowDirsOnly)
|
||||
self.assertFalse(mocked_get_open_file_name.called)
|
||||
self.assertFalse(mocked_normpath.called)
|
||||
|
||||
def test_on_browse_button_clicked_directory_custom_caption(self):
|
||||
"""
|
||||
Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Directories,
|
||||
and `dialog_caption` is set.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked
|
||||
# QFileDialog.getExistingDirectory with `default_caption` set.
|
||||
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory', return_value='') as \
|
||||
mocked_get_existing_directory, \
|
||||
patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName') as \
|
||||
mocked_get_open_file_name, \
|
||||
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
|
||||
self.widget._path_type = PathType.Directories
|
||||
self.widget._path = 'test/path/'
|
||||
self.widget.dialog_caption = 'Directory Caption'
|
||||
|
||||
# WHEN: Calling on_browse_button_clicked
|
||||
self.widget.on_browse_button_clicked()
|
||||
|
||||
# THEN: The FileDialog.getExistingDirectory should have been called with the custom caption
|
||||
mocked_get_existing_directory.assert_called_once_with(self.widget, 'Directory Caption', 'test/path/',
|
||||
QtWidgets.QFileDialog.ShowDirsOnly)
|
||||
self.assertFalse(mocked_get_open_file_name.called)
|
||||
self.assertFalse(mocked_normpath.called)
|
||||
|
||||
def test_on_browse_button_clicked_file(self):
|
||||
"""
|
||||
Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName
|
||||
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory') as \
|
||||
mocked_get_existing_directory, \
|
||||
patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \
|
||||
mocked_get_open_file_name, \
|
||||
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
|
||||
self.widget._path_type = PathType.Files
|
||||
self.widget._path = 'test/pat.h'
|
||||
|
||||
# WHEN: Calling on_browse_button_clicked
|
||||
self.widget.on_browse_button_clicked()
|
||||
|
||||
# THEN: The FileDialog.getOpenFileName should have been called with the default caption
|
||||
mocked_get_open_file_name.assert_called_once_with(self.widget, 'Select File', 'test/pat.h',
|
||||
self.widget.filters)
|
||||
self.assertFalse(mocked_get_existing_directory.called)
|
||||
self.assertFalse(mocked_normpath.called)
|
||||
|
||||
def test_on_browse_button_clicked_file_custom_caption(self):
|
||||
"""
|
||||
Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files and
|
||||
`dialog_caption` is set.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName
|
||||
# with `default_caption` set.
|
||||
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getExistingDirectory') as \
|
||||
mocked_get_existing_directory, \
|
||||
patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \
|
||||
mocked_get_open_file_name, \
|
||||
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
|
||||
self.widget._path_type = PathType.Files
|
||||
self.widget._path = 'test/pat.h'
|
||||
self.widget.dialog_caption = 'File Caption'
|
||||
|
||||
# WHEN: Calling on_browse_button_clicked
|
||||
self.widget.on_browse_button_clicked()
|
||||
|
||||
# THEN: The FileDialog.getOpenFileName should have been called with the custom caption
|
||||
mocked_get_open_file_name.assert_called_once_with(self.widget, 'File Caption', 'test/pat.h',
|
||||
self.widget.filters)
|
||||
self.assertFalse(mocked_get_existing_directory.called)
|
||||
self.assertFalse(mocked_normpath.called)
|
||||
|
||||
def test_on_browse_button_clicked_user_cancels(self):
|
||||
"""
|
||||
Test the `browse_button` `clicked` handler on_browse_button_clicked when the user cancels the FileDialog (an
|
||||
empty str is returned)
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns an empty str for the
|
||||
# file path.
|
||||
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')) as \
|
||||
mocked_get_open_file_name, \
|
||||
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath:
|
||||
|
||||
# WHEN: Calling on_browse_button_clicked
|
||||
self.widget.on_browse_button_clicked()
|
||||
|
||||
# THEN: normpath should not have been called
|
||||
self.assertTrue(mocked_get_open_file_name.called)
|
||||
self.assertFalse(mocked_normpath.called)
|
||||
|
||||
def test_on_browse_button_clicked_user_accepts(self):
|
||||
"""
|
||||
Test the `browse_button` `clicked` handler on_browse_button_clicked when the user accepts the FileDialog (a path
|
||||
is returned)
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns a str for the file
|
||||
# path.
|
||||
with patch('openlp.core.ui.lib.pathedit.QtWidgets.QFileDialog.getOpenFileName',
|
||||
return_value=('/test/pat.h', '')) as mocked_get_open_file_name, \
|
||||
patch('openlp.core.ui.lib.pathedit.os.path.normpath') as mocked_normpath, \
|
||||
patch.object(self.widget, 'on_new_path'):
|
||||
|
||||
# WHEN: Calling on_browse_button_clicked
|
||||
self.widget.on_browse_button_clicked()
|
||||
|
||||
# THEN: normpath and `on_new_path` should have been called
|
||||
self.assertTrue(mocked_get_open_file_name.called)
|
||||
mocked_normpath.assert_called_once_with('/test/pat.h')
|
||||
self.assertTrue(self.widget.on_new_path.called)
|
||||
|
||||
def test_on_revert_button_clicked(self):
|
||||
"""
|
||||
Test that the default path is set as the path when the `revert_button.clicked` handler is called.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with a mocked `on_new_path`, and the `default_path` set.
|
||||
with patch.object(self.widget, 'on_new_path') as mocked_on_new_path:
|
||||
self.widget.default_path = '/default/pat.h'
|
||||
|
||||
# WHEN: Calling `on_revert_button_clicked`
|
||||
self.widget.on_revert_button_clicked()
|
||||
|
||||
# THEN: on_new_path should have been called with the default path
|
||||
mocked_on_new_path.assert_called_once_with('/default/pat.h')
|
||||
|
||||
def test_on_line_edit_editing_finished(self):
|
||||
"""
|
||||
Test that the new path is set as the path when the `line_edit.editingFinished` handler is called.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with a mocked `line_edit` and `on_new_path`.
|
||||
with patch.object(self.widget, 'on_new_path') as mocked_on_new_path:
|
||||
self.widget.line_edit = MagicMock(**{'text.return_value': '/test/pat.h'})
|
||||
|
||||
# WHEN: Calling `on_line_edit_editing_finished`
|
||||
self.widget.on_line_edit_editing_finished()
|
||||
|
||||
# THEN: on_new_path should have been called with the path enetered in `line_edit`
|
||||
mocked_on_new_path.assert_called_once_with('/test/pat.h')
|
||||
|
||||
def test_on_new_path_no_change(self):
|
||||
"""
|
||||
Test `on_new_path` when called with a path that is the same as the existing path.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal
|
||||
with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock):
|
||||
self.widget._path = '/old/test/pat.h'
|
||||
self.widget.pathChanged = MagicMock()
|
||||
|
||||
# WHEN: Calling `on_new_path` with the same path as the existing path
|
||||
self.widget.on_new_path('/old/test/pat.h')
|
||||
|
||||
# THEN: The `pathChanged` signal should not be emitted
|
||||
self.assertFalse(self.widget.pathChanged.emit.called)
|
||||
|
||||
def test_on_new_path_change(self):
|
||||
"""
|
||||
Test `on_new_path` when called with a path that is the different to the existing path.
|
||||
"""
|
||||
# GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal
|
||||
with patch('openlp.core.ui.lib.pathedit.PathEdit.path', new_callable=PropertyMock):
|
||||
self.widget._path = '/old/test/pat.h'
|
||||
self.widget.pathChanged = MagicMock()
|
||||
|
||||
# WHEN: Calling `on_new_path` with the a new path
|
||||
self.widget.on_new_path('/new/test/pat.h')
|
||||
|
||||
# THEN: The `pathChanged` signal should be emitted
|
||||
self.widget.pathChanged.emit.assert_called_once_with('/new/test/pat.h')
|
@ -187,7 +187,7 @@ class TestEasyWorshipSongImport(TestCase):
|
||||
|
||||
def test_find_field_exists(self):
|
||||
"""
|
||||
Test finding an existing field in a given list using the :mod:`find_field`
|
||||
Test finding an existing field in a given list using the :mod:`db_find_field`
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions.
|
||||
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
|
||||
@ -201,11 +201,11 @@ class TestEasyWorshipSongImport(TestCase):
|
||||
for field_name in existing_fields:
|
||||
|
||||
# THEN: The item corresponding the index returned should have the same name attribute
|
||||
self.assertEqual(importer.field_descriptions[importer.find_field(field_name)].name, field_name)
|
||||
self.assertEqual(importer.field_descriptions[importer.db_find_field(field_name)].name, field_name)
|
||||
|
||||
def test_find_non_existing_field(self):
|
||||
"""
|
||||
Test finding an non-existing field in a given list using the :mod:`find_field`
|
||||
Test finding an non-existing field in a given list using the :mod:`db_find_field`
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions
|
||||
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
|
||||
@ -218,11 +218,11 @@ class TestEasyWorshipSongImport(TestCase):
|
||||
for field_name in non_existing_fields:
|
||||
|
||||
# THEN: The importer object should not be None
|
||||
self.assertRaises(IndexError, importer.find_field, field_name)
|
||||
self.assertRaises(IndexError, importer.db_find_field, field_name)
|
||||
|
||||
def test_set_record_struct(self):
|
||||
"""
|
||||
Test the :mod:`set_record_struct` module
|
||||
Test the :mod:`db_set_record_struct` module
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out struct class, and a mocked out "manager" and a list of
|
||||
# field descriptions
|
||||
@ -231,17 +231,17 @@ class TestEasyWorshipSongImport(TestCase):
|
||||
mocked_manager = MagicMock()
|
||||
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
|
||||
|
||||
# WHEN: set_record_struct is called with a list of field descriptions
|
||||
return_value = importer.set_record_struct(TEST_FIELD_DESCS)
|
||||
# WHEN: db_set_record_struct is called with a list of field descriptions
|
||||
return_value = importer.db_set_record_struct(TEST_FIELD_DESCS)
|
||||
|
||||
# THEN: set_record_struct should return None and Struct should be called with a value representing
|
||||
# THEN: db_set_record_struct should return None and Struct should be called with a value representing
|
||||
# the list of field descriptions
|
||||
self.assertIsNone(return_value, 'set_record_struct should return None')
|
||||
self.assertIsNone(return_value, 'db_set_record_struct should return None')
|
||||
mocked_struct.Struct.assert_called_with('>50sHIB250s250s10sQ')
|
||||
|
||||
def test_get_field(self):
|
||||
"""
|
||||
Test the :mod:`get_field` module
|
||||
Test the :mod:`db_get_field` module
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results
|
||||
with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
|
||||
@ -254,16 +254,16 @@ class TestEasyWorshipSongImport(TestCase):
|
||||
|
||||
# WHEN: Called with test data
|
||||
for field_index, result in field_results:
|
||||
return_value = importer.get_field(field_index)
|
||||
return_value = importer.db_get_field(field_index)
|
||||
|
||||
# THEN: get_field should return the known results
|
||||
# THEN: db_get_field should return the known results
|
||||
self.assertEqual(return_value, result,
|
||||
'get_field should return "%s" when called with "%s"' %
|
||||
'db_get_field should return "%s" when called with "%s"' %
|
||||
(result, TEST_FIELDS[field_index]))
|
||||
|
||||
def test_get_memo_field(self):
|
||||
"""
|
||||
Test the :mod:`get_field` module
|
||||
Test the :mod:`db_get_field` module
|
||||
"""
|
||||
for test_results in GET_MEMO_FIELD_TEST_RESULTS:
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager", a mocked out memo_file and an encoding
|
||||
@ -283,8 +283,9 @@ class TestEasyWorshipSongImport(TestCase):
|
||||
get_field_read_calls = test_results[2]['read']
|
||||
get_field_seek_calls = test_results[2]['seek']
|
||||
|
||||
# THEN: get_field should return the appropriate value with the appropriate mocked objects being called
|
||||
self.assertEqual(importer.get_field(field_index), get_field_result)
|
||||
# THEN: db_get_field should return the appropriate value with the appropriate mocked objects being
|
||||
# called
|
||||
self.assertEqual(importer.db_get_field(field_index), get_field_result)
|
||||
for call in get_field_read_calls:
|
||||
mocked_memo_file.read.assert_any_call(call)
|
||||
for call in get_field_seek_calls:
|
||||
@ -405,6 +406,12 @@ class TestEasyWorshipSongImport(TestCase):
|
||||
mocked_retrieve_windows_encoding.assert_any_call(encoding)
|
||||
|
||||
def test_db_file_import(self):
|
||||
return self._test_db_file_import(os.path.join(TEST_PATH, 'Songs.DB'))
|
||||
|
||||
def test_sqlite_db_file_import(self):
|
||||
return self._test_db_file_import(os.path.join(TEST_PATH, 'ew6'))
|
||||
|
||||
def _test_db_file_import(self, source_path):
|
||||
"""
|
||||
Test the actual import of real song database files and check that the imported data is correct.
|
||||
"""
|
||||
@ -432,7 +439,7 @@ class TestEasyWorshipSongImport(TestCase):
|
||||
importer.topics = []
|
||||
|
||||
# WHEN: Importing each file
|
||||
importer.import_source = os.path.join(TEST_PATH, 'Songs.DB')
|
||||
importer.import_source = source_path
|
||||
import_result = importer.do_import()
|
||||
|
||||
# THEN: do_import should return none, the song data should be as expected, and finish should have been
|
||||
|
@ -28,11 +28,12 @@ from unittest.mock import MagicMock, patch
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.plugins.bibles.forms import bibleimportform
|
||||
from openlp.plugins.bibles.forms.bibleimportform import BibleImportForm, PYSWORD_AVAILABLE
|
||||
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
|
||||
@skip('One of the QFormLayouts in the BibleImportForm is causing a segfault')
|
||||
class TestBibleImportForm(TestCase, TestMixin):
|
||||
"""
|
||||
Test the BibleImportForm class
|
||||
@ -46,9 +47,9 @@ class TestBibleImportForm(TestCase, TestMixin):
|
||||
self.setup_application()
|
||||
self.main_window = QtWidgets.QMainWindow()
|
||||
Registry().register('main_window', self.main_window)
|
||||
bibleimportform.PYSWORD_AVAILABLE = False
|
||||
PYSWORD_AVAILABLE = False
|
||||
self.mocked_manager = MagicMock()
|
||||
self.form = bibleimportform.BibleImportForm(self.main_window, self.mocked_manager, MagicMock())
|
||||
self.form = BibleImportForm(self.main_window, self.mocked_manager, MagicMock())
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
|
BIN
tests/resources/easyworshipsongs/ew6/Databases/Data/SongWords.db
Normal file
BIN
tests/resources/easyworshipsongs/ew6/Databases/Data/SongWords.db
Normal file
Binary file not shown.
BIN
tests/resources/easyworshipsongs/ew6/Databases/Data/Songs.db
Normal file
BIN
tests/resources/easyworshipsongs/ew6/Databases/Data/Songs.db
Normal file
Binary file not shown.
@ -27,6 +27,8 @@ import os
|
||||
from tempfile import gettempdir
|
||||
|
||||
# Test data
|
||||
TEST_DB_PJLINK1 = 'projector_pjlink1.sqlite'
|
||||
|
||||
TEST_DB = os.path.join(gettempdir(), 'openlp-test-projectordb.sql')
|
||||
|
||||
TEST_SALT = '498e4a67'
|
||||
@ -44,21 +46,33 @@ TEST1_DATA = dict(ip='111.111.111.111',
|
||||
pin='1111',
|
||||
name='___TEST_ONE___',
|
||||
location='location one',
|
||||
notes='notes one')
|
||||
notes='notes one',
|
||||
serial_no='Serial Number 1',
|
||||
sw_version='Version 1',
|
||||
model_filter='Filter type 1',
|
||||
model_lamp='Lamp type 1')
|
||||
|
||||
TEST2_DATA = dict(ip='222.222.222.222',
|
||||
port='2222',
|
||||
pin='2222',
|
||||
name='___TEST_TWO___',
|
||||
location='location two',
|
||||
notes='notes two')
|
||||
notes='notes one',
|
||||
serial_no='Serial Number 2',
|
||||
sw_version='Version 2',
|
||||
model_filter='Filter type 2',
|
||||
model_lamp='Lamp type 2')
|
||||
|
||||
TEST3_DATA = dict(ip='333.333.333.333',
|
||||
port='3333',
|
||||
pin='3333',
|
||||
name='___TEST_THREE___',
|
||||
location='location three',
|
||||
notes='notes three')
|
||||
notes='notes one',
|
||||
serial_no='Serial Number 3',
|
||||
sw_version='Version 3',
|
||||
model_filter='Filter type 3',
|
||||
model_lamp='Lamp type 3')
|
||||
|
||||
TEST_VIDEO_CODES = {
|
||||
'11': 'RGB 1',
|
||||
|
BIN
tests/resources/projector/projector_pjlink1.sqlite
Normal file
BIN
tests/resources/projector/projector_pjlink1.sqlite
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user