forked from openlp/openlp
Trunk
This commit is contained in:
commit
ed66c5cf34
@ -1 +1 @@
|
||||
2.1.0-bzr2141
|
||||
2.1.0-bzr2234
|
||||
|
@ -111,10 +111,10 @@ class OpenLP(QtGui.QApplication):
|
||||
# Decide how many screens we have and their size
|
||||
screens = ScreenList.create(self.desktop())
|
||||
# First time checks in settings
|
||||
has_run_wizard = Settings().value(u'general/has run wizard')
|
||||
has_run_wizard = Settings().value(u'core/has run wizard')
|
||||
if not has_run_wizard:
|
||||
if FirstTimeForm(screens).exec_() == QtGui.QDialog.Accepted:
|
||||
Settings().setValue(u'general/has run wizard', True)
|
||||
Settings().setValue(u'core/has run wizard', True)
|
||||
# Correct stylesheet bugs
|
||||
application_stylesheet = u''
|
||||
if not Settings().value(u'advanced/alternate rows'):
|
||||
@ -126,7 +126,7 @@ class OpenLP(QtGui.QApplication):
|
||||
application_stylesheet += NT_REPAIR_STYLESHEET
|
||||
if application_stylesheet:
|
||||
self.setStyleSheet(application_stylesheet)
|
||||
show_splash = Settings().value(u'general/show splash')
|
||||
show_splash = Settings().value(u'core/show splash')
|
||||
if show_splash:
|
||||
self.splash = SplashScreen()
|
||||
self.splash.show()
|
||||
@ -147,7 +147,7 @@ class OpenLP(QtGui.QApplication):
|
||||
self.processEvents()
|
||||
if not has_run_wizard:
|
||||
self.main_window.first_time()
|
||||
update_check = Settings().value(u'general/update check')
|
||||
update_check = Settings().value(u'core/update check')
|
||||
if update_check:
|
||||
VersionThread(self.main_window).start()
|
||||
self.main_window.is_display_blank()
|
||||
@ -305,8 +305,10 @@ def main(args=None):
|
||||
# Instance check
|
||||
if application.is_already_running():
|
||||
sys.exit()
|
||||
# Remove/convert obsolete settings.
|
||||
Settings().remove_obsolete_settings()
|
||||
# First time checks in settings
|
||||
if not Settings().value(u'general/has run wizard'):
|
||||
if not Settings().value(u'core/has run wizard'):
|
||||
if not FirstTimeLanguageForm().exec_():
|
||||
# if cancel then stop processing
|
||||
sys.exit()
|
||||
|
@ -30,6 +30,7 @@
|
||||
The :mod:`lib` module contains most of the components and libraries that make
|
||||
OpenLP work.
|
||||
"""
|
||||
from __future__ import division
|
||||
from distutils.version import LooseVersion
|
||||
import logging
|
||||
import os
|
||||
@ -155,7 +156,7 @@ def build_icon(icon):
|
||||
|
||||
``icon``
|
||||
The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file
|
||||
location like ``/path/to/file.png``.
|
||||
location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
|
||||
"""
|
||||
button_icon = QtGui.QIcon()
|
||||
if isinstance(icon, QtGui.QIcon):
|
||||
@ -202,12 +203,13 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
||||
States if an icon should be build and returned from the thumb. Defaults to ``True``.
|
||||
|
||||
``size``
|
||||
Allows to state a own size to use. Defaults to ``None``, which means that a default height of 88 is used.
|
||||
Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default height of 88
|
||||
is used.
|
||||
"""
|
||||
ext = os.path.splitext(thumb_path)[1].lower()
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
if size is None:
|
||||
ratio = float(reader.size().width()) / float(reader.size().height())
|
||||
ratio = reader.size().width() / reader.size().height()
|
||||
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
|
||||
else:
|
||||
reader.setScaledSize(size)
|
||||
@ -260,8 +262,8 @@ def resize_image(image_path, width, height, background=u'#000000'):
|
||||
log.debug(u'resize_image - start')
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
# The image's ratio.
|
||||
image_ratio = float(reader.size().width()) / float(reader.size().height())
|
||||
resize_ratio = float(width) / float(height)
|
||||
image_ratio = reader.size().width() / reader.size().height()
|
||||
resize_ratio = width / height
|
||||
# Figure out the size we want to resize the image to (keep aspect ratio).
|
||||
if image_ratio == resize_ratio:
|
||||
size = QtCore.QSize(width, height)
|
||||
@ -282,7 +284,7 @@ def resize_image(image_path, width, height, background=u'#000000'):
|
||||
new_image = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
painter = QtGui.QPainter(new_image)
|
||||
painter.fillRect(new_image.rect(), QtGui.QColor(background))
|
||||
painter.drawImage((width - real_width) / 2, (height - real_height) / 2, preview)
|
||||
painter.drawImage((width - real_width) // 2, (height - real_height) // 2, preview)
|
||||
return new_image
|
||||
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
"""
|
||||
Provide additional functionality required by OpenLP from the inherited QDockWidget.
|
||||
"""
|
||||
from __future__ import division
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui
|
||||
@ -55,7 +56,7 @@ class OpenLPDockWidget(QtGui.QDockWidget):
|
||||
self.setWindowIcon(build_icon(icon))
|
||||
# Sort out the minimum width.
|
||||
screens = ScreenList()
|
||||
main_window_docbars = screens.current[u'size'].width() / 5
|
||||
main_window_docbars = screens.current[u'size'].width() // 5
|
||||
if main_window_docbars > 300:
|
||||
self.setMinimumWidth(300)
|
||||
else:
|
||||
|
@ -26,7 +26,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import division
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtWebKit
|
||||
@ -276,7 +276,7 @@ def build_background_css(item, width):
|
||||
``item``
|
||||
Service Item containing theme and location information
|
||||
"""
|
||||
width = int(width) / 2
|
||||
width = int(width) // 2
|
||||
theme = item.themedata
|
||||
background = u'background-color: black'
|
||||
if theme:
|
||||
|
@ -102,7 +102,9 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
self.setupUi()
|
||||
self.retranslateUi()
|
||||
self.auto_select_id = -1
|
||||
Registry().register_function(u'%s_service_load' % self.plugin.name, self.service_load)
|
||||
# Need to use event as called across threads and UI is updated
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_go_live' % self.plugin.name), self.go_live_remote)
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_add_to_service' % self.plugin.name), self.add_to_service_remote)
|
||||
|
||||
def required_icons(self):
|
||||
"""
|
||||
@ -424,7 +426,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
"""
|
||||
raise NotImplementedError(u'MediaManagerItem.on_delete_click needs to be defined by the plugin')
|
||||
|
||||
def onFocus(self):
|
||||
def on_focus(self):
|
||||
"""
|
||||
Run when a tab in the media manager gains focus. This gives the media
|
||||
item a chance to focus any elements it wants to.
|
||||
@ -481,6 +483,15 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
else:
|
||||
self.go_live()
|
||||
|
||||
def go_live_remote(self, message):
|
||||
"""
|
||||
Remote Call wrapper
|
||||
|
||||
``message``
|
||||
The passed data item_id:Remote.
|
||||
"""
|
||||
self.go_live(message[0], remote=message[1])
|
||||
|
||||
def go_live(self, item_id=None, remote=False):
|
||||
"""
|
||||
Make the currently selected item go live.
|
||||
@ -523,6 +534,15 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
for item in items:
|
||||
self.add_to_service(item)
|
||||
|
||||
def add_to_service_remote(self, message):
|
||||
"""
|
||||
Remote Call wrapper
|
||||
|
||||
``message``
|
||||
The passed data item:Remote.
|
||||
"""
|
||||
self.add_to_service(message[0], remote=message[1])
|
||||
|
||||
def add_to_service(self, item=None, replace=None, remote=False):
|
||||
"""
|
||||
Add this item to the current service.
|
||||
@ -564,12 +584,15 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
else:
|
||||
return None
|
||||
|
||||
def service_load(self, message):
|
||||
def service_load(self, item):
|
||||
"""
|
||||
Method to add processing when a service has been loaded and individual service items need to be processed by the
|
||||
plugins.
|
||||
|
||||
``item``
|
||||
The item to be processed and returned.
|
||||
"""
|
||||
pass
|
||||
return item
|
||||
|
||||
def check_search_result(self):
|
||||
"""
|
||||
|
@ -103,7 +103,7 @@ class Plugin(QtCore.QObject):
|
||||
``add_export_menu_Item(export_menu)``
|
||||
Add an item to the Export menu.
|
||||
|
||||
``create_settings_Tab()``
|
||||
``create_settings_tab()``
|
||||
Creates a new instance of SettingsTabItem to be used in the Settings
|
||||
dialog.
|
||||
|
||||
@ -252,7 +252,7 @@ class Plugin(QtCore.QObject):
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_settings_Tab(self, parent):
|
||||
def create_settings_tab(self, parent):
|
||||
"""
|
||||
Create a tab for the settings window to display the configurable options
|
||||
for this plugin to the user.
|
||||
|
@ -153,7 +153,7 @@ class PluginManager(object):
|
||||
"""
|
||||
for plugin in self.plugins:
|
||||
if plugin.status is not PluginStatus.Disabled:
|
||||
plugin.create_settings_Tab(self.settings_form)
|
||||
plugin.create_settings_tab(self.settings_form)
|
||||
|
||||
def hook_import_menu(self):
|
||||
"""
|
||||
|
@ -26,7 +26,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import division
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui, QtCore, QtWebKit
|
||||
@ -327,7 +327,7 @@ class Renderer(object):
|
||||
screen_size = self.screens.current[u'size']
|
||||
self.width = screen_size.width()
|
||||
self.height = screen_size.height()
|
||||
self.screen_ratio = float(self.height) / float(self.width)
|
||||
self.screen_ratio = self.height / self.width
|
||||
log.debug(u'_calculate default %s, %f' % (screen_size, self.screen_ratio))
|
||||
# 90% is start of footer
|
||||
self.footer_start = int(self.height * 0.90)
|
||||
@ -546,15 +546,15 @@ class Renderer(object):
|
||||
"""
|
||||
smallest_index = 0
|
||||
highest_index = len(html_list) - 1
|
||||
index = int(highest_index / 2)
|
||||
index = highest_index // 2
|
||||
while True:
|
||||
if not self._text_fits_on_slide(previous_html + separator.join(html_list[:index + 1]).strip()):
|
||||
# We know that it does not fit, so change/calculate the new index and highest_index accordingly.
|
||||
highest_index = index
|
||||
index = int(index - (index - smallest_index) / 2)
|
||||
index = index - (index - smallest_index) // 2
|
||||
else:
|
||||
smallest_index = index
|
||||
index = int(index + (highest_index - index) / 2)
|
||||
index = index + (highest_index - index) // 2
|
||||
# We found the number of words which will fit.
|
||||
if smallest_index == index or highest_index == index:
|
||||
index = smallest_index
|
||||
@ -582,7 +582,7 @@ class Renderer(object):
|
||||
html_list[0] = html_tags + html_list[0]
|
||||
smallest_index = 0
|
||||
highest_index = len(html_list) - 1
|
||||
index = int(highest_index / 2)
|
||||
index = highest_index // 2
|
||||
return previous_html, previous_raw
|
||||
|
||||
def _text_fits_on_slide(self, text):
|
||||
|
@ -30,6 +30,7 @@
|
||||
The :mod:`screen` module provides management functionality for a machines'
|
||||
displays.
|
||||
"""
|
||||
from __future__ import division
|
||||
import logging
|
||||
import copy
|
||||
|
||||
@ -232,8 +233,8 @@ class ScreenList(object):
|
||||
``window``
|
||||
A QWidget we are finding the location of.
|
||||
"""
|
||||
x = window.x() + (window.width() / 2)
|
||||
y = window.y() + (window.height() / 2)
|
||||
x = window.x() + (window.width() // 2)
|
||||
y = window.y() + (window.height() // 2)
|
||||
for screen in self.screen_list:
|
||||
size = screen[u'size']
|
||||
if x >= size.x() and x <= (size.x() + size.width()) and y >= size.y() and y <= (size.y() + size.height()):
|
||||
@ -247,15 +248,15 @@ class ScreenList(object):
|
||||
# Add the screen settings to the settings dict. This has to be done here due to cyclic dependency.
|
||||
# Do not do this anywhere else.
|
||||
screen_settings = {
|
||||
u'general/x position': self.current[u'size'].x(),
|
||||
u'general/y position': self.current[u'size'].y(),
|
||||
u'general/monitor': self.display_count - 1,
|
||||
u'general/height': self.current[u'size'].height(),
|
||||
u'general/width': self.current[u'size'].width()
|
||||
u'core/x position': self.current[u'size'].x(),
|
||||
u'core/y position': self.current[u'size'].y(),
|
||||
u'core/monitor': self.display_count - 1,
|
||||
u'core/height': self.current[u'size'].height(),
|
||||
u'core/width': self.current[u'size'].width()
|
||||
}
|
||||
Settings.extend_default_settings(screen_settings)
|
||||
settings = Settings()
|
||||
settings.beginGroup(u'general')
|
||||
settings.beginGroup(u'core')
|
||||
monitor = settings.value(u'monitor')
|
||||
self.set_current_display(monitor)
|
||||
self.display = settings.value(u'display on monitor')
|
||||
|
@ -26,7 +26,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from __future__ import division
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
@ -85,10 +85,10 @@ class SearchEdit(QtGui.QLineEdit):
|
||||
size = self.clear_button.size()
|
||||
frame_width = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
|
||||
self.clear_button.move(self.rect().right() - frame_width - size.width(),
|
||||
(self.rect().bottom() + 1 - size.height()) / 2)
|
||||
(self.rect().bottom() + 1 - size.height()) // 2)
|
||||
if hasattr(self, u'menu_button'):
|
||||
size = self.menu_button.size()
|
||||
self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) / 2)
|
||||
self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) // 2)
|
||||
|
||||
def current_search_type(self):
|
||||
"""
|
||||
|
@ -58,23 +58,19 @@ class ItemCapabilities(object):
|
||||
Provides an enumeration of a service item's capabilities
|
||||
|
||||
``CanPreview``
|
||||
The capability to allow the ServiceManager to add to the preview
|
||||
tab when making the previous item live.
|
||||
The capability to allow the ServiceManager to add to the preview tab when making the previous item live.
|
||||
|
||||
``CanEdit``
|
||||
The capability to allow the ServiceManager to allow the item to be
|
||||
edited
|
||||
The capability to allow the ServiceManager to allow the item to be edited
|
||||
|
||||
``CanMaintain``
|
||||
The capability to allow the ServiceManager to allow the item to be
|
||||
reordered.
|
||||
The capability to allow the ServiceManager to allow the item to be reordered.
|
||||
|
||||
``RequiresMedia``
|
||||
Determines is the service_item needs a Media Player
|
||||
|
||||
``CanLoop``
|
||||
The capability to allow the SlideController to allow the loop
|
||||
processing.
|
||||
The capability to allow the SlideController to allow the loop processing.
|
||||
|
||||
``CanAppend``
|
||||
The capability to allow the ServiceManager to add leaves to the
|
||||
@ -84,22 +80,19 @@ class ItemCapabilities(object):
|
||||
The capability to remove lines breaks in the renderer
|
||||
|
||||
``OnLoadUpdate``
|
||||
The capability to update MediaManager when a service Item is
|
||||
loaded.
|
||||
The capability to update MediaManager when a service Item is loaded.
|
||||
|
||||
``AddIfNewItem``
|
||||
Not Used
|
||||
|
||||
``ProvidesOwnDisplay``
|
||||
The capability to tell the SlideController the service Item has a
|
||||
different display.
|
||||
The capability to tell the SlideController the service Item has a different display.
|
||||
|
||||
``HasDetailedTitleDisplay``
|
||||
ServiceItem provides a title
|
||||
Being Removed and decommissioned.
|
||||
|
||||
``HasVariableStartTime``
|
||||
The capability to tell the ServiceManager that a change to start
|
||||
time is possible.
|
||||
The capability to tell the ServiceManager that a change to start time is possible.
|
||||
|
||||
``CanSoftBreak``
|
||||
The capability to tell the renderer that Soft Break is allowed
|
||||
@ -156,7 +149,7 @@ class ServiceItem(object):
|
||||
if plugin:
|
||||
self.name = plugin.name
|
||||
self.title = u''
|
||||
self.shortname = u''
|
||||
self.processor = None
|
||||
self.audit = u''
|
||||
self.items = []
|
||||
self.iconic_representation = None
|
||||
@ -360,7 +353,8 @@ class ServiceItem(object):
|
||||
u'media_length': self.media_length,
|
||||
u'background_audio': self.background_audio,
|
||||
u'theme_overwritten': self.theme_overwritten,
|
||||
u'will_auto_start': self.will_auto_start
|
||||
u'will_auto_start': self.will_auto_start,
|
||||
u'processor': self.processor
|
||||
}
|
||||
service_data = []
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
@ -394,7 +388,6 @@ class ServiceItem(object):
|
||||
self.title = header[u'title']
|
||||
self.name = header[u'name']
|
||||
self.service_item_type = header[u'type']
|
||||
self.shortname = header[u'plugin']
|
||||
self.theme = header[u'theme']
|
||||
self.add_icon(header[u'icon'])
|
||||
self.raw_footer = header[u'footer']
|
||||
@ -413,7 +406,13 @@ class ServiceItem(object):
|
||||
self.auto_play_slides_loop = header.get(u'auto_play_slides_loop', False)
|
||||
self.timed_slide_interval = header.get(u'timed_slide_interval', 0)
|
||||
self.will_auto_start = header.get(u'will_auto_start', False)
|
||||
self.processor = header.get(u'processor', None)
|
||||
self.has_original_files = True
|
||||
#TODO Remove me in 2,3 build phase
|
||||
if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay):
|
||||
self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay)
|
||||
self.processor = self.title
|
||||
self.title = None
|
||||
if u'background_audio' in header:
|
||||
self.background_audio = []
|
||||
for filename in header[u'background_audio']:
|
||||
@ -436,6 +435,8 @@ class ServiceItem(object):
|
||||
self.add_from_image(text_image[u'path'], text_image[u'title'], background)
|
||||
elif self.service_item_type == ServiceItemType.Command:
|
||||
for text_image in serviceitem[u'serviceitem'][u'data']:
|
||||
if not self.title:
|
||||
self.title = text_image[u'title']
|
||||
if path:
|
||||
self.has_original_files = False
|
||||
self.add_from_command(path, text_image[u'title'], text_image[u'image'])
|
||||
@ -450,9 +451,7 @@ class ServiceItem(object):
|
||||
if self.is_text() or ItemCapabilities.CanEditTitle in self.capabilities:
|
||||
return self.title
|
||||
else:
|
||||
if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities:
|
||||
return self._raw_frames[0][u'title']
|
||||
elif len(self._raw_frames) > 1:
|
||||
if len(self._raw_frames) > 1:
|
||||
return self.title
|
||||
else:
|
||||
return self._raw_frames[0][u'title']
|
||||
|
@ -116,30 +116,29 @@ class Settings(QtCore.QSettings):
|
||||
u'advanced/x11 bypass wm': X11_BYPASS_DEFAULT,
|
||||
u'crashreport/last directory': u'',
|
||||
u'displayTags/html_tags': u'',
|
||||
u'general/audio repeat list': False,
|
||||
u'general/auto open': False,
|
||||
u'general/auto preview': False,
|
||||
u'general/audio start paused': True,
|
||||
u'general/auto unblank': False,
|
||||
u'general/blank warning': False,
|
||||
u'general/ccli number': u'',
|
||||
u'general/has run wizard': False,
|
||||
u'general/language': u'[en]',
|
||||
# This defaults to yesterday in order to force the update check to run when you've never run it before.
|
||||
u'general/last version test': datetime.datetime.now().date() - datetime.timedelta(days=1),
|
||||
u'general/loop delay': 5,
|
||||
u'general/recent files': [],
|
||||
u'general/save prompt': False,
|
||||
u'general/screen blank': False,
|
||||
u'general/show splash': True,
|
||||
u'general/songselect password': u'',
|
||||
u'general/songselect username': u'',
|
||||
u'general/update check': True,
|
||||
u'general/view mode': u'default',
|
||||
u'core/audio repeat list': False,
|
||||
u'core/auto open': False,
|
||||
u'core/auto preview': False,
|
||||
u'core/audio start paused': True,
|
||||
u'core/auto unblank': False,
|
||||
u'core/blank warning': False,
|
||||
u'core/ccli number': u'',
|
||||
u'core/has run wizard': False,
|
||||
u'core/language': u'[en]',
|
||||
u'core/last version test': u'',
|
||||
u'core/loop delay': 5,
|
||||
u'core/recent files': [],
|
||||
u'core/save prompt': False,
|
||||
u'core/screen blank': False,
|
||||
u'core/show splash': True,
|
||||
u'core/songselect password': u'',
|
||||
u'core/songselect username': u'',
|
||||
u'core/update check': True,
|
||||
u'core/view mode': u'default',
|
||||
# The other display settings (display position and dimensions) are defined in the ScreenList class due to a
|
||||
# circular dependency.
|
||||
u'general/display on monitor': True,
|
||||
u'general/override position': False,
|
||||
u'core/display on monitor': True,
|
||||
u'core/override position': False,
|
||||
u'images/background color': u'#000000',
|
||||
u'media/players': u'webkit',
|
||||
u'media/override player': QtCore.Qt.Unchecked,
|
||||
@ -272,6 +271,7 @@ class Settings(QtCore.QSettings):
|
||||
u'shortcuts/songImportItem': [],
|
||||
u'shortcuts/themeScreen': [QtGui.QKeySequence(u'T')],
|
||||
u'shortcuts/toolsReindexItem': [],
|
||||
u'shortcuts/toolsFindDuplicates': [],
|
||||
u'shortcuts/toolsAlertItem': [QtGui.QKeySequence(u'F7')],
|
||||
u'shortcuts/toolsFirstTimeWizard': [],
|
||||
u'shortcuts/toolsOpenDataFolder': [],
|
||||
@ -304,7 +304,7 @@ class Settings(QtCore.QSettings):
|
||||
# Changed during 1.9.x development.
|
||||
(u'bibles/bookname language', u'bibles/book name language', []),
|
||||
(u'general/enable slide loop', u'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]),
|
||||
(u'songs/ccli number', u'general/ccli number', []),
|
||||
(u'songs/ccli number', u'core/ccli number', []),
|
||||
(u'media/use phonon', u'', []),
|
||||
# Changed during 2.1.x development.
|
||||
(u'advanced/stylesheet fix', u'', []),
|
||||
@ -315,7 +315,34 @@ class Settings(QtCore.QSettings):
|
||||
(u'songs/last directory 1', u'songs/last directory import', []),
|
||||
(u'songusage/last directory 1', u'songusage/last directory export', []),
|
||||
(u'user interface/mainwindow splitter geometry', u'user interface/main window splitter geometry', []),
|
||||
(u'shortcuts/makeLive', u'shortcuts/make_live', [])
|
||||
(u'shortcuts/makeLive', u'shortcuts/make_live', []),
|
||||
(u'general/audio repeat list', u'core/audio repeat list', []),
|
||||
(u'general/auto open', u'core/auto open', []),
|
||||
(u'general/auto preview', u'core/auto preview', []),
|
||||
(u'general/audio start paused', u'core/audio start paused', []),
|
||||
(u'general/auto unblank', u'core/auto unblank', []),
|
||||
(u'general/blank warning', u'core/blank warning', []),
|
||||
(u'general/ccli number', u'core/ccli number', []),
|
||||
(u'general/has run wizard', u'core/has run wizard', []),
|
||||
(u'general/language', u'core/language', []),
|
||||
(u'general/last version test', u'core/last version test', []),
|
||||
(u'general/loop delay', u'core/loop delay', []),
|
||||
(u'general/recent files', u'core/recent files', []),
|
||||
(u'general/save prompt', u'core/save prompt', []),
|
||||
(u'general/screen blank', u'core/screen blank', []),
|
||||
(u'general/show splash', u'core/show splash', []),
|
||||
(u'general/songselect password', u'core/songselect password', []),
|
||||
(u'general/songselect username', u'core/songselect username', []),
|
||||
(u'general/update check', u'core/update check', []),
|
||||
(u'general/view mode', u'core/view mode', []),
|
||||
(u'general/display on monitor', u'core/display on monitor', []),
|
||||
(u'general/override position', u'core/override position', []),
|
||||
(u'general/x position', u'core/x position', []),
|
||||
(u'general/y position', u'core/y position', []),
|
||||
(u'general/monitor', u'core/monitor', []),
|
||||
(u'general/height', u'core/height', []),
|
||||
(u'general/monitor', u'core/monitor', []),
|
||||
(u'general/width', u'core/width', [])
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
|
@ -30,6 +30,7 @@
|
||||
The :mod:`~openlp.core.lib.settingstab` module contains the base SettingsTab class which plugins use for adding their
|
||||
own tab to the settings dialog.
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
@ -90,7 +91,7 @@ class SettingsTab(QtGui.QWidget):
|
||||
QtGui.QWidget.resizeEvent(self, event)
|
||||
width = self.width() - self.tab_layout.spacing() - \
|
||||
self.tab_layout.contentsMargins().left() - self.tab_layout.contentsMargins().right()
|
||||
left_width = min(width - self.right_column.minimumSizeHint().width(), width / 2)
|
||||
left_width = min(width - self.right_column.minimumSizeHint().width(), width // 2)
|
||||
left_width = max(left_width, self.left_column.minimumSizeHint().width())
|
||||
self.left_column.setFixedWidth(left_width)
|
||||
|
||||
|
@ -107,7 +107,6 @@ class UiStrings(object):
|
||||
self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
|
||||
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
|
||||
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
|
||||
self.OLPV1 = translate('OpenLP.Ui', 'openlp.org 1.x')
|
||||
self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2')
|
||||
self.OLPV2x = translate('OpenLP.Ui', 'OpenLP 2.1')
|
||||
self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?')
|
||||
|
@ -35,7 +35,7 @@ import os
|
||||
import platform
|
||||
|
||||
import sqlalchemy
|
||||
import BeautifulSoup
|
||||
from bs4 import BeautifulSoup
|
||||
from lxml import etree
|
||||
from PyQt4 import Qt, QtCore, QtGui, QtWebKit
|
||||
|
||||
@ -59,16 +59,24 @@ try:
|
||||
ENCHANT_VERSION = enchant.__version__
|
||||
except ImportError:
|
||||
ENCHANT_VERSION = u'-'
|
||||
try:
|
||||
import sqlite
|
||||
SQLITE_VERSION = sqlite.version
|
||||
except ImportError:
|
||||
SQLITE_VERSION = u'-'
|
||||
try:
|
||||
import mako
|
||||
MAKO_VERSION = mako.__version__
|
||||
except ImportError:
|
||||
MAKO_VERSION = u'-'
|
||||
try:
|
||||
import icu
|
||||
try:
|
||||
ICU_VERSION = icu.VERSION
|
||||
except AttributeError:
|
||||
ICU_VERSION = u'OK'
|
||||
except ImportError:
|
||||
ICU_VERSION = u'-'
|
||||
try:
|
||||
import cherrypy
|
||||
CHERRYPY_VERSION = cherrypy.__version__
|
||||
except ImportError:
|
||||
CHERRYPY_VERSION = u'-'
|
||||
try:
|
||||
import uno
|
||||
arg = uno.createUnoStruct(u'com.sun.star.beans.PropertyValue')
|
||||
@ -141,8 +149,9 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
|
||||
u'lxml: %s\n' % etree.__version__ + \
|
||||
u'Chardet: %s\n' % CHARDET_VERSION + \
|
||||
u'PyEnchant: %s\n' % ENCHANT_VERSION + \
|
||||
u'PySQLite: %s\n' % SQLITE_VERSION + \
|
||||
u'Mako: %s\n' % MAKO_VERSION + \
|
||||
u'CherryPy: %s\n' % CHERRYPY_VERSION + \
|
||||
u'pyICU: %s\n' % ICU_VERSION + \
|
||||
u'pyUNO bridge: %s\n' % UNO_VERSION + \
|
||||
u'VLC: %s\n' % VLC_VERSION
|
||||
if platform.system() == u'Linux':
|
||||
|
@ -118,7 +118,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
check_directory_exists(os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp'))
|
||||
self.noInternetFinishButton.setVisible(False)
|
||||
# Check if this is a re-run of the wizard.
|
||||
self.hasRunWizard = Settings().value(u'general/has run wizard')
|
||||
self.hasRunWizard = Settings().value(u'core/has run wizard')
|
||||
# Sort out internet access for downloads
|
||||
if self.web_access:
|
||||
songs = self.config.get(u'songs', u'languages')
|
||||
@ -252,7 +252,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
self.application.set_busy_cursor()
|
||||
self._performWizard()
|
||||
self.application.set_normal_cursor()
|
||||
Settings().setValue(u'general/has run wizard', True)
|
||||
Settings().setValue(u'core/has run wizard', True)
|
||||
self.close()
|
||||
|
||||
def urlGetFile(self, url, fpath):
|
||||
@ -459,7 +459,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
self.urlGetFile(u'%s%s' % (self.web, theme), os.path.join(themes_destination, theme))
|
||||
# Set Default Display
|
||||
if self.displayComboBox.currentIndex() != -1:
|
||||
Settings().setValue(u'General/monitor', self.displayComboBox.currentIndex())
|
||||
Settings().setValue(u'core/monitor', self.displayComboBox.currentIndex())
|
||||
self.screens.set_current_display(self.displayComboBox.currentIndex())
|
||||
# Set Global Theme
|
||||
if self.themeComboBox.currentIndex() != -1:
|
||||
|
@ -39,39 +39,39 @@ class Ui_FirstTimeLanguageDialog(object):
|
||||
"""
|
||||
The UI widgets of the language selection dialog.
|
||||
"""
|
||||
def setupUi(self, languageDialog):
|
||||
def setupUi(self, language_dialog):
|
||||
"""
|
||||
Set up the UI.
|
||||
"""
|
||||
languageDialog.setObjectName(u'languageDialog')
|
||||
languageDialog.resize(300, 50)
|
||||
self.dialogLayout = QtGui.QVBoxLayout(languageDialog)
|
||||
self.dialogLayout.setContentsMargins(8, 8, 8, 8)
|
||||
self.dialogLayout.setSpacing(8)
|
||||
self.dialogLayout.setObjectName(u'dialog_layout')
|
||||
self.infoLabel = QtGui.QLabel(languageDialog)
|
||||
self.infoLabel.setObjectName(u'infoLabel')
|
||||
self.dialogLayout.addWidget(self.infoLabel)
|
||||
self.languageLayout = QtGui.QHBoxLayout()
|
||||
self.languageLayout.setObjectName(u'languageLayout')
|
||||
self.languageLabel = QtGui.QLabel(languageDialog)
|
||||
self.languageLabel.setObjectName(u'languageLabel')
|
||||
self.languageLayout.addWidget(self.languageLabel)
|
||||
self.languageComboBox = QtGui.QComboBox(languageDialog)
|
||||
self.languageComboBox.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
|
||||
self.languageComboBox.setObjectName("languageComboBox")
|
||||
self.languageLayout.addWidget(self.languageComboBox)
|
||||
self.dialogLayout.addLayout(self.languageLayout)
|
||||
self.button_box = create_button_box(languageDialog, u'button_box', [u'cancel', u'ok'])
|
||||
self.dialogLayout.addWidget(self.button_box)
|
||||
self.retranslateUi(languageDialog)
|
||||
language_dialog.setObjectName(u'language_dialog')
|
||||
language_dialog.resize(300, 50)
|
||||
self.dialog_layout = QtGui.QVBoxLayout(language_dialog)
|
||||
self.dialog_layout.setContentsMargins(8, 8, 8, 8)
|
||||
self.dialog_layout.setSpacing(8)
|
||||
self.dialog_layout.setObjectName(u'dialog_layout')
|
||||
self.info_label = QtGui.QLabel(language_dialog)
|
||||
self.info_label.setObjectName(u'info_label')
|
||||
self.dialog_layout.addWidget(self.info_label)
|
||||
self.language_layout = QtGui.QHBoxLayout()
|
||||
self.language_layout.setObjectName(u'language_layout')
|
||||
self.language_label = QtGui.QLabel(language_dialog)
|
||||
self.language_label.setObjectName(u'language_label')
|
||||
self.language_layout.addWidget(self.language_label)
|
||||
self.language_combo_box = QtGui.QComboBox(language_dialog)
|
||||
self.language_combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
|
||||
self.language_combo_box.setObjectName("language_combo_box")
|
||||
self.language_layout.addWidget(self.language_combo_box)
|
||||
self.dialog_layout.addLayout(self.language_layout)
|
||||
self.button_box = create_button_box(language_dialog, u'button_box', [u'cancel', u'ok'])
|
||||
self.dialog_layout.addWidget(self.button_box)
|
||||
self.retranslateUi(language_dialog)
|
||||
self.setMaximumHeight(self.sizeHint().height())
|
||||
|
||||
def retranslateUi(self, languageDialog):
|
||||
def retranslateUi(self, language_dialog):
|
||||
"""
|
||||
Translate the UI on the fly.
|
||||
"""
|
||||
self.setWindowTitle(translate('OpenLP.FirstTimeLanguageForm', 'Select Translation'))
|
||||
self.infoLabel.setText(
|
||||
self.info_label.setText(
|
||||
translate('OpenLP.FirstTimeLanguageForm', 'Choose the translation you\'d like to use in OpenLP.'))
|
||||
self.languageLabel.setText(translate('OpenLP.FirstTimeLanguageForm', 'Translation:'))
|
||||
self.language_label.setText(translate('OpenLP.FirstTimeLanguageForm', 'Translation:'))
|
||||
|
@ -47,8 +47,8 @@ class FirstTimeLanguageForm(QtGui.QDialog, Ui_FirstTimeLanguageDialog):
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
self.qmList = LanguageManager.get_qm_list()
|
||||
self.languageComboBox.addItem(u'Autodetect')
|
||||
self.languageComboBox.addItems(sorted(self.qmList.keys()))
|
||||
self.language_combo_box.addItem(u'Autodetect')
|
||||
self.language_combo_box.addItems(sorted(self.qmList.keys()))
|
||||
|
||||
def exec_(self):
|
||||
"""
|
||||
@ -61,12 +61,12 @@ class FirstTimeLanguageForm(QtGui.QDialog, Ui_FirstTimeLanguageDialog):
|
||||
Run when the dialog is OKed.
|
||||
"""
|
||||
# It's the first row so must be Automatic
|
||||
if self.languageComboBox.currentIndex() == 0:
|
||||
if self.language_combo_box.currentIndex() == 0:
|
||||
LanguageManager.auto_language = True
|
||||
LanguageManager.set_language(False, False)
|
||||
else:
|
||||
LanguageManager.auto_language = False
|
||||
action = create_action(None, self.languageComboBox.currentText())
|
||||
action = create_action(None, self.language_combo_box.currentText())
|
||||
LanguageManager.set_language(action, False)
|
||||
return QtGui.QDialog.accept(self)
|
||||
|
||||
|
@ -49,7 +49,7 @@ class GeneralTab(SettingsTab):
|
||||
self.screens = ScreenList()
|
||||
self.icon_path = u':/icon/openlp-logo-16x16.png'
|
||||
general_translated = translate('OpenLP.GeneralTab', 'General')
|
||||
SettingsTab.__init__(self, parent, u'General', general_translated)
|
||||
SettingsTab.__init__(self, parent, u'Core', general_translated)
|
||||
|
||||
def setupUi(self):
|
||||
"""
|
||||
|
173
openlp/core/ui/listpreviewwidget.py
Normal file
173
openlp/core/ui/listpreviewwidget.py
Normal file
@ -0,0 +1,173 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`listpreviewwidget` is a widget that lists the slides in the slide controller.
|
||||
It is based on a QTableWidget but represents its contents in list form.
|
||||
"""
|
||||
from __future__ import division
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import ImageSource, Registry, ServiceItem
|
||||
|
||||
|
||||
class ListPreviewWidget(QtGui.QTableWidget):
|
||||
def __init__(self, parent, screen_ratio):
|
||||
"""
|
||||
Initializes the widget to default state.
|
||||
An empty ServiceItem is used per default.
|
||||
One needs to call replace_service_manager_item() to make this widget display something.
|
||||
"""
|
||||
super(QtGui.QTableWidget, self).__init__(parent)
|
||||
# Set up the widget.
|
||||
self.setColumnCount(1)
|
||||
self.horizontalHeader().setVisible(False)
|
||||
self.setColumnWidth(0, parent.width())
|
||||
self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
||||
self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
|
||||
self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
|
||||
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.setAlternatingRowColors(True)
|
||||
# Initialize variables.
|
||||
self.service_item = ServiceItem()
|
||||
self.screen_ratio = screen_ratio
|
||||
|
||||
def resizeEvent(self, QResizeEvent):
|
||||
"""
|
||||
Overloaded method from QTableWidget. Will recalculate the layout.
|
||||
"""
|
||||
self.__recalculate_layout()
|
||||
|
||||
def __recalculate_layout(self):
|
||||
"""
|
||||
Recalculates the layout of the table widget. It will set height and width
|
||||
of the table cells. QTableWidget does not adapt the cells to the widget size on its own.
|
||||
"""
|
||||
self.setColumnWidth(0, self.viewport().width())
|
||||
if self.service_item:
|
||||
# Sort out songs, bibles, etc.
|
||||
if self.service_item.is_text():
|
||||
self.resizeRowsToContents()
|
||||
else:
|
||||
# Sort out image heights.
|
||||
for framenumber in range(len(self.service_item.get_frames())):
|
||||
height = self.viewport().width() // self.screen_ratio
|
||||
self.setRowHeight(framenumber, height)
|
||||
|
||||
def screen_size_changed(self, screen_ratio):
|
||||
"""
|
||||
To be called whenever the live screen size changes.
|
||||
Because this makes a layout recalculation necessary.
|
||||
"""
|
||||
self.screen_ratio = screen_ratio
|
||||
self.__recalculate_layout()
|
||||
|
||||
def replace_service_item(self, service_item, width, slideNumber):
|
||||
"""
|
||||
Replaces the current preview items with the ones in service_item.
|
||||
Displays the given slide.
|
||||
"""
|
||||
self.service_item = service_item
|
||||
self.clear()
|
||||
self.setRowCount(0)
|
||||
self.setColumnWidth(0, width)
|
||||
row = 0
|
||||
text = []
|
||||
for framenumber, frame in enumerate(self.service_item.get_frames()):
|
||||
self.setRowCount(self.slide_count() + 1)
|
||||
item = QtGui.QTableWidgetItem()
|
||||
slide_height = 0
|
||||
if self.service_item.is_text():
|
||||
if frame[u'verseTag']:
|
||||
# These tags are already translated.
|
||||
verse_def = frame[u'verseTag']
|
||||
verse_def = u'%s%s' % (verse_def[0], verse_def[1:])
|
||||
two_line_def = u'%s\n%s' % (verse_def[0], verse_def[1:])
|
||||
row = two_line_def
|
||||
else:
|
||||
row += 1
|
||||
item.setText(frame[u'text'])
|
||||
else:
|
||||
label = QtGui.QLabel()
|
||||
label.setMargin(4)
|
||||
if self.service_item.is_media():
|
||||
label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
|
||||
else:
|
||||
label.setScaledContents(True)
|
||||
if self.service_item.is_command():
|
||||
label.setPixmap(QtGui.QPixmap(frame[u'image']))
|
||||
else:
|
||||
image = self.image_manager.get_image(frame[u'path'], ImageSource.ImagePlugin)
|
||||
label.setPixmap(QtGui.QPixmap.fromImage(image))
|
||||
self.setCellWidget(framenumber, 0, label)
|
||||
slide_height = width // self.screen_ratio
|
||||
row += 1
|
||||
text.append(unicode(row))
|
||||
self.setItem(framenumber, 0, item)
|
||||
if slide_height:
|
||||
self.setRowHeight(framenumber, slide_height)
|
||||
self.setVerticalHeaderLabels(text)
|
||||
if self.service_item.is_text():
|
||||
self.resizeRowsToContents()
|
||||
self.setColumnWidth(0, self.viewport().width())
|
||||
self.setFocus()
|
||||
self.change_slide(slideNumber)
|
||||
|
||||
def change_slide(self, slide):
|
||||
"""
|
||||
Switches to the given row.
|
||||
"""
|
||||
if slide >= self.slide_count():
|
||||
slide = self.slide_count() - 1
|
||||
# Scroll to next item if possible.
|
||||
if slide + 1 < self.slide_count():
|
||||
self.scrollToItem(self.item(slide + 1, 0))
|
||||
self.selectRow(slide)
|
||||
|
||||
def current_slide_number(self):
|
||||
"""
|
||||
Returns the position of the currently active item. Will return -1 if the widget is empty.
|
||||
"""
|
||||
return super(ListPreviewWidget, self).currentRow()
|
||||
|
||||
def slide_count(self):
|
||||
"""
|
||||
Returns the number of slides this widget holds.
|
||||
"""
|
||||
return super(ListPreviewWidget, self).rowCount()
|
||||
|
||||
def _get_image_manager(self):
|
||||
"""
|
||||
Adds the image manager to the class dynamically.
|
||||
"""
|
||||
if not hasattr(self, u'_image_manager'):
|
||||
self._image_manager = Registry().get(u'image_manager')
|
||||
return self._image_manager
|
||||
|
||||
image_manager = property(_get_image_manager)
|
||||
|
@ -35,6 +35,7 @@ Some of the code for this form is based on the examples at:
|
||||
* `http://html5demos.com/two-videos`_
|
||||
|
||||
"""
|
||||
from __future__ import division
|
||||
import cgi
|
||||
import logging
|
||||
import sys
|
||||
@ -207,8 +208,8 @@ class MainDisplay(Display):
|
||||
painter_image.begin(self.initial_fame)
|
||||
painter_image.fillRect(self.initial_fame.rect(), background_color)
|
||||
painter_image.drawImage(
|
||||
(self.screen[u'size'].width() - splash_image.width()) / 2,
|
||||
(self.screen[u'size'].height() - splash_image.height()) / 2,
|
||||
(self.screen[u'size'].width() - splash_image.width()) // 2,
|
||||
(self.screen[u'size'].height() - splash_image.height()) // 2,
|
||||
splash_image)
|
||||
service_item = ServiceItem()
|
||||
service_item.bg_image_bytes = image_to_byte(self.initial_fame)
|
||||
@ -268,7 +269,7 @@ class MainDisplay(Display):
|
||||
self.resize(self.width(), alert_height)
|
||||
self.setVisible(True)
|
||||
if location == AlertLocation.Middle:
|
||||
self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) / 2)
|
||||
self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) // 2)
|
||||
elif location == AlertLocation.Bottom:
|
||||
self.move(self.screen[u'size'].left(), self.screen[u'size'].height() - alert_height)
|
||||
else:
|
||||
@ -287,7 +288,7 @@ class MainDisplay(Display):
|
||||
self.image(path)
|
||||
# Update the preview frame.
|
||||
if self.is_live:
|
||||
self.live_controller.updatePreview()
|
||||
self.live_controller.update_preview()
|
||||
return True
|
||||
|
||||
def image(self, path):
|
||||
@ -357,7 +358,7 @@ class MainDisplay(Display):
|
||||
# Single screen active
|
||||
if self.screens.display_count == 1:
|
||||
# Only make visible if setting enabled.
|
||||
if Settings().value(u'general/display on monitor'):
|
||||
if Settings().value(u'core/display on monitor'):
|
||||
self.setVisible(True)
|
||||
else:
|
||||
self.setVisible(True)
|
||||
@ -405,7 +406,7 @@ class MainDisplay(Display):
|
||||
self.footer(service_item.foot_text)
|
||||
# if was hidden keep it hidden
|
||||
if self.hide_mode and self.is_live and not service_item.is_media():
|
||||
if Settings().value(u'general/auto unblank'):
|
||||
if Settings().value(u'core/auto unblank'):
|
||||
Registry().execute(u'slidecontroller_live_unblank')
|
||||
else:
|
||||
self.hide_display(self.hide_mode)
|
||||
@ -427,7 +428,7 @@ class MainDisplay(Display):
|
||||
log.debug(u'hide_display mode = %d', mode)
|
||||
if self.screens.display_count == 1:
|
||||
# Only make visible if setting enabled.
|
||||
if not Settings().value(u'general/display on monitor'):
|
||||
if not Settings().value(u'core/display on monitor'):
|
||||
return
|
||||
if mode == HideMode.Screen:
|
||||
self.frame.evaluateJavaScript(u'show_blank("desktop");')
|
||||
@ -450,7 +451,7 @@ class MainDisplay(Display):
|
||||
log.debug(u'show_display')
|
||||
if self.screens.display_count == 1:
|
||||
# Only make visible if setting enabled.
|
||||
if not Settings().value(u'general/display on monitor'):
|
||||
if not Settings().value(u'core/display on monitor'):
|
||||
return
|
||||
self.frame.evaluateJavaScript('show_blank("show");')
|
||||
if self.isHidden():
|
||||
|
@ -476,7 +476,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.arguments = self.application.args
|
||||
# Set up settings sections for the main application (not for use by plugins).
|
||||
self.ui_settings_section = u'user interface'
|
||||
self.general_settings_section = u'general'
|
||||
self.general_settings_section = u'core'
|
||||
self.advanced_settings_section = u'advanced'
|
||||
self.shortcuts_settings_section = u'shortcuts'
|
||||
self.service_manager_settings_section = u'servicemanager'
|
||||
@ -491,7 +491,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.new_data_path = None
|
||||
self.copy_data = False
|
||||
Settings().set_up_default_values()
|
||||
Settings().remove_obsolete_settings()
|
||||
self.service_not_saved = False
|
||||
self.about_form = AboutForm(self)
|
||||
self.media_controller = MediaController()
|
||||
@ -560,7 +559,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
widget = self.media_tool_box.widget(index)
|
||||
if widget:
|
||||
widget.onFocus()
|
||||
widget.on_focus()
|
||||
|
||||
def version_notice(self, version):
|
||||
"""
|
||||
@ -670,7 +669,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
Check and display message if screen blank on setup.
|
||||
"""
|
||||
settings = Settings()
|
||||
self.live_controller.mainDisplaySetBackground()
|
||||
self.live_controller.main_display_set_background()
|
||||
if settings.value(u'%s/screen blank' % self.general_settings_section):
|
||||
if settings.value(u'%s/blank warning' % self.general_settings_section):
|
||||
QtGui.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Main Display Blanked'),
|
||||
@ -780,8 +779,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
We need to make sure, that the SlidePreview's size is correct.
|
||||
"""
|
||||
self.preview_controller.previewSizeChanged()
|
||||
self.live_controller.previewSizeChanged()
|
||||
self.preview_controller.preview_size_changed()
|
||||
self.live_controller.preview_size_changed()
|
||||
|
||||
def on_settings_shortcuts_item_clicked(self):
|
||||
"""
|
||||
@ -990,8 +989,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.application.set_busy_cursor()
|
||||
self.image_manager.update_display()
|
||||
self.renderer.update_display()
|
||||
self.preview_controller.screenSizeChanged()
|
||||
self.live_controller.screenSizeChanged()
|
||||
self.preview_controller.screen_size_changed()
|
||||
self.live_controller.screen_size_changed()
|
||||
self.setFocus()
|
||||
self.activateWindow()
|
||||
self.application.set_normal_cursor()
|
||||
|
@ -415,7 +415,7 @@ class MediaController(object):
|
||||
elif not hidden or controller.media_info.is_background or service_item.will_auto_start:
|
||||
autoplay = True
|
||||
# Unblank on load set
|
||||
elif Settings().value(u'general/auto unblank'):
|
||||
elif Settings().value(u'core/auto unblank'):
|
||||
autoplay = True
|
||||
if autoplay:
|
||||
if not self.media_play(controller):
|
||||
@ -466,8 +466,8 @@ class MediaController(object):
|
||||
The ServiceItem containing the details to be played.
|
||||
"""
|
||||
used_players = get_media_players()[0]
|
||||
if service_item.title != UiStrings().Automatic:
|
||||
used_players = [service_item.title.lower()]
|
||||
if service_item.processor != UiStrings().Automatic:
|
||||
used_players = [service_item.processor.lower()]
|
||||
if controller.media_info.file_info.isFile():
|
||||
suffix = u'*.%s' % controller.media_info.file_info.suffix().lower()
|
||||
for title in used_players:
|
||||
|
227
openlp/core/ui/media/vendor/vlc.py
vendored
227
openlp/core/ui/media/vendor/vlc.py
vendored
@ -48,7 +48,7 @@ import sys
|
||||
from inspect import getargspec
|
||||
|
||||
__version__ = "N/A"
|
||||
build_date = "Thu Mar 21 22:33:03 2013"
|
||||
build_date = "Mon Apr 1 23:47:38 2013"
|
||||
|
||||
if sys.version_info[0] > 2:
|
||||
str = str
|
||||
@ -70,7 +70,7 @@ if sys.version_info[0] > 2:
|
||||
if isinstance(b, bytes):
|
||||
return b.decode(sys.getfilesystemencoding())
|
||||
else:
|
||||
return str(b)
|
||||
return b
|
||||
else:
|
||||
str = str
|
||||
unicode = unicode
|
||||
@ -278,6 +278,11 @@ def class_result(classname):
|
||||
return classname(result)
|
||||
return wrap_errcheck
|
||||
|
||||
# Wrapper for the opaque struct libvlc_log_t
|
||||
class Log(ctypes.Structure):
|
||||
pass
|
||||
Log_ptr = ctypes.POINTER(Log)
|
||||
|
||||
# FILE* ctypes wrapper, copied from
|
||||
# http://svn.python.org/projects/ctypes/trunk/ctypeslib/ctypeslib/contrib/pythonhdr.py
|
||||
class FILE(ctypes.Structure):
|
||||
@ -675,11 +680,14 @@ class Callback(ctypes.c_void_p):
|
||||
pass
|
||||
class LogCb(ctypes.c_void_p):
|
||||
"""Callback prototype for LibVLC log message handler.
|
||||
\param data data pointer as given to L{libvlc_log_subscribe}()
|
||||
\param data data pointer as given to L{libvlc_log_set}()
|
||||
\param level message level (@ref enum libvlc_log_level)
|
||||
\param ctx message context (meta-informations about the message)
|
||||
\param fmt printf() format string (as defined by ISO C11)
|
||||
\param args variable argument list for the format
|
||||
\note Log message handlers <b>must</b> be thread-safe.
|
||||
\warning The message context pointer, the format string parameters and the
|
||||
variable arguments are only valid until the callback returns.
|
||||
"""
|
||||
pass
|
||||
class VideoLockCb(ctypes.c_void_p):
|
||||
@ -813,13 +821,16 @@ class CallbackDecorators(object):
|
||||
Callback.__doc__ = '''Callback function notification
|
||||
\param p_event the event triggering the callback
|
||||
'''
|
||||
LogCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_void_p)
|
||||
LogCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, Log_ptr, ctypes.c_char_p, ctypes.c_void_p)
|
||||
LogCb.__doc__ = '''Callback prototype for LibVLC log message handler.
|
||||
\param data data pointer as given to L{libvlc_log_subscribe}()
|
||||
\param data data pointer as given to L{libvlc_log_set}()
|
||||
\param level message level (@ref enum libvlc_log_level)
|
||||
\param ctx message context (meta-informations about the message)
|
||||
\param fmt printf() format string (as defined by ISO C11)
|
||||
\param args variable argument list for the format
|
||||
\note Log message handlers <b>must</b> be thread-safe.
|
||||
\warning The message context pointer, the format string parameters and the
|
||||
variable arguments are only valid until the callback returns.
|
||||
'''
|
||||
VideoLockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p))
|
||||
VideoLockCb.__doc__ = '''Callback prototype to allocate and lock a picture buffer.
|
||||
@ -1407,7 +1418,7 @@ class Instance(_Ctype):
|
||||
@param name: interface name, or NULL for default.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_add_intf(self, name)
|
||||
return libvlc_add_intf(self, str_to_bytes(name))
|
||||
|
||||
def set_user_agent(self, name, http):
|
||||
'''Sets the application name. LibVLC passes this as the user agent string
|
||||
@ -1416,7 +1427,33 @@ class Instance(_Ctype):
|
||||
@param http: HTTP User Agent, e.g. "FooBar/1.2.3 Python/2.6.0".
|
||||
@version: LibVLC 1.1.1 or later.
|
||||
'''
|
||||
return libvlc_set_user_agent(self, name, http)
|
||||
return libvlc_set_user_agent(self, str_to_bytes(name), str_to_bytes(http))
|
||||
|
||||
def log_unset(self):
|
||||
'''Unsets the logging callback for a LibVLC instance. This is rarely needed:
|
||||
the callback is implicitly unset when the instance is destroyed.
|
||||
This function will wait for any pending callbacks invocation to complete
|
||||
(causing a deadlock if called from within the callback).
|
||||
@version: LibVLC 2.1.0 or later.
|
||||
'''
|
||||
return libvlc_log_unset(self)
|
||||
|
||||
def log_set(self, data, p_instance):
|
||||
'''Sets the logging callback for a LibVLC instance.
|
||||
This function is thread-safe: it will wait for any pending callbacks
|
||||
invocation to complete.
|
||||
@param data: opaque data pointer for the callback function @note Some log messages (especially debug) are emitted by LibVLC while is being initialized. These messages cannot be captured with this interface. @warning A deadlock may occur if this function is called from the callback.
|
||||
@param p_instance: libvlc instance.
|
||||
@version: LibVLC 2.1.0 or later.
|
||||
'''
|
||||
return libvlc_log_set(self, data, p_instance)
|
||||
|
||||
def log_set_file(self, stream):
|
||||
'''Sets up logging to a file.
|
||||
@param stream: FILE pointer opened for writing (the FILE pointer must remain valid until L{log_unset}()).
|
||||
@version: LibVLC 2.1.0 or later.
|
||||
'''
|
||||
return libvlc_log_set_file(self, stream)
|
||||
|
||||
def media_new_location(self, psz_mrl):
|
||||
'''Create a media with a certain given media resource location,
|
||||
@ -1429,7 +1466,7 @@ class Instance(_Ctype):
|
||||
@param psz_mrl: the media location.
|
||||
@return: the newly created media or NULL on error.
|
||||
'''
|
||||
return libvlc_media_new_location(self, psz_mrl)
|
||||
return libvlc_media_new_location(self, str_to_bytes(psz_mrl))
|
||||
|
||||
def media_new_path(self, path):
|
||||
'''Create a media for a certain file path.
|
||||
@ -1437,7 +1474,7 @@ class Instance(_Ctype):
|
||||
@param path: local filesystem path.
|
||||
@return: the newly created media or NULL on error.
|
||||
'''
|
||||
return libvlc_media_new_path(self, path)
|
||||
return libvlc_media_new_path(self, str_to_bytes(path))
|
||||
|
||||
def media_new_fd(self, fd):
|
||||
'''Create a media for an already open file descriptor.
|
||||
@ -1465,14 +1502,14 @@ class Instance(_Ctype):
|
||||
@param psz_name: the name of the node.
|
||||
@return: the new empty media or NULL on error.
|
||||
'''
|
||||
return libvlc_media_new_as_node(self, psz_name)
|
||||
return libvlc_media_new_as_node(self, str_to_bytes(psz_name))
|
||||
|
||||
def media_discoverer_new_from_name(self, psz_name):
|
||||
'''Discover media service by name.
|
||||
@param psz_name: service name.
|
||||
@return: media discover object or NULL in case of error.
|
||||
'''
|
||||
return libvlc_media_discoverer_new_from_name(self, psz_name)
|
||||
return libvlc_media_discoverer_new_from_name(self, str_to_bytes(psz_name))
|
||||
|
||||
def media_library_new(self):
|
||||
'''Create an new Media Library object.
|
||||
@ -1500,7 +1537,7 @@ class Instance(_Ctype):
|
||||
@return: A NULL-terminated linked list of potential audio output devices. It must be freed it with L{audio_output_device_list_release}().
|
||||
@version: LibVLC 2.1.0 or later.
|
||||
'''
|
||||
return libvlc_audio_output_device_list_get(self, aout)
|
||||
return libvlc_audio_output_device_list_get(self, str_to_bytes(aout))
|
||||
|
||||
def vlm_release(self):
|
||||
'''Release the vlm instance related to the given L{Instance}.
|
||||
@ -1518,7 +1555,7 @@ class Instance(_Ctype):
|
||||
@param b_loop: Should this broadcast be played in loop ?
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_add_broadcast(self, psz_name, psz_input, psz_output, i_options, ppsz_options, b_enabled, b_loop)
|
||||
return libvlc_vlm_add_broadcast(self, str_to_bytes(psz_name), str_to_bytes(psz_input), str_to_bytes(psz_output), i_options, ppsz_options, b_enabled, b_loop)
|
||||
|
||||
def vlm_add_vod(self, psz_name, psz_input, i_options, ppsz_options, b_enabled, psz_mux):
|
||||
'''Add a vod, with one input.
|
||||
@ -1530,14 +1567,14 @@ class Instance(_Ctype):
|
||||
@param psz_mux: the muxer of the vod media.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_add_vod(self, psz_name, psz_input, i_options, ppsz_options, b_enabled, psz_mux)
|
||||
return libvlc_vlm_add_vod(self, str_to_bytes(psz_name), str_to_bytes(psz_input), i_options, ppsz_options, b_enabled, str_to_bytes(psz_mux))
|
||||
|
||||
def vlm_del_media(self, psz_name):
|
||||
'''Delete a media (VOD or broadcast).
|
||||
@param psz_name: the media to delete.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_del_media(self, psz_name)
|
||||
return libvlc_vlm_del_media(self, str_to_bytes(psz_name))
|
||||
|
||||
def vlm_set_enabled(self, psz_name, b_enabled):
|
||||
'''Enable or disable a media (VOD or broadcast).
|
||||
@ -1545,7 +1582,7 @@ class Instance(_Ctype):
|
||||
@param b_enabled: the new status.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_set_enabled(self, psz_name, b_enabled)
|
||||
return libvlc_vlm_set_enabled(self, str_to_bytes(psz_name), b_enabled)
|
||||
|
||||
def vlm_set_output(self, psz_name, psz_output):
|
||||
'''Set the output for a media.
|
||||
@ -1553,7 +1590,7 @@ class Instance(_Ctype):
|
||||
@param psz_output: the output MRL (the parameter to the "sout" variable).
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_set_output(self, psz_name, psz_output)
|
||||
return libvlc_vlm_set_output(self, str_to_bytes(psz_name), str_to_bytes(psz_output))
|
||||
|
||||
def vlm_set_input(self, psz_name, psz_input):
|
||||
'''Set a media's input MRL. This will delete all existing inputs and
|
||||
@ -1562,7 +1599,7 @@ class Instance(_Ctype):
|
||||
@param psz_input: the input MRL.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_set_input(self, psz_name, psz_input)
|
||||
return libvlc_vlm_set_input(self, str_to_bytes(psz_name), str_to_bytes(psz_input))
|
||||
|
||||
def vlm_add_input(self, psz_name, psz_input):
|
||||
'''Add a media's input MRL. This will add the specified one.
|
||||
@ -1570,7 +1607,7 @@ class Instance(_Ctype):
|
||||
@param psz_input: the input MRL.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_add_input(self, psz_name, psz_input)
|
||||
return libvlc_vlm_add_input(self, str_to_bytes(psz_name), str_to_bytes(psz_input))
|
||||
|
||||
def vlm_set_loop(self, psz_name, b_loop):
|
||||
'''Set a media's loop status.
|
||||
@ -1578,7 +1615,7 @@ class Instance(_Ctype):
|
||||
@param b_loop: the new status.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_set_loop(self, psz_name, b_loop)
|
||||
return libvlc_vlm_set_loop(self, str_to_bytes(psz_name), b_loop)
|
||||
|
||||
def vlm_set_mux(self, psz_name, psz_mux):
|
||||
'''Set a media's vod muxer.
|
||||
@ -1586,7 +1623,7 @@ class Instance(_Ctype):
|
||||
@param psz_mux: the new muxer.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_set_mux(self, psz_name, psz_mux)
|
||||
return libvlc_vlm_set_mux(self, str_to_bytes(psz_name), str_to_bytes(psz_mux))
|
||||
|
||||
def vlm_change_media(self, psz_name, psz_input, psz_output, i_options, ppsz_options, b_enabled, b_loop):
|
||||
'''Edit the parameters of a media. This will delete all existing inputs and
|
||||
@ -1600,28 +1637,28 @@ class Instance(_Ctype):
|
||||
@param b_loop: Should this broadcast be played in loop ?
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_change_media(self, psz_name, psz_input, psz_output, i_options, ppsz_options, b_enabled, b_loop)
|
||||
return libvlc_vlm_change_media(self, str_to_bytes(psz_name), str_to_bytes(psz_input), str_to_bytes(psz_output), i_options, ppsz_options, b_enabled, b_loop)
|
||||
|
||||
def vlm_play_media(self, psz_name):
|
||||
'''Play the named broadcast.
|
||||
@param psz_name: the name of the broadcast.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_play_media(self, psz_name)
|
||||
return libvlc_vlm_play_media(self, str_to_bytes(psz_name))
|
||||
|
||||
def vlm_stop_media(self, psz_name):
|
||||
'''Stop the named broadcast.
|
||||
@param psz_name: the name of the broadcast.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_stop_media(self, psz_name)
|
||||
return libvlc_vlm_stop_media(self, str_to_bytes(psz_name))
|
||||
|
||||
def vlm_pause_media(self, psz_name):
|
||||
'''Pause the named broadcast.
|
||||
@param psz_name: the name of the broadcast.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_pause_media(self, psz_name)
|
||||
return libvlc_vlm_pause_media(self, str_to_bytes(psz_name))
|
||||
|
||||
def vlm_seek_media(self, psz_name, f_percentage):
|
||||
'''Seek in the named broadcast.
|
||||
@ -1629,7 +1666,7 @@ class Instance(_Ctype):
|
||||
@param f_percentage: the percentage to seek to.
|
||||
@return: 0 on success, -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_seek_media(self, psz_name, f_percentage)
|
||||
return libvlc_vlm_seek_media(self, str_to_bytes(psz_name), f_percentage)
|
||||
|
||||
def vlm_show_media(self, psz_name):
|
||||
'''Return information about the named media as a JSON
|
||||
@ -1643,7 +1680,7 @@ class Instance(_Ctype):
|
||||
@param psz_name: the name of the media, if the name is an empty string, all media is described.
|
||||
@return: string with information about named media, or NULL on error.
|
||||
'''
|
||||
return libvlc_vlm_show_media(self, psz_name)
|
||||
return libvlc_vlm_show_media(self, str_to_bytes(psz_name))
|
||||
|
||||
def vlm_get_media_instance_position(self, psz_name, i_instance):
|
||||
'''Get vlm_media instance position by name or instance id.
|
||||
@ -1651,7 +1688,7 @@ class Instance(_Ctype):
|
||||
@param i_instance: instance id.
|
||||
@return: position as float or -1. on error.
|
||||
'''
|
||||
return libvlc_vlm_get_media_instance_position(self, psz_name, i_instance)
|
||||
return libvlc_vlm_get_media_instance_position(self, str_to_bytes(psz_name), i_instance)
|
||||
|
||||
def vlm_get_media_instance_time(self, psz_name, i_instance):
|
||||
'''Get vlm_media instance time by name or instance id.
|
||||
@ -1659,7 +1696,7 @@ class Instance(_Ctype):
|
||||
@param i_instance: instance id.
|
||||
@return: time as integer or -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_get_media_instance_time(self, psz_name, i_instance)
|
||||
return libvlc_vlm_get_media_instance_time(self, str_to_bytes(psz_name), i_instance)
|
||||
|
||||
def vlm_get_media_instance_length(self, psz_name, i_instance):
|
||||
'''Get vlm_media instance length by name or instance id.
|
||||
@ -1667,7 +1704,7 @@ class Instance(_Ctype):
|
||||
@param i_instance: instance id.
|
||||
@return: length of media item or -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_get_media_instance_length(self, psz_name, i_instance)
|
||||
return libvlc_vlm_get_media_instance_length(self, str_to_bytes(psz_name), i_instance)
|
||||
|
||||
def vlm_get_media_instance_rate(self, psz_name, i_instance):
|
||||
'''Get vlm_media instance playback rate by name or instance id.
|
||||
@ -1675,7 +1712,7 @@ class Instance(_Ctype):
|
||||
@param i_instance: instance id.
|
||||
@return: playback rate or -1 on error.
|
||||
'''
|
||||
return libvlc_vlm_get_media_instance_rate(self, psz_name, i_instance)
|
||||
return libvlc_vlm_get_media_instance_rate(self, str_to_bytes(psz_name), i_instance)
|
||||
|
||||
def vlm_get_media_instance_title(self, psz_name, i_instance):
|
||||
'''Get vlm_media instance title number by name or instance id.
|
||||
@ -1684,7 +1721,7 @@ class Instance(_Ctype):
|
||||
@return: title as number or -1 on error.
|
||||
@bug: will always return 0.
|
||||
'''
|
||||
return libvlc_vlm_get_media_instance_title(self, psz_name, i_instance)
|
||||
return libvlc_vlm_get_media_instance_title(self, str_to_bytes(psz_name), i_instance)
|
||||
|
||||
def vlm_get_media_instance_chapter(self, psz_name, i_instance):
|
||||
'''Get vlm_media instance chapter number by name or instance id.
|
||||
@ -1693,7 +1730,7 @@ class Instance(_Ctype):
|
||||
@return: chapter as number or -1 on error.
|
||||
@bug: will always return 0.
|
||||
'''
|
||||
return libvlc_vlm_get_media_instance_chapter(self, psz_name, i_instance)
|
||||
return libvlc_vlm_get_media_instance_chapter(self, str_to_bytes(psz_name), i_instance)
|
||||
|
||||
def vlm_get_media_instance_seekable(self, psz_name, i_instance):
|
||||
'''Is libvlc instance seekable ?
|
||||
@ -1702,7 +1739,7 @@ class Instance(_Ctype):
|
||||
@return: 1 if seekable, 0 if not, -1 if media does not exist.
|
||||
@bug: will always return 0.
|
||||
'''
|
||||
return libvlc_vlm_get_media_instance_seekable(self, psz_name, i_instance)
|
||||
return libvlc_vlm_get_media_instance_seekable(self, str_to_bytes(psz_name), i_instance)
|
||||
|
||||
def vlm_get_event_manager(self):
|
||||
'''Get libvlc_event_manager from a vlm media.
|
||||
@ -1751,7 +1788,7 @@ class Media(_Ctype):
|
||||
self.add_option(o)
|
||||
|
||||
|
||||
def add_option(self, ppsz_options):
|
||||
def add_option(self, psz_options):
|
||||
'''Add an option to the media.
|
||||
This option will be used to determine how the media_player will
|
||||
read the media. This allows to use VLC's advanced
|
||||
@ -1763,11 +1800,11 @@ class Media(_Ctype):
|
||||
Specifically, due to architectural issues most audio and video options,
|
||||
such as text renderer options, have no effects on an individual media.
|
||||
These options must be set through L{new}() instead.
|
||||
@param ppsz_options: the options (as a string).
|
||||
@param psz_options: the options (as a string).
|
||||
'''
|
||||
return libvlc_media_add_option(self, ppsz_options)
|
||||
return libvlc_media_add_option(self, str_to_bytes(psz_options))
|
||||
|
||||
def add_option_flag(self, ppsz_options, i_flags):
|
||||
def add_option_flag(self, psz_options, i_flags):
|
||||
'''Add an option to the media with configurable flags.
|
||||
This option will be used to determine how the media_player will
|
||||
read the media. This allows to use VLC's advanced
|
||||
@ -1777,10 +1814,10 @@ class Media(_Ctype):
|
||||
specifically, due to architectural issues, video-related options
|
||||
such as text renderer options cannot be set on a single media. They
|
||||
must be set on the whole libvlc instance instead.
|
||||
@param ppsz_options: the options (as a string).
|
||||
@param psz_options: the options (as a string).
|
||||
@param i_flags: the flags for this option.
|
||||
'''
|
||||
return libvlc_media_add_option_flag(self, ppsz_options, i_flags)
|
||||
return libvlc_media_add_option_flag(self, str_to_bytes(psz_options), i_flags)
|
||||
|
||||
def retain(self):
|
||||
'''Retain a reference to a media descriptor object (libvlc_media_t). Use
|
||||
@ -1829,7 +1866,7 @@ class Media(_Ctype):
|
||||
@param e_meta: the meta to write.
|
||||
@param psz_value: the media's meta.
|
||||
'''
|
||||
return libvlc_media_set_meta(self, e_meta, psz_value)
|
||||
return libvlc_media_set_meta(self, e_meta, str_to_bytes(psz_value))
|
||||
|
||||
def save_meta(self):
|
||||
'''Save the meta previously set.
|
||||
@ -2490,7 +2527,7 @@ class MediaPlayer(_Ctype):
|
||||
@version: LibVLC 1.1.1 or later.
|
||||
@bug: All pixel planes are expected to have the same pitch. To use the YCbCr color space with chrominance subsampling, consider using L{video_set_format_callbacks}() instead.
|
||||
'''
|
||||
return libvlc_video_set_format(self, chroma, width, height, pitch)
|
||||
return libvlc_video_set_format(self, str_to_bytes(chroma), width, height, pitch)
|
||||
|
||||
def video_set_format_callbacks(self, setup, cleanup):
|
||||
'''Set decoded video chroma and dimensions. This only works in combination with
|
||||
@ -2617,7 +2654,7 @@ class MediaPlayer(_Ctype):
|
||||
@param channels: channels count.
|
||||
@version: LibVLC 2.0.0 or later.
|
||||
'''
|
||||
return libvlc_audio_set_format(self, format, rate, channels)
|
||||
return libvlc_audio_set_format(self, str_to_bytes(format), rate, channels)
|
||||
|
||||
def get_length(self):
|
||||
'''Get the current movie length (in ms).
|
||||
@ -2843,7 +2880,7 @@ class MediaPlayer(_Ctype):
|
||||
'''Set new video aspect ratio.
|
||||
@param psz_aspect: new video aspect-ratio or NULL to reset to default @note Invalid aspect ratios are ignored.
|
||||
'''
|
||||
return libvlc_video_set_aspect_ratio(self, psz_aspect)
|
||||
return libvlc_video_set_aspect_ratio(self, str_to_bytes(psz_aspect))
|
||||
|
||||
def video_get_spu(self):
|
||||
'''Get current video subtitle.
|
||||
@ -2859,7 +2896,7 @@ class MediaPlayer(_Ctype):
|
||||
|
||||
def video_set_spu(self, i_spu):
|
||||
'''Set new video subtitle.
|
||||
@param i_spu: new video subtitle to select.
|
||||
@param i_spu: video subtitle track to select (i_id from track description).
|
||||
@return: 0 on success, -1 if out of range.
|
||||
'''
|
||||
return libvlc_video_set_spu(self, i_spu)
|
||||
@ -2869,7 +2906,7 @@ class MediaPlayer(_Ctype):
|
||||
@param psz_subtitle: new video subtitle file.
|
||||
@return: the success status (boolean).
|
||||
'''
|
||||
return libvlc_video_set_subtitle_file(self, psz_subtitle)
|
||||
return libvlc_video_set_subtitle_file(self, str_to_bytes(psz_subtitle))
|
||||
|
||||
def video_get_spu_delay(self):
|
||||
'''Get the current subtitle delay. Positive values means subtitles are being
|
||||
@ -2900,7 +2937,7 @@ class MediaPlayer(_Ctype):
|
||||
'''Set new crop filter geometry.
|
||||
@param psz_geometry: new crop filter geometry (NULL to unset).
|
||||
'''
|
||||
return libvlc_video_set_crop_geometry(self, psz_geometry)
|
||||
return libvlc_video_set_crop_geometry(self, str_to_bytes(psz_geometry))
|
||||
|
||||
def video_get_teletext(self):
|
||||
'''Get current teletext page requested.
|
||||
@ -2948,13 +2985,13 @@ class MediaPlayer(_Ctype):
|
||||
@param i_height: the snapshot's height.
|
||||
@return: 0 on success, -1 if the video was not found.
|
||||
'''
|
||||
return libvlc_video_take_snapshot(self, num, psz_filepath, i_width, i_height)
|
||||
return libvlc_video_take_snapshot(self, num, str_to_bytes(psz_filepath), i_width, i_height)
|
||||
|
||||
def video_set_deinterlace(self, psz_mode):
|
||||
'''Enable or disable deinterlace filter.
|
||||
@param psz_mode: type of deinterlace filter, NULL to disable.
|
||||
'''
|
||||
return libvlc_video_set_deinterlace(self, psz_mode)
|
||||
return libvlc_video_set_deinterlace(self, str_to_bytes(psz_mode))
|
||||
|
||||
def video_get_marquee_int(self, option):
|
||||
'''Get an integer marquee option value.
|
||||
@ -2982,7 +3019,7 @@ class MediaPlayer(_Ctype):
|
||||
@param option: marq option to set See libvlc_video_marquee_string_option_t.
|
||||
@param psz_text: marq option value.
|
||||
'''
|
||||
return libvlc_video_set_marquee_string(self, option, psz_text)
|
||||
return libvlc_video_set_marquee_string(self, option, str_to_bytes(psz_text))
|
||||
|
||||
def video_get_logo_int(self, option):
|
||||
'''Get integer logo option.
|
||||
@ -3006,7 +3043,7 @@ class MediaPlayer(_Ctype):
|
||||
@param option: logo option to set, values of libvlc_video_logo_option_t.
|
||||
@param psz_value: logo option value.
|
||||
'''
|
||||
return libvlc_video_set_logo_string(self, option, psz_value)
|
||||
return libvlc_video_set_logo_string(self, option, str_to_bytes(psz_value))
|
||||
|
||||
def video_get_adjust_int(self, option):
|
||||
'''Get integer adjust option.
|
||||
@ -3049,7 +3086,7 @@ class MediaPlayer(_Ctype):
|
||||
@param psz_name: name of audio output, use psz_name of See L{AudioOutput}.
|
||||
@return: 0 if function succeded, -1 on error.
|
||||
'''
|
||||
return libvlc_audio_output_set(self, psz_name)
|
||||
return libvlc_audio_output_set(self, str_to_bytes(psz_name))
|
||||
|
||||
def audio_output_device_set(self, psz_audio_output, psz_device_id):
|
||||
'''Configures an explicit audio output device for a given audio output plugin.
|
||||
@ -3065,7 +3102,7 @@ class MediaPlayer(_Ctype):
|
||||
@param psz_device_id: device.
|
||||
@return: Nothing. Errors are ignored.
|
||||
'''
|
||||
return libvlc_audio_output_device_set(self, psz_audio_output, psz_device_id)
|
||||
return libvlc_audio_output_device_set(self, str_to_bytes(psz_audio_output), str_to_bytes(psz_device_id))
|
||||
|
||||
def audio_toggle_mute(self):
|
||||
'''Toggle mute status.
|
||||
@ -3314,44 +3351,43 @@ def libvlc_event_type_name(event_type):
|
||||
ctypes.c_char_p, ctypes.c_uint)
|
||||
return f(event_type)
|
||||
|
||||
def libvlc_log_subscribe(sub, cb, data):
|
||||
'''Registers a logging callback to LibVLC.
|
||||
This function is thread-safe.
|
||||
@param sub: uninitialized subscriber structure.
|
||||
def libvlc_log_unset(p_instance):
|
||||
'''Unsets the logging callback for a LibVLC instance. This is rarely needed:
|
||||
the callback is implicitly unset when the instance is destroyed.
|
||||
This function will wait for any pending callbacks invocation to complete
|
||||
(causing a deadlock if called from within the callback).
|
||||
@param p_instance: libvlc instance.
|
||||
@version: LibVLC 2.1.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_log_unset', None) or \
|
||||
_Cfunction('libvlc_log_unset', ((1,),), None,
|
||||
None, Instance)
|
||||
return f(p_instance)
|
||||
|
||||
def libvlc_log_set(cb, data, p_instance):
|
||||
'''Sets the logging callback for a LibVLC instance.
|
||||
This function is thread-safe: it will wait for any pending callbacks
|
||||
invocation to complete.
|
||||
@param cb: callback function pointer.
|
||||
@param data: opaque data pointer for the callback function @note Some log messages (especially debug) are emitted by LibVLC while initializing, before any LibVLC instance even exists. Thus this function does not require a LibVLC instance parameter. @warning As a consequence of not depending on a LibVLC instance, all logging callbacks are shared by all LibVLC instances within the process / address space. This also enables log messages to be emitted by LibVLC components that are not specific to any given LibVLC instance. @warning Do not call this function from within a logging callback. It would trigger a dead lock.
|
||||
@param data: opaque data pointer for the callback function @note Some log messages (especially debug) are emitted by LibVLC while is being initialized. These messages cannot be captured with this interface. @warning A deadlock may occur if this function is called from the callback.
|
||||
@param p_instance: libvlc instance.
|
||||
@version: LibVLC 2.1.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_log_subscribe', None) or \
|
||||
_Cfunction('libvlc_log_subscribe', ((1,), (1,), (1,),), None,
|
||||
None, ctypes.c_void_p, LogCb, ctypes.c_void_p)
|
||||
return f(sub, cb, data)
|
||||
f = _Cfunctions.get('libvlc_log_set', None) or \
|
||||
_Cfunction('libvlc_log_set', ((1,), (1,), (1,),), None,
|
||||
None, Instance, LogCb, ctypes.c_void_p)
|
||||
return f(cb, data, p_instance)
|
||||
|
||||
def libvlc_log_subscribe_file(sub, stream):
|
||||
'''Registers a logging callback to a file.
|
||||
@param stream: FILE pointer opened for writing (the FILE pointer must remain valid until L{libvlc_log_unsubscribe}()).
|
||||
def libvlc_log_set_file(p_instance, stream):
|
||||
'''Sets up logging to a file.
|
||||
@param p_instance: libvlc instance.
|
||||
@param stream: FILE pointer opened for writing (the FILE pointer must remain valid until L{libvlc_log_unset}()).
|
||||
@version: LibVLC 2.1.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_log_subscribe_file', None) or \
|
||||
_Cfunction('libvlc_log_subscribe_file', ((1,), (1,),), None,
|
||||
None, ctypes.c_void_p, FILE_ptr)
|
||||
return f(sub, stream)
|
||||
|
||||
def libvlc_log_unsubscribe(sub):
|
||||
'''Deregisters a logging callback from LibVLC.
|
||||
This function is thread-safe.
|
||||
@note: After (and only after) L{libvlc_log_unsubscribe}() has returned,
|
||||
LibVLC warrants that there are no more pending calls of the subscription
|
||||
callback function.
|
||||
@warning: Do not call this function from within a logging callback.
|
||||
It would trigger a dead lock.
|
||||
@param sub: initialized subscriber structure.
|
||||
@version: LibVLC 2.1.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_log_unsubscribe', None) or \
|
||||
_Cfunction('libvlc_log_unsubscribe', ((1,),), None,
|
||||
None, ctypes.c_void_p)
|
||||
return f(sub)
|
||||
f = _Cfunctions.get('libvlc_log_set_file', None) or \
|
||||
_Cfunction('libvlc_log_set_file', ((1,), (1,),), None,
|
||||
None, Instance, FILE_ptr)
|
||||
return f(p_instance, stream)
|
||||
|
||||
def libvlc_module_description_list_release(p_list):
|
||||
'''Release a list of module descriptions.
|
||||
@ -3460,7 +3496,7 @@ def libvlc_media_new_as_node(p_instance, psz_name):
|
||||
ctypes.c_void_p, Instance, ctypes.c_char_p)
|
||||
return f(p_instance, psz_name)
|
||||
|
||||
def libvlc_media_add_option(p_md, ppsz_options):
|
||||
def libvlc_media_add_option(p_md, psz_options):
|
||||
'''Add an option to the media.
|
||||
This option will be used to determine how the media_player will
|
||||
read the media. This allows to use VLC's advanced
|
||||
@ -3473,14 +3509,14 @@ def libvlc_media_add_option(p_md, ppsz_options):
|
||||
such as text renderer options, have no effects on an individual media.
|
||||
These options must be set through L{libvlc_new}() instead.
|
||||
@param p_md: the media descriptor.
|
||||
@param ppsz_options: the options (as a string).
|
||||
@param psz_options: the options (as a string).
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_media_add_option', None) or \
|
||||
_Cfunction('libvlc_media_add_option', ((1,), (1,),), None,
|
||||
None, Media, ctypes.c_char_p)
|
||||
return f(p_md, ppsz_options)
|
||||
return f(p_md, psz_options)
|
||||
|
||||
def libvlc_media_add_option_flag(p_md, ppsz_options, i_flags):
|
||||
def libvlc_media_add_option_flag(p_md, psz_options, i_flags):
|
||||
'''Add an option to the media with configurable flags.
|
||||
This option will be used to determine how the media_player will
|
||||
read the media. This allows to use VLC's advanced
|
||||
@ -3491,13 +3527,13 @@ def libvlc_media_add_option_flag(p_md, ppsz_options, i_flags):
|
||||
such as text renderer options cannot be set on a single media. They
|
||||
must be set on the whole libvlc instance instead.
|
||||
@param p_md: the media descriptor.
|
||||
@param ppsz_options: the options (as a string).
|
||||
@param psz_options: the options (as a string).
|
||||
@param i_flags: the flags for this option.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_media_add_option_flag', None) or \
|
||||
_Cfunction('libvlc_media_add_option_flag', ((1,), (1,), (1,),), None,
|
||||
None, Media, ctypes.c_char_p, ctypes.c_uint)
|
||||
return f(p_md, ppsz_options, i_flags)
|
||||
return f(p_md, psz_options, i_flags)
|
||||
|
||||
def libvlc_media_retain(p_md):
|
||||
'''Retain a reference to a media descriptor object (libvlc_media_t). Use
|
||||
@ -4949,12 +4985,12 @@ def libvlc_video_get_spu_description(p_mi):
|
||||
def libvlc_video_set_spu(p_mi, i_spu):
|
||||
'''Set new video subtitle.
|
||||
@param p_mi: the media player.
|
||||
@param i_spu: new video subtitle to select.
|
||||
@param i_spu: video subtitle track to select (i_id from track description).
|
||||
@return: 0 on success, -1 if out of range.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_set_spu', None) or \
|
||||
_Cfunction('libvlc_video_set_spu', ((1,), (1,),), None,
|
||||
ctypes.c_int, MediaPlayer, ctypes.c_uint)
|
||||
ctypes.c_int, MediaPlayer, ctypes.c_int)
|
||||
return f(p_mi, i_spu)
|
||||
|
||||
def libvlc_video_set_subtitle_file(p_mi, psz_subtitle):
|
||||
@ -5791,7 +5827,7 @@ def libvlc_vlm_get_event_manager(p_instance):
|
||||
# libvlc_printerr
|
||||
# libvlc_set_exit_handler
|
||||
|
||||
# 18 function(s) not wrapped as methods:
|
||||
# 15 function(s) not wrapped as methods:
|
||||
# libvlc_audio_output_device_list_release
|
||||
# libvlc_audio_output_list_release
|
||||
# libvlc_clearerr
|
||||
@ -5802,9 +5838,6 @@ def libvlc_vlm_get_event_manager(p_instance):
|
||||
# libvlc_get_changeset
|
||||
# libvlc_get_compiler
|
||||
# libvlc_get_version
|
||||
# libvlc_log_subscribe
|
||||
# libvlc_log_subscribe_file
|
||||
# libvlc_log_unsubscribe
|
||||
# libvlc_media_tracks_release
|
||||
# libvlc_module_description_list_release
|
||||
# libvlc_new
|
||||
|
@ -31,7 +31,7 @@ The actual plugin view form
|
||||
"""
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import PluginStatus, Registry, translate
|
||||
from plugindialog import Ui_PluginViewDialog
|
||||
|
@ -236,18 +236,22 @@ class ServiceManagerDialog(object):
|
||||
icon=u':/general/general_edit.png', triggers=self.create_custom)
|
||||
self.menu.addSeparator()
|
||||
# Add AutoPlay menu actions
|
||||
self.auto_play_slides_group = QtGui.QMenu(translate('OpenLP.ServiceManager', '&Auto play slides'))
|
||||
self.menu.addMenu(self.auto_play_slides_group)
|
||||
self.auto_play_slides_loop = create_widget_action(self.auto_play_slides_group,
|
||||
self.auto_play_slides_menu = QtGui.QMenu(translate('OpenLP.ServiceManager', '&Auto play slides'))
|
||||
self.menu.addMenu(self.auto_play_slides_menu)
|
||||
auto_play_slides_group = QtGui.QActionGroup(self.auto_play_slides_menu)
|
||||
auto_play_slides_group.setExclusive(True)
|
||||
self.auto_play_slides_loop = create_widget_action(self.auto_play_slides_menu,
|
||||
text=translate('OpenLP.ServiceManager', 'Auto play slides &Loop'),
|
||||
checked=False, triggers=self.toggle_auto_play_slides_loop)
|
||||
self.auto_play_slides_once = create_widget_action(self.auto_play_slides_group,
|
||||
auto_play_slides_group.addAction(self.auto_play_slides_loop)
|
||||
self.auto_play_slides_once = create_widget_action(self.auto_play_slides_menu,
|
||||
text=translate('OpenLP.ServiceManager', 'Auto play slides &Once'),
|
||||
checked=False, triggers=self.toggle_auto_play_slides_once)
|
||||
self.auto_play_slides_group.addSeparator()
|
||||
self.timed_slide_interval = create_widget_action(self.auto_play_slides_group,
|
||||
auto_play_slides_group.addAction(self.auto_play_slides_once)
|
||||
self.auto_play_slides_menu.addSeparator()
|
||||
self.timed_slide_interval = create_widget_action(self.auto_play_slides_menu,
|
||||
text=translate('OpenLP.ServiceManager', '&Delay between slides'),
|
||||
checked=False, triggers=self.on_timed_slide_interval)
|
||||
triggers=self.on_timed_slide_interval)
|
||||
self.menu.addSeparator()
|
||||
self.preview_action = create_widget_action(self.menu, text=translate('OpenLP.ServiceManager', 'Show &Preview'),
|
||||
icon=u':/general/general_preview.png', triggers=self.make_preview)
|
||||
@ -271,7 +275,6 @@ class ServiceManagerDialog(object):
|
||||
Registry().register_function(u'config_screen_changed', self.regenerate_service_Items)
|
||||
Registry().register_function(u'theme_update_global', self.theme_change)
|
||||
Registry().register_function(u'mediaitem_suffix_reset', self.reset_supported_suffixes)
|
||||
Registry().register_function(u'servicemanager_set_item', self.on_set_item)
|
||||
|
||||
def drag_enter_event(self, event):
|
||||
"""
|
||||
@ -294,8 +297,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
Sets up the service manager, toolbars, list view, et al.
|
||||
"""
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
self.active = build_icon(QtGui.QImage(u':/media/auto-start_active.png'))
|
||||
self.inactive = build_icon(QtGui.QImage(u':/media/auto-start_inactive.png'))
|
||||
self.active = build_icon(u':/media/auto-start_active.png')
|
||||
self.inactive = build_icon(u':/media/auto-start_inactive.png')
|
||||
Registry().register(u'service_manager', self)
|
||||
self.service_items = []
|
||||
self.suffixes = []
|
||||
@ -313,6 +316,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setMargin(0)
|
||||
self.setup_ui(self)
|
||||
# Need to use event as called across threads and UI is updated
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'servicemanager_set_item'), self.on_set_item)
|
||||
|
||||
def set_modified(self, modified=True):
|
||||
"""
|
||||
@ -712,13 +717,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
else:
|
||||
service_item.set_from_service(item, self.servicePath)
|
||||
service_item.validate_item(self.suffixes)
|
||||
self.load_item_unique_identifier = 0
|
||||
if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
|
||||
Registry().execute(u'%s_service_load' % service_item.name.lower(), service_item)
|
||||
# if the item has been processed
|
||||
if service_item.unique_identifier == self.load_item_unique_identifier:
|
||||
service_item.edit_id = int(self.load_item_edit_id)
|
||||
service_item.temporary_edit = self.load_item_temporary
|
||||
new_item = Registry().get(service_item.name).service_load(service_item)
|
||||
if new_item:
|
||||
service_item = new_item
|
||||
self.add_service_item(service_item, repaint=False)
|
||||
delete_file(p_file)
|
||||
self.main_window.add_recent_file(file_name)
|
||||
@ -791,7 +793,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.notes_action.setVisible(True)
|
||||
if service_item[u'service_item'].is_capable(ItemCapabilities.CanLoop) and \
|
||||
len(service_item[u'service_item'].get_frames()) > 1:
|
||||
self.auto_play_slides_group.menuAction().setVisible(True)
|
||||
self.auto_play_slides_menu.menuAction().setVisible(True)
|
||||
self.auto_play_slides_once.setChecked(service_item[u'service_item'].auto_play_slides_once)
|
||||
self.auto_play_slides_loop.setChecked(service_item[u'service_item'].auto_play_slides_loop)
|
||||
self.timed_slide_interval.setChecked(service_item[u'service_item'].timed_slide_interval > 0)
|
||||
@ -803,7 +805,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
translate('OpenLP.ServiceManager', '&Delay between slides') + delay_suffix)
|
||||
# TODO for future: make group explains itself more visually
|
||||
else:
|
||||
self.auto_play_slides_group.menuAction().setVisible(False)
|
||||
self.auto_play_slides_menu.menuAction().setVisible(False)
|
||||
if service_item[u'service_item'].is_capable(ItemCapabilities.HasVariableStartTime):
|
||||
self.time_action.setVisible(True)
|
||||
if service_item[u'service_item'].is_capable(ItemCapabilities.CanAutoStartForLive):
|
||||
@ -994,7 +996,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
|
||||
def on_set_item(self, message):
|
||||
"""
|
||||
Called by a signal to select a specific item.
|
||||
Called by a signal to select a specific item and make it live usually from remote.
|
||||
"""
|
||||
self.set_item(int(message))
|
||||
|
||||
@ -1260,14 +1262,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(-1, -1)
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def service_item_update(self, edit_id, unique_identifier, temporary=False):
|
||||
"""
|
||||
Triggered from plugins to update service items. Save the values as they will be used as part of the service load
|
||||
"""
|
||||
self.load_item_unique_identifier = unique_identifier
|
||||
self.load_item_edit_id = int(edit_id)
|
||||
self.load_item_temporary = str_to_bool(temporary)
|
||||
|
||||
def replace_service_item(self, newItem):
|
||||
"""
|
||||
Using the service item passed replace the one with the same edit id if found.
|
||||
@ -1278,7 +1272,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
newItem.merge(item[u'service_item'])
|
||||
item[u'service_item'] = newItem
|
||||
self.repaint_service_list(item_count + 1, 0)
|
||||
self.live_controller.replaceServiceManagerItem(newItem)
|
||||
self.live_controller.replace_service_manager_item(newItem)
|
||||
self.set_modified()
|
||||
|
||||
def add_service_item(self, item, rebuild=False, expand=None, replace=False, repaint=True, selected=False):
|
||||
@ -1300,7 +1294,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
item.merge(self.service_items[sitem][u'service_item'])
|
||||
self.service_items[sitem][u'service_item'] = item
|
||||
self.repaint_service_list(sitem, child)
|
||||
self.live_controller.replaceServiceManagerItem(item)
|
||||
self.live_controller.replace_service_manager_item(item)
|
||||
else:
|
||||
item.render()
|
||||
# nothing selected for dnd
|
||||
@ -1323,7 +1317,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.repaint_service_list(self.drop_position, -1)
|
||||
# if rebuilding list make sure live is fixed.
|
||||
if rebuild:
|
||||
self.live_controller.replaceServiceManagerItem(item)
|
||||
self.live_controller.replace_service_manager_item(item)
|
||||
self.drop_position = 0
|
||||
self.set_modified()
|
||||
|
||||
@ -1334,7 +1328,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.application.set_busy_cursor()
|
||||
item, child = self.find_service_item()
|
||||
if self.service_items[item][u'service_item'].is_valid:
|
||||
self.preview_controller.addServiceManagerItem(self.service_items[item][u'service_item'], child)
|
||||
self.preview_controller.add_service_manager_item(self.service_items[item][u'service_item'], child)
|
||||
else:
|
||||
critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'),
|
||||
translate('OpenLP.ServiceManager',
|
||||
@ -1372,15 +1366,15 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
child = row
|
||||
self.application.set_busy_cursor()
|
||||
if self.service_items[item][u'service_item'].is_valid:
|
||||
self.live_controller.addServiceManagerItem(self.service_items[item][u'service_item'], child)
|
||||
self.live_controller.add_service_manager_item(self.service_items[item][u'service_item'], child)
|
||||
if Settings().value(self.main_window.general_settings_section + u'/auto preview'):
|
||||
item += 1
|
||||
if self.service_items and item < len(self.service_items) and \
|
||||
self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanPreview):
|
||||
self.preview_controller.addServiceManagerItem(self.service_items[item][u'service_item'], 0)
|
||||
self.preview_controller.add_service_manager_item(self.service_items[item][u'service_item'], 0)
|
||||
next_item = self.service_manager_list.topLevelItem(item)
|
||||
self.service_manager_list.setCurrentItem(next_item)
|
||||
self.live_controller.preview_list_widget.setFocus()
|
||||
self.live_controller.preview_widget.setFocus()
|
||||
else:
|
||||
critical_error_message_box(translate('OpenLP.ServiceManager', 'Missing Display Handler'),
|
||||
translate('OpenLP.ServiceManager',
|
||||
@ -1394,7 +1388,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
item = self.find_service_item()[0]
|
||||
if self.service_items[item][u'service_item'].is_capable(ItemCapabilities.CanEdit):
|
||||
new_item = Registry().get(self.service_items[item][u'service_item'].name). \
|
||||
onRemoteEdit(self.service_items[item][u'service_item'].edit_id)
|
||||
on_remote_edit(self.service_items[item][u'service_item'].edit_id)
|
||||
if new_item:
|
||||
self.add_service_item(new_item, replace=True)
|
||||
|
||||
|
@ -96,6 +96,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
Process the form saving the settings
|
||||
"""
|
||||
log.debug(u'Processing settings exit')
|
||||
for tabIndex in range(self.stacked_layout.count()):
|
||||
self.stacked_layout.widget(tabIndex).save()
|
||||
# if the display of image background are changing we need to regenerate the image cache
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,7 @@ from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, Backgr
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
|
||||
from openlp.core.theme import Theme
|
||||
from openlp.core.ui import FileRenameForm, ThemeForm
|
||||
from openlp.core.utils import AppLocation, delete_file, locale_compare, get_filesystem_encoding
|
||||
from openlp.core.utils import AppLocation, delete_file, get_locale_key, get_filesystem_encoding
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -418,7 +418,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
self.theme_list_widget.clear()
|
||||
files = AppLocation.get_files(self.settings_section, u'.png')
|
||||
# Sort the themes by its name considering language specific
|
||||
files.sort(key=lambda file_name: unicode(file_name), cmp=locale_compare)
|
||||
files.sort(key=lambda file_name: get_locale_key(unicode(file_name)))
|
||||
# now process the file list of png files
|
||||
for name in files:
|
||||
# check to see file is in theme root directory
|
||||
|
@ -29,6 +29,8 @@
|
||||
"""
|
||||
The Themes configuration tab
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Registry, Settings, SettingsTab, UiStrings, translate
|
||||
@ -90,7 +92,7 @@ class ThemesTab(SettingsTab):
|
||||
self.global_level_label.setObjectName(u'global_level_label')
|
||||
self.level_layout.addRow(self.global_level_radio_button, self.global_level_label)
|
||||
label_top_margin = (self.song_level_radio_button.sizeHint().height() -
|
||||
self.song_level_label.sizeHint().height()) / 2
|
||||
self.song_level_label.sizeHint().height()) // 2
|
||||
for label in [self.song_level_label, self.service_level_label, self.global_level_label]:
|
||||
rect = label.rect()
|
||||
rect.setTop(rect.top() + label_top_margin)
|
||||
|
@ -178,8 +178,8 @@ class Ui_ThemeWizard(object):
|
||||
self.lineSpacingLabel = QtGui.QLabel(self.mainAreaPage)
|
||||
self.lineSpacingLabel.setObjectName(u'LineSpacingLabel')
|
||||
self.lineSpacingSpinBox = QtGui.QSpinBox(self.mainAreaPage)
|
||||
self.lineSpacingSpinBox.setMinimum(-50)
|
||||
self.lineSpacingSpinBox.setMaximum(50)
|
||||
self.lineSpacingSpinBox.setMinimum(-250)
|
||||
self.lineSpacingSpinBox.setMaximum(250)
|
||||
self.lineSpacingSpinBox.setObjectName(u'LineSpacingSpinBox')
|
||||
self.mainAreaLayout.addRow(self.lineSpacingLabel, self.lineSpacingSpinBox)
|
||||
self.outlineCheckBox = QtGui.QCheckBox(self.mainAreaPage)
|
||||
|
@ -58,8 +58,6 @@ class WizardStrings(object):
|
||||
ImportingType = translate('OpenLP.Ui', 'Importing "%s"...')
|
||||
ImportSelect = translate('OpenLP.Ui', 'Select Import Source')
|
||||
ImportSelectLong = translate('OpenLP.Ui', 'Select the import format and the location to import from.')
|
||||
NoSqlite = translate('OpenLP.Ui', 'The openlp.org 1.x importer has been disabled due to a missing Python module. '
|
||||
'If you want to use this importer, you will need to install the "python-sqlite" module.')
|
||||
OpenTypeFile = translate('OpenLP.Ui', 'Open %s File')
|
||||
OpenTypeFolder = translate('OpenLP.Ui', 'Open %s Folder')
|
||||
PercentSymbolFormat = translate('OpenLP.Ui', '%p%')
|
||||
@ -77,13 +75,30 @@ class OpenLPWizard(QtGui.QWizard):
|
||||
"""
|
||||
Generic OpenLP wizard to provide generic functionality and a unified look
|
||||
and feel.
|
||||
|
||||
``parent``
|
||||
The QWidget-derived parent of the wizard.
|
||||
|
||||
``plugin``
|
||||
Plugin this wizard is part of. The plugin will be saved in the "plugin" variable.
|
||||
The plugin will also be used as basis for the file dialog methods this class provides.
|
||||
|
||||
``name``
|
||||
The object name this wizard should have.
|
||||
|
||||
``image``
|
||||
The image to display on the "welcome" page of the wizard. Should be 163x350.
|
||||
|
||||
``add_progress_page``
|
||||
Whether to add a progress page with a progressbar at the end of the wizard.
|
||||
"""
|
||||
def __init__(self, parent, plugin, name, image):
|
||||
def __init__(self, parent, plugin, name, image, add_progress_page=True):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
QtGui.QWizard.__init__(self, parent)
|
||||
self.plugin = plugin
|
||||
self.with_progress_page = add_progress_page
|
||||
self.setObjectName(name)
|
||||
self.open_icon = build_icon(u':/general/general_open.png')
|
||||
self.delete_icon = build_icon(u':/general/general_delete.png')
|
||||
@ -94,6 +109,7 @@ class OpenLPWizard(QtGui.QWizard):
|
||||
self.custom_init()
|
||||
self.custom_signals()
|
||||
self.currentIdChanged.connect(self.on_current_id_changed)
|
||||
if self.with_progress_page:
|
||||
self.error_copy_to_button.clicked.connect(self.on_error_copy_to_button_clicked)
|
||||
self.error_save_to_button.clicked.connect(self.on_error_save_to_button_clicked)
|
||||
|
||||
@ -107,6 +123,7 @@ class OpenLPWizard(QtGui.QWizard):
|
||||
QtGui.QWizard.NoBackButtonOnStartPage | QtGui.QWizard.NoBackButtonOnLastPage)
|
||||
add_welcome_page(self, image)
|
||||
self.add_custom_pages()
|
||||
if self.with_progress_page:
|
||||
self.add_progress_page()
|
||||
self.retranslateUi()
|
||||
|
||||
@ -187,7 +204,7 @@ class OpenLPWizard(QtGui.QWizard):
|
||||
Stop the wizard on cancel button, close button or ESC key.
|
||||
"""
|
||||
log.debug(u'Wizard cancelled by user.')
|
||||
if self.currentPage() == self.progress_page:
|
||||
if self.with_progress_page and self.currentPage() == self.progress_page:
|
||||
Registry().execute(u'openlp_stop_wizard')
|
||||
self.done(QtGui.QDialog.Rejected)
|
||||
|
||||
@ -195,14 +212,14 @@ class OpenLPWizard(QtGui.QWizard):
|
||||
"""
|
||||
Perform necessary functions depending on which wizard page is active.
|
||||
"""
|
||||
if self.page(pageId) == self.progress_page:
|
||||
if self.with_progress_page and self.page(pageId) == self.progress_page:
|
||||
self.pre_wizard()
|
||||
self.performWizard()
|
||||
self.post_wizard()
|
||||
else:
|
||||
self.custom_cage_changed(pageId)
|
||||
self.custom_page_changed(pageId)
|
||||
|
||||
def custom_cage_changed(self, pageId):
|
||||
def custom_page_changed(self, pageId):
|
||||
"""
|
||||
Called when changing to a page other than the progress page
|
||||
"""
|
||||
|
@ -38,6 +38,7 @@ import re
|
||||
from subprocess import Popen, PIPE
|
||||
import sys
|
||||
import urllib2
|
||||
import icu
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
@ -56,10 +57,12 @@ from openlp.core.lib import translate
|
||||
log = logging.getLogger(__name__)
|
||||
APPLICATION_VERSION = {}
|
||||
IMAGES_FILTER = None
|
||||
ICU_COLLATOR = None
|
||||
UNO_CONNECTION_TYPE = u'pipe'
|
||||
#UNO_CONNECTION_TYPE = u'socket'
|
||||
CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE)
|
||||
INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE)
|
||||
DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+', re.UNICODE)
|
||||
|
||||
|
||||
class VersionThread(QtCore.QThread):
|
||||
@ -98,46 +101,38 @@ def get_application_version():
|
||||
if APPLICATION_VERSION:
|
||||
return APPLICATION_VERSION
|
||||
if u'--dev-version' in sys.argv or u'-d' in sys.argv:
|
||||
# If we're running the dev version, let's use bzr to get the version.
|
||||
try:
|
||||
# If bzrlib is available, use it.
|
||||
from bzrlib.branch import Branch
|
||||
b = Branch.open_containing('.')[0]
|
||||
b.lock_read()
|
||||
try:
|
||||
# Get the branch's latest revision number.
|
||||
revno = b.revno()
|
||||
# Convert said revision number into a bzr revision id.
|
||||
revision_id = b.dotted_revno_to_revision_id((revno,))
|
||||
# Get a dict of tags, with the revision id as the key.
|
||||
tags = b.tags.get_reverse_tag_dict()
|
||||
# Check if the latest
|
||||
if revision_id in tags:
|
||||
full_version = u'%s' % tags[revision_id][0]
|
||||
else:
|
||||
full_version = '%s-bzr%s' % (sorted(b.tags.get_tag_dict().keys())[-1], revno)
|
||||
finally:
|
||||
b.unlock()
|
||||
except:
|
||||
# Otherwise run the command line bzr client.
|
||||
bzr = Popen((u'bzr', u'tags', u'--sort', u'time'), stdout=PIPE)
|
||||
# NOTE: The following code is a duplicate of the code in setup.py. Any fix applied here should also be applied
|
||||
# there.
|
||||
|
||||
# Get the revision of this tree.
|
||||
bzr = Popen((u'bzr', u'revno'), stdout=PIPE)
|
||||
tree_revision, error = bzr.communicate()
|
||||
code = bzr.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error running bzr log')
|
||||
|
||||
# Get all tags.
|
||||
bzr = Popen((u'bzr', u'tags'), stdout=PIPE)
|
||||
output, error = bzr.communicate()
|
||||
code = bzr.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error running bzr tags')
|
||||
lines = output.splitlines()
|
||||
if not lines:
|
||||
tag = u'0.0.0'
|
||||
revision = u'0'
|
||||
tags = output.splitlines()
|
||||
if not tags:
|
||||
tag_version = u'0.0.0'
|
||||
tag_revision = u'0'
|
||||
else:
|
||||
tag, revision = lines[-1].split()
|
||||
bzr = Popen((u'bzr', u'log', u'--line', u'-r', u'-1'), stdout=PIPE)
|
||||
output, error = bzr.communicate()
|
||||
code = bzr.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error running bzr log')
|
||||
latest = output.split(u':')[0]
|
||||
full_version = latest == revision and tag or u'%s-bzr%s' % (tag, latest)
|
||||
# Remove any tag that has "?" as revision number. A "?" as revision number indicates, that this tag is from
|
||||
# another series.
|
||||
tags = [tag for tag in tags if tag.split()[-1].strip() != u'?']
|
||||
# Get the last tag and split it in a revision and tag name.
|
||||
tag_version, tag_revision = tags[-1].split()
|
||||
# If they are equal, then this tree is tarball with the source for the release. We do not want the revision
|
||||
# number in the full version.
|
||||
if tree_revision == tag_revision:
|
||||
full_version = tag_version
|
||||
else:
|
||||
full_version = u'%s-bzr%s' % (tag_version, tree_revision)
|
||||
else:
|
||||
# We're not running the development version, let's use the file.
|
||||
filepath = AppLocation.get_directory(AppLocation.VersionDir)
|
||||
@ -182,9 +177,9 @@ def check_latest_version(current_version):
|
||||
version_string = current_version[u'full']
|
||||
# set to prod in the distribution config file.
|
||||
settings = Settings()
|
||||
settings.beginGroup(u'general')
|
||||
settings.beginGroup(u'core')
|
||||
last_test = settings.value(u'last version test')
|
||||
this_test = datetime.now().date()
|
||||
this_test = unicode(datetime.now().date())
|
||||
settings.setValue(u'last version test', this_test)
|
||||
settings.endGroup()
|
||||
# Tell the main window whether there will ever be data to display
|
||||
@ -244,8 +239,7 @@ def get_images_filter():
|
||||
global IMAGES_FILTER
|
||||
if not IMAGES_FILTER:
|
||||
log.debug(u'Generating images filter.')
|
||||
formats = [unicode(fmt)
|
||||
for fmt in QtGui.QImageReader.supportedImageFormats()]
|
||||
formats = map(unicode, QtGui.QImageReader.supportedImageFormats())
|
||||
visible_formats = u'(*.%s)' % u'; *.'.join(formats)
|
||||
actual_formats = u'(*.%s)' % u' *.'.join(formats)
|
||||
IMAGES_FILTER = u'%s %s %s' % (translate('OpenLP', 'Image Files'), visible_formats, actual_formats)
|
||||
@ -379,21 +373,32 @@ def format_time(text, local_time):
|
||||
return re.sub('\%[a-zA-Z]', match_formatting, text)
|
||||
|
||||
|
||||
def locale_compare(string1, string2):
|
||||
def get_locale_key(string):
|
||||
"""
|
||||
Compares two strings according to the current locale settings.
|
||||
|
||||
As any other compare function, returns a negative, or a positive value,
|
||||
or 0, depending on whether string1 collates before or after string2 or
|
||||
is equal to it. Comparison is case insensitive.
|
||||
Creates a key for case insensitive, locale aware string sorting.
|
||||
"""
|
||||
# Function locale.strcoll() from standard Python library does not work properly on Windows.
|
||||
return locale.strcoll(string1.lower(), string2.lower())
|
||||
string = string.lower()
|
||||
# For Python 3 on platforms other than Windows ICU is not necessary. In those cases locale.strxfrm(str) can be used.
|
||||
global ICU_COLLATOR
|
||||
if ICU_COLLATOR is None:
|
||||
from languagemanager import LanguageManager
|
||||
locale = LanguageManager.get_language()
|
||||
icu_locale = icu.Locale(locale)
|
||||
ICU_COLLATOR = icu.Collator.createInstance(icu_locale)
|
||||
return ICU_COLLATOR.getSortKey(string)
|
||||
|
||||
|
||||
# For performance reasons provide direct reference to compare function without wrapping it in another function making
|
||||
# the string lowercase. This is needed for sorting songs.
|
||||
locale_direct_compare = locale.strcoll
|
||||
def get_natural_key(string):
|
||||
"""
|
||||
Generate a key for locale aware natural string sorting.
|
||||
Returns a list of string compare keys and integers.
|
||||
"""
|
||||
key = DIGITS_OR_NONDIGITS.findall(string)
|
||||
key = [int(part) if part.isdigit() else get_locale_key(part) for part in key]
|
||||
# Python 3 does not support comparision of different types anymore. So make sure, that we do not compare str and int.
|
||||
#if string[0].isdigit():
|
||||
# return [''] + key
|
||||
return key
|
||||
|
||||
|
||||
from applocation import AppLocation
|
||||
@ -403,4 +408,4 @@ from actions import ActionList
|
||||
|
||||
__all__ = [u'AppLocation', u'ActionList', u'LanguageManager', u'get_application_version', u'check_latest_version',
|
||||
u'add_actions', u'get_filesystem_encoding', u'get_web_page', u'get_uno_command', u'get_uno_instance',
|
||||
u'delete_file', u'clean_filename', u'format_time', u'locale_compare', u'locale_direct_compare']
|
||||
u'delete_file', u'clean_filename', u'format_time', u'get_locale_key', u'get_natural_key']
|
||||
|
@ -98,7 +98,7 @@ class LanguageManager(object):
|
||||
"""
|
||||
Retrieve a saved language to use from settings
|
||||
"""
|
||||
language = Settings().value(u'general/language')
|
||||
language = Settings().value(u'core/language')
|
||||
language = str(language)
|
||||
log.info(u'Language file: \'%s\' Loaded from conf file' % language)
|
||||
if re.match(r'[[].*[]]', language):
|
||||
@ -128,7 +128,7 @@ class LanguageManager(object):
|
||||
language = unicode(qm_list[action_name])
|
||||
if LanguageManager.auto_language:
|
||||
language = u'[%s]' % language
|
||||
Settings().setValue(u'general/language', language)
|
||||
Settings().setValue(u'core/language', language)
|
||||
log.info(u'Language file: \'%s\' written to conf file' % language)
|
||||
if message:
|
||||
QtGui.QMessageBox.information(None,
|
||||
|
@ -27,6 +27,5 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`alerts` module provides the Alerts plugin for producing impromptu
|
||||
on-screen announcements during a service.
|
||||
The :mod:`alerts` module provides the Alerts plugin for producing impromptu on-screen announcements during a service.
|
||||
"""
|
||||
|
@ -139,12 +139,10 @@ class AlertsPlugin(Plugin):
|
||||
|
||||
def add_tools_menu_item(self, tools_menu):
|
||||
"""
|
||||
Give the alerts plugin the opportunity to add items to the
|
||||
**Tools** menu.
|
||||
Give the alerts plugin the opportunity to add items to the **Tools** menu.
|
||||
|
||||
``tools_menu``
|
||||
The actual **Tools** menu item, so that your actions can
|
||||
use it as their parent.
|
||||
The actual **Tools** menu item, so that your actions can use it as their parent.
|
||||
"""
|
||||
log.info(u'add tools menu')
|
||||
self.tools_alert_item = create_action(tools_menu, u'toolsAlertItem',
|
||||
|
@ -27,20 +27,16 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Forms in OpenLP are made up of two classes. One class holds all the graphical
|
||||
elements, like buttons and lists, and the other class holds all the functional
|
||||
code, like slots and loading and saving.
|
||||
Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the
|
||||
other class holds all the functional code, like slots and loading and saving.
|
||||
|
||||
The first class, commonly known as the **Dialog** class, is typically named
|
||||
``Ui_<name>Dialog``. It is a slightly modified version of the class that the
|
||||
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
|
||||
converting most strings from "" to u'' and using OpenLP's ``translate()``
|
||||
function for translating strings.
|
||||
The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
|
||||
modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
|
||||
converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
|
||||
|
||||
The second class, commonly known as the **Form** class, is typically named
|
||||
``<name>Form``. This class is the one which is instantiated and used. It uses
|
||||
dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class
|
||||
mentioned above, like so::
|
||||
The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
|
||||
is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned
|
||||
above, like so::
|
||||
|
||||
class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
|
||||
|
||||
@ -48,9 +44,8 @@ mentioned above, like so::
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
|
||||
them separate from the functionality, so that it is easier to recreate the GUI
|
||||
from the .ui files later if necessary.
|
||||
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality,
|
||||
so that it is easier to recreate the GUI from the .ui files later if necessary.
|
||||
"""
|
||||
|
||||
from alertform import AlertForm
|
||||
|
@ -71,13 +71,13 @@ class Ui_AlertDialog(object):
|
||||
self.save_button.setObjectName(u'save_button')
|
||||
self.manage_button_layout.addWidget(self.save_button)
|
||||
self.delete_button = create_button(alert_dialog, u'delete_button', role=u'delete', enabled=False,
|
||||
click=alert_dialog.onDeleteButtonClicked)
|
||||
click=alert_dialog.on_delete_button_clicked)
|
||||
self.manage_button_layout.addWidget(self.delete_button)
|
||||
self.manage_button_layout.addStretch()
|
||||
self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1)
|
||||
displayIcon = build_icon(u':/general/general_live.png')
|
||||
self.display_button = create_button(alert_dialog, u'display_button', icon=displayIcon, enabled=False)
|
||||
self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=displayIcon,
|
||||
display_icon = build_icon(u':/general/general_live.png')
|
||||
self.display_button = create_button(alert_dialog, u'display_button', icon=display_icon, enabled=False)
|
||||
self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=display_icon,
|
||||
enabled=False)
|
||||
self.button_box = create_button_box(alert_dialog, u'button_box', [u'close'],
|
||||
[self.display_button, self.display_close_button])
|
||||
|
@ -93,7 +93,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
if self.trigger_alert(self.alert_text_edit.text()):
|
||||
self.close()
|
||||
|
||||
def onDeleteButtonClicked(self):
|
||||
def on_delete_button_clicked(self):
|
||||
"""
|
||||
Deletes the selected item.
|
||||
"""
|
||||
@ -160,8 +160,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
|
||||
def on_single_click(self):
|
||||
"""
|
||||
List item has been single clicked to add it to the edit field so it can
|
||||
be changed.
|
||||
List item has been single clicked to add it to the edit field so it can be changed.
|
||||
"""
|
||||
item = self.alert_list_widget.selectedIndexes()[0]
|
||||
bitem = self.alert_list_widget.item(item.row())
|
||||
@ -204,8 +203,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
|
||||
def on_current_row_changed(self, row):
|
||||
"""
|
||||
Called when the *alert_list_widget*'s current row has been changed. This
|
||||
enables or disables buttons which require an item to act on.
|
||||
Called when the *alert_list_widget*'s current row has been changed. This enables or disables buttons which
|
||||
require an item to act on.
|
||||
|
||||
``row``
|
||||
The row (int). If there is no current row, the value is -1.
|
||||
|
@ -27,8 +27,8 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of
|
||||
the plugin which manages storing and displaying of alerts.
|
||||
The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and
|
||||
displaying of alerts.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@ -49,22 +49,23 @@ class AlertsManager(QtCore.QObject):
|
||||
|
||||
def __init__(self, parent):
|
||||
QtCore.QObject.__init__(self, parent)
|
||||
Registry().register(u'alerts_manager', self)
|
||||
self.timer_id = 0
|
||||
self.alert_list = []
|
||||
Registry().register_function(u'live_display_active', self.generate_alert)
|
||||
Registry().register_function(u'alerts_text', self.alert_text)
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'alerts_text'), self.alert_text)
|
||||
|
||||
def alert_text(self, message):
|
||||
"""
|
||||
Called via a alerts_text event. Message is single element array
|
||||
containing text
|
||||
Called via a alerts_text event. Message is single element array containing text.
|
||||
"""
|
||||
if message:
|
||||
self.display_alert(message[0])
|
||||
|
||||
def display_alert(self, text=u''):
|
||||
"""
|
||||
Called from the Alert Tab to display an alert
|
||||
Called from the Alert Tab to display an alert.
|
||||
|
||||
``text``
|
||||
display text
|
||||
@ -81,7 +82,7 @@ class AlertsManager(QtCore.QObject):
|
||||
|
||||
def generate_alert(self):
|
||||
"""
|
||||
Format and request the Alert and start the timer
|
||||
Format and request the Alert and start the timer.
|
||||
"""
|
||||
log.debug(u'Generate Alert called')
|
||||
if not self.alert_list:
|
||||
@ -95,8 +96,7 @@ class AlertsManager(QtCore.QObject):
|
||||
|
||||
def timerEvent(self, event):
|
||||
"""
|
||||
Time has finished so if our time then request the next Alert
|
||||
if there is one and reset the timer.
|
||||
Time has finished so if our time then request the next Alert if there is one and reset the timer.
|
||||
|
||||
``event``
|
||||
the QT event that has been triggered.
|
||||
|
@ -27,8 +27,7 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`db` module provides the database and schema that is the backend for
|
||||
the Alerts plugin
|
||||
The :mod:`db` module provides the database and schema that is the backend for the Alerts plugin.
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, Table, types
|
||||
@ -36,12 +35,14 @@ from sqlalchemy.orm import mapper
|
||||
|
||||
from openlp.core.lib.db import BaseModel, init_db
|
||||
|
||||
|
||||
class AlertItem(BaseModel):
|
||||
"""
|
||||
AlertItem model
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def init_schema(url):
|
||||
"""
|
||||
Setup the alerts database connection and initialise the database schema
|
||||
|
@ -27,6 +27,5 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`bibles` module provides the Bible plugin to enable OpenLP to display
|
||||
scripture.
|
||||
The :mod:`bibles` module provides the Bible plugin to enable OpenLP to display scripture.
|
||||
"""
|
||||
|
@ -124,18 +124,15 @@ class BiblePlugin(Plugin):
|
||||
|
||||
def add_export_menu_Item(self, export_menu):
|
||||
self.export_bible_item = create_action(export_menu, u'exportBibleItem',
|
||||
text=translate('BiblesPlugin', '&Bible'),
|
||||
visible=False)
|
||||
text=translate('BiblesPlugin', '&Bible'), visible=False)
|
||||
export_menu.addAction(self.export_bible_item)
|
||||
|
||||
def add_tools_menu_item(self, tools_menu):
|
||||
"""
|
||||
Give the bible plugin the opportunity to add items to the
|
||||
**Tools** menu.
|
||||
Give the bible plugin the opportunity to add items to the **Tools** menu.
|
||||
|
||||
``tools_menu``
|
||||
The actual **Tools** menu item, so that your actions can
|
||||
use it as their parent.
|
||||
The actual **Tools** menu item, so that your actions can use it as their parent.
|
||||
"""
|
||||
log.debug(u'add tools menu')
|
||||
self.tools_upgrade_item = create_action(tools_menu, u'toolsUpgradeItem',
|
||||
@ -166,25 +163,23 @@ class BiblePlugin(Plugin):
|
||||
|
||||
def uses_theme(self, theme):
|
||||
"""
|
||||
Called to find out if the bible plugin is currently using a theme.
|
||||
Returns ``True`` if the theme is being used, otherwise returns
|
||||
``False``.
|
||||
Called to find out if the bible plugin is currently using a theme. Returns ``True`` if the theme is being used,
|
||||
otherwise returns ``False``.
|
||||
"""
|
||||
return unicode(self.settings_tab.bible_theme) == theme
|
||||
|
||||
def rename_theme(self, oldTheme, newTheme):
|
||||
def rename_theme(self, old_theme, new_theme):
|
||||
"""
|
||||
Rename the theme the bible plugin is using making the plugin use the
|
||||
new name.
|
||||
|
||||
``oldTheme``
|
||||
The name of the theme the plugin should stop using. Unused for
|
||||
this particular plugin.
|
||||
``old_theme``
|
||||
The name of the theme the plugin should stop using. Unused for this particular plugin.
|
||||
|
||||
``newTheme``
|
||||
``new_theme``
|
||||
The new name the plugin should now use.
|
||||
"""
|
||||
self.settings_tab.bible_theme = newTheme
|
||||
self.settings_tab.bible_theme = new_theme
|
||||
self.settings_tab.save()
|
||||
|
||||
def set_plugin_text_strings(self):
|
||||
|
@ -28,30 +28,25 @@
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
Forms in OpenLP are made up of two classes. One class holds all the graphical
|
||||
elements, like buttons and lists, and the other class holds all the functional
|
||||
code, like slots and loading and saving.
|
||||
Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the
|
||||
other class holds all the functional code, like slots and loading and saving.
|
||||
|
||||
The first class, commonly known as the **Dialog** class, is typically named
|
||||
``Ui_<name>Dialog``. It is a slightly modified version of the class that the
|
||||
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
|
||||
converting most strings from "" to u'' and using OpenLP's ``translate()``
|
||||
function for translating strings.
|
||||
The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
|
||||
modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
|
||||
converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
|
||||
|
||||
The second class, commonly known as the **Form** class, is typically named
|
||||
``<name>Form``. This class is the one which is instantiated and used. It uses
|
||||
dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class
|
||||
mentioned above, like so::
|
||||
The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
|
||||
is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned
|
||||
above, like so::
|
||||
|
||||
class BibleImportForm(QtGui.QWizard, Ui_BibleImportWizard):
|
||||
|
||||
def __init__(self, parent, manager, bibleplugin):
|
||||
def __init__(self, parent, manager, bible_plugin):
|
||||
QtGui.QWizard.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
|
||||
them separate from the functionality, so that it is easier to recreate the GUI
|
||||
from the .ui files later if necessary.
|
||||
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality,
|
||||
so that it is easier to recreate the GUI from the .ui files later if necessary.
|
||||
"""
|
||||
from booknameform import BookNameForm
|
||||
from languageform import LanguageForm
|
||||
@ -59,5 +54,4 @@ from bibleimportform import BibleImportForm
|
||||
from bibleupgradeform import BibleUpgradeForm
|
||||
from editbibleform import EditBibleForm
|
||||
|
||||
__all__ = [u'BookNameForm', u'LanguageForm', u'BibleImportForm',
|
||||
u'BibleUpgradeForm', u'EditBibleForm']
|
||||
__all__ = [u'BookNameForm', u'LanguageForm', u'BibleImportForm', u'BibleUpgradeForm', u'EditBibleForm']
|
||||
|
@ -38,7 +38,7 @@ from openlp.core.lib import Settings, UiStrings, translate
|
||||
from openlp.core.lib.db import delete_database
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.utils import AppLocation, locale_compare
|
||||
from openlp.core.utils import AppLocation, get_locale_key
|
||||
from openlp.plugins.bibles.lib.manager import BibleFormat
|
||||
from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename
|
||||
|
||||
@ -58,12 +58,12 @@ class WebDownload(object):
|
||||
|
||||
class BibleImportForm(OpenLPWizard):
|
||||
"""
|
||||
This is the Bible Import Wizard, which allows easy importing of Bibles
|
||||
into OpenLP from other formats like OSIS, CSV and OpenSong.
|
||||
This is the Bible Import Wizard, which allows easy importing of Bibles into OpenLP from other formats like OSIS,
|
||||
CSV and OpenSong.
|
||||
"""
|
||||
log.info(u'BibleImportForm loaded')
|
||||
|
||||
def __init__(self, parent, manager, bibleplugin):
|
||||
def __init__(self, parent, manager, bible_plugin):
|
||||
"""
|
||||
Instantiate the wizard, and run any extra setup we need to.
|
||||
|
||||
@ -73,12 +73,12 @@ class BibleImportForm(OpenLPWizard):
|
||||
``manager``
|
||||
The Bible manager.
|
||||
|
||||
``bibleplugin``
|
||||
``bible_plugin``
|
||||
The Bible plugin.
|
||||
"""
|
||||
self.manager = manager
|
||||
self.web_bible_list = {}
|
||||
OpenLPWizard.__init__(self, parent, bibleplugin, u'bibleImportWizard', u':/wizards/wizard_importbible.bmp')
|
||||
OpenLPWizard.__init__(self, parent, bible_plugin, u'bibleImportWizard', u':/wizards/wizard_importbible.bmp')
|
||||
|
||||
def setupUi(self, image):
|
||||
"""
|
||||
@ -94,19 +94,11 @@ class BibleImportForm(OpenLPWizard):
|
||||
button.
|
||||
"""
|
||||
self.selectStack.setCurrentIndex(index)
|
||||
next_button = self.button(QtGui.QWizard.NextButton)
|
||||
next_button.setEnabled(BibleFormat.get_availability(index))
|
||||
|
||||
def custom_init(self):
|
||||
"""
|
||||
Perform any custom initialisation for bible importing.
|
||||
"""
|
||||
if BibleFormat.get_availability(BibleFormat.OpenLP1):
|
||||
self.openlp1DisabledLabel.hide()
|
||||
else:
|
||||
self.openlp1FileLabel.hide()
|
||||
self.openlp1FileEdit.hide()
|
||||
self.openlp1BrowseButton.hide()
|
||||
self.manager.set_process_dialog(self)
|
||||
self.loadWebBibles()
|
||||
self.restart()
|
||||
@ -121,7 +113,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.csvBooksButton.clicked.connect(self.onCsvBooksBrowseButtonClicked)
|
||||
self.csvVersesButton.clicked.connect(self.onCsvVersesBrowseButtonClicked)
|
||||
self.openSongBrowseButton.clicked.connect(self.onOpenSongBrowseButtonClicked)
|
||||
self.openlp1BrowseButton.clicked.connect(self.onOpenlp1BrowseButtonClicked)
|
||||
|
||||
def add_custom_pages(self):
|
||||
"""
|
||||
@ -137,7 +128,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.formatLabel = QtGui.QLabel(self.selectPage)
|
||||
self.formatLabel.setObjectName(u'FormatLabel')
|
||||
self.formatComboBox = QtGui.QComboBox(self.selectPage)
|
||||
self.formatComboBox.addItems([u'', u'', u'', u'', u''])
|
||||
self.formatComboBox.addItems([u'', u'', u'', u''])
|
||||
self.formatComboBox.setObjectName(u'FormatComboBox')
|
||||
self.formatLayout.addRow(self.formatLabel, self.formatComboBox)
|
||||
self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
|
||||
@ -259,29 +250,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.webProxyLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.webPasswordEdit)
|
||||
self.webTabWidget.addTab(self.webProxyTab, u'')
|
||||
self.selectStack.addWidget(self.webTabWidget)
|
||||
self.openlp1Widget = QtGui.QWidget(self.selectPage)
|
||||
self.openlp1Widget.setObjectName(u'Openlp1Widget')
|
||||
self.openlp1Layout = QtGui.QFormLayout(self.openlp1Widget)
|
||||
self.openlp1Layout.setMargin(0)
|
||||
self.openlp1Layout.setObjectName(u'Openlp1Layout')
|
||||
self.openlp1FileLabel = QtGui.QLabel(self.openlp1Widget)
|
||||
self.openlp1FileLabel.setObjectName(u'Openlp1FileLabel')
|
||||
self.openlp1FileLayout = QtGui.QHBoxLayout()
|
||||
self.openlp1FileLayout.setObjectName(u'Openlp1FileLayout')
|
||||
self.openlp1FileEdit = QtGui.QLineEdit(self.openlp1Widget)
|
||||
self.openlp1FileEdit.setObjectName(u'Openlp1FileEdit')
|
||||
self.openlp1FileLayout.addWidget(self.openlp1FileEdit)
|
||||
self.openlp1BrowseButton = QtGui.QToolButton(self.openlp1Widget)
|
||||
self.openlp1BrowseButton.setIcon(self.open_icon)
|
||||
self.openlp1BrowseButton.setObjectName(u'Openlp1BrowseButton')
|
||||
self.openlp1FileLayout.addWidget(self.openlp1BrowseButton)
|
||||
self.openlp1Layout.addRow(self.openlp1FileLabel, self.openlp1FileLayout)
|
||||
self.openlp1DisabledLabel = QtGui.QLabel(self.openlp1Widget)
|
||||
self.openlp1DisabledLabel.setWordWrap(True)
|
||||
self.openlp1DisabledLabel.setObjectName(u'Openlp1DisabledLabel')
|
||||
self.openlp1Layout.addRow(self.openlp1DisabledLabel)
|
||||
self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer)
|
||||
self.selectStack.addWidget(self.openlp1Widget)
|
||||
self.selectPageLayout.addLayout(self.selectStack)
|
||||
self.addPage(self.selectPage)
|
||||
# License Page
|
||||
@ -330,8 +298,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.formatComboBox.setItemText(BibleFormat.OpenSong, WizardStrings.OS)
|
||||
self.formatComboBox.setItemText(BibleFormat.WebDownload,
|
||||
translate('BiblesPlugin.ImportWizardForm', 'Web Download'))
|
||||
self.formatComboBox.setItemText(BibleFormat.OpenLP1, UiStrings().OLPV1)
|
||||
self.openlp1FileLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
|
||||
self.osisFileLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Bible file:'))
|
||||
self.csvBooksLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Books file:'))
|
||||
self.csvVersesLabel.setText(translate('BiblesPlugin.ImportWizardForm', 'Verses file:'))
|
||||
@ -364,14 +330,12 @@ class BibleImportForm(OpenLPWizard):
|
||||
'Please wait while your Bible is imported.'))
|
||||
self.progress_label.setText(WizardStrings.Ready)
|
||||
self.progress_bar.setFormat(u'%p%')
|
||||
self.openlp1DisabledLabel.setText(WizardStrings.NoSqlite)
|
||||
# Align all QFormLayouts towards each other.
|
||||
labelWidth = max(self.formatLabel.minimumSizeHint().width(),
|
||||
self.osisFileLabel.minimumSizeHint().width(),
|
||||
self.csvBooksLabel.minimumSizeHint().width(),
|
||||
self.csvVersesLabel.minimumSizeHint().width(),
|
||||
self.openSongFileLabel.minimumSizeHint().width(),
|
||||
self.openlp1FileLabel.minimumSizeHint().width())
|
||||
self.openSongFileLabel.minimumSizeHint().width())
|
||||
self.spacer.changeSize(labelWidth, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
|
||||
|
||||
def validateCurrentPage(self):
|
||||
@ -406,11 +370,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
elif self.field(u'source_format') == BibleFormat.WebDownload:
|
||||
self.versionNameEdit.setText(self.webTranslationComboBox.currentText())
|
||||
return True
|
||||
elif self.field(u'source_format') == BibleFormat.OpenLP1:
|
||||
if not self.field(u'openlp1_location'):
|
||||
critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % UiStrings().OLPV1)
|
||||
self.openlp1FileEdit.setFocus()
|
||||
return False
|
||||
return True
|
||||
elif self.currentPage() == self.licenseDetailsPage:
|
||||
license_version = self.field(u'license_version')
|
||||
@ -455,7 +414,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
"""
|
||||
self.webTranslationComboBox.clear()
|
||||
bibles = self.web_bible_list[index].keys()
|
||||
bibles.sort(cmp=locale_compare)
|
||||
bibles.sort(key=get_locale_key)
|
||||
self.webTranslationComboBox.addItems(bibles)
|
||||
|
||||
def onOsisBrowseButtonClicked(self):
|
||||
@ -484,13 +443,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
"""
|
||||
self.get_file_name(WizardStrings.OpenTypeFile % WizardStrings.OS, self.openSongFileEdit, u'last directory import')
|
||||
|
||||
def onOpenlp1BrowseButtonClicked(self):
|
||||
"""
|
||||
Show the file open dialog for the openlp.org 1.x file.
|
||||
"""
|
||||
self.get_file_name(WizardStrings.OpenTypeFile % UiStrings().OLPV1, self.openlp1FileEdit, u'last directory import',
|
||||
u'%s (*.bible)' % translate('BiblesPlugin.ImportWizardForm', 'openlp.org 1.x Bible Files'))
|
||||
|
||||
def register_fields(self):
|
||||
"""
|
||||
Register the bible import wizard fields.
|
||||
@ -505,7 +457,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.selectPage.registerField(u'proxy_server', self.webServerEdit)
|
||||
self.selectPage.registerField(u'proxy_username', self.webUserEdit)
|
||||
self.selectPage.registerField(u'proxy_password', self.webPasswordEdit)
|
||||
self.selectPage.registerField(u'openlp1_location', self.openlp1FileEdit)
|
||||
self.licenseDetailsPage.registerField(u'license_version', self.versionNameEdit)
|
||||
self.licenseDetailsPage.registerField(u'license_copyright', self.copyrightEdit)
|
||||
self.licenseDetailsPage.registerField(u'license_permissions', self.permissionsEdit)
|
||||
@ -529,7 +480,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
self.setField(u'proxy_server', settings.value(u'proxy address'))
|
||||
self.setField(u'proxy_username', settings.value(u'proxy username'))
|
||||
self.setField(u'proxy_password', settings.value(u'proxy password'))
|
||||
self.setField(u'openlp1_location', '')
|
||||
self.setField(u'license_version', self.versionNameEdit.text())
|
||||
self.setField(u'license_copyright', self.copyrightEdit.text())
|
||||
self.setField(u'license_permissions', self.permissionsEdit.text())
|
||||
@ -561,7 +511,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
name = bible[u'abbreviation']
|
||||
self.web_bible_list[download_type][version] = name.strip()
|
||||
|
||||
def preWizard(self):
|
||||
def pre_wizard(self):
|
||||
"""
|
||||
Prepare the UI for the import.
|
||||
"""
|
||||
@ -615,12 +565,6 @@ class BibleImportForm(OpenLPWizard):
|
||||
proxy_username=self.field(u'proxy_username'),
|
||||
proxy_password=self.field(u'proxy_password')
|
||||
)
|
||||
elif bible_type == BibleFormat.OpenLP1:
|
||||
# Import an openlp.org 1.x bible.
|
||||
importer = self.manager.import_bible(BibleFormat.OpenLP1,
|
||||
name=license_version,
|
||||
filename=self.field(u'openlp1_location')
|
||||
)
|
||||
if importer.do_import(license_version):
|
||||
self.manager.save_meta_data(license_version, license_version,
|
||||
license_copyright, license_permissions)
|
||||
|
@ -48,8 +48,8 @@ log = logging.getLogger(__name__)
|
||||
|
||||
class BibleUpgradeForm(OpenLPWizard):
|
||||
"""
|
||||
This is the Bible Upgrade Wizard, which allows easy importing of Bibles
|
||||
into OpenLP from older OpenLP2 database versions.
|
||||
This is the Bible Upgrade Wizard, which allows easy importing of Bibles into OpenLP from older OpenLP2 database
|
||||
versions.
|
||||
"""
|
||||
log.info(u'BibleUpgradeForm loaded')
|
||||
|
||||
@ -63,7 +63,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
``manager``
|
||||
The Bible manager.
|
||||
|
||||
``bibleplugin``
|
||||
``bible_plugin``
|
||||
The Bible plugin.
|
||||
"""
|
||||
self.manager = manager
|
||||
@ -74,7 +74,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.temp_dir = os.path.join(unicode(gettempdir(), get_filesystem_encoding()), u'openlp')
|
||||
self.files = self.manager.old_bible_databases
|
||||
self.success = {}
|
||||
self.newbibles = {}
|
||||
self.new_bibles = {}
|
||||
OpenLPWizard.__init__(self, parent, bible_plugin, u'bibleUpgradeWizard', u':/wizards/wizard_importbible.bmp')
|
||||
|
||||
def setupUi(self, image):
|
||||
@ -105,7 +105,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
Perform necessary functions depending on which wizard page is active.
|
||||
"""
|
||||
if self.page(pageId) == self.progress_page:
|
||||
self.preWizard()
|
||||
self.pre_wizard()
|
||||
self.performWizard()
|
||||
self.post_wizard()
|
||||
elif self.page(pageId) == self.selectPage and not self.files:
|
||||
@ -159,41 +159,41 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
Add the bible import specific wizard pages.
|
||||
"""
|
||||
# Backup Page
|
||||
self.backupPage = QtGui.QWizardPage()
|
||||
self.backupPage.setObjectName(u'BackupPage')
|
||||
self.backupLayout = QtGui.QVBoxLayout(self.backupPage)
|
||||
self.backup_page = QtGui.QWizardPage()
|
||||
self.backup_page.setObjectName(u'BackupPage')
|
||||
self.backupLayout = QtGui.QVBoxLayout(self.backup_page)
|
||||
self.backupLayout.setObjectName(u'BackupLayout')
|
||||
self.backupInfoLabel = QtGui.QLabel(self.backupPage)
|
||||
self.backupInfoLabel = QtGui.QLabel(self.backup_page)
|
||||
self.backupInfoLabel.setOpenExternalLinks(True)
|
||||
self.backupInfoLabel.setTextFormat(QtCore.Qt.RichText)
|
||||
self.backupInfoLabel.setWordWrap(True)
|
||||
self.backupInfoLabel.setObjectName(u'backupInfoLabel')
|
||||
self.backupLayout.addWidget(self.backupInfoLabel)
|
||||
self.selectLabel = QtGui.QLabel(self.backupPage)
|
||||
self.selectLabel = QtGui.QLabel(self.backup_page)
|
||||
self.selectLabel.setObjectName(u'select_label')
|
||||
self.backupLayout.addWidget(self.selectLabel)
|
||||
self.formLayout = QtGui.QFormLayout()
|
||||
self.formLayout.setMargin(0)
|
||||
self.formLayout.setObjectName(u'FormLayout')
|
||||
self.backupDirectoryLabel = QtGui.QLabel(self.backupPage)
|
||||
self.backupDirectoryLabel = QtGui.QLabel(self.backup_page)
|
||||
self.backupDirectoryLabel.setObjectName(u'backupDirectoryLabel')
|
||||
self.backupDirectoryLayout = QtGui.QHBoxLayout()
|
||||
self.backupDirectoryLayout.setObjectName(u'BackupDirectoryLayout')
|
||||
self.backupDirectoryEdit = QtGui.QLineEdit(self.backupPage)
|
||||
self.backupDirectoryEdit = QtGui.QLineEdit(self.backup_page)
|
||||
self.backupDirectoryEdit.setObjectName(u'BackupFolderEdit')
|
||||
self.backupDirectoryLayout.addWidget(self.backupDirectoryEdit)
|
||||
self.backupBrowseButton = QtGui.QToolButton(self.backupPage)
|
||||
self.backupBrowseButton = QtGui.QToolButton(self.backup_page)
|
||||
self.backupBrowseButton.setIcon(self.open_icon)
|
||||
self.backupBrowseButton.setObjectName(u'BackupBrowseButton')
|
||||
self.backupDirectoryLayout.addWidget(self.backupBrowseButton)
|
||||
self.formLayout.addRow(self.backupDirectoryLabel, self.backupDirectoryLayout)
|
||||
self.backupLayout.addLayout(self.formLayout)
|
||||
self.noBackupCheckBox = QtGui.QCheckBox(self.backupPage)
|
||||
self.noBackupCheckBox = QtGui.QCheckBox(self.backup_page)
|
||||
self.noBackupCheckBox.setObjectName('NoBackupCheckBox')
|
||||
self.backupLayout.addWidget(self.noBackupCheckBox)
|
||||
self.spacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
|
||||
self.backupLayout.addItem(self.spacer)
|
||||
self.addPage(self.backupPage)
|
||||
self.addPage(self.backup_page)
|
||||
# Select Page
|
||||
self.selectPage = QtGui.QWizardPage()
|
||||
self.selectPage.setObjectName(u'SelectPage')
|
||||
@ -247,8 +247,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.information_label.setText(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'This wizard will help you to upgrade your existing Bibles from a prior version of OpenLP 2. '
|
||||
'Click the next button below to start the upgrade process.'))
|
||||
self.backupPage.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Select Backup Directory'))
|
||||
self.backupPage.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
self.backup_page.setTitle(translate('BiblesPlugin.UpgradeWizardForm', 'Select Backup Directory'))
|
||||
self.backup_page.setSubTitle(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Please select a backup directory for your Bibles'))
|
||||
self.backupInfoLabel.setText(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Previous releases of OpenLP 2.0 are unable to use upgraded Bibles.'
|
||||
@ -277,7 +277,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
"""
|
||||
if self.currentPage() == self.welcome_page:
|
||||
return True
|
||||
elif self.currentPage() == self.backupPage:
|
||||
elif self.currentPage() == self.backup_page:
|
||||
if not self.noBackupCheckBox.checkState() == QtCore.Qt.Checked:
|
||||
backup_path = self.backupDirectoryEdit.text()
|
||||
if not backup_path:
|
||||
@ -316,7 +316,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
settings.beginGroup(self.plugin.settings_section)
|
||||
self.stop_import_flag = False
|
||||
self.success.clear()
|
||||
self.newbibles.clear()
|
||||
self.new_bibles.clear()
|
||||
self.clearScrollArea()
|
||||
self.files = self.manager.old_bible_databases
|
||||
self.addScrollArea()
|
||||
@ -329,7 +329,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.cancel_button.setVisible(True)
|
||||
settings.endGroup()
|
||||
|
||||
def preWizard(self):
|
||||
def pre_wizard(self):
|
||||
"""
|
||||
Prepare the UI for the upgrade.
|
||||
"""
|
||||
@ -372,8 +372,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
name = filename[1]
|
||||
self.progress_label.setText(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nUpgrading ...') % (number + 1, max_bibles, name))
|
||||
self.newbibles[number] = BibleDB(self.media_item, path=self.path, name=name, file=filename[0])
|
||||
self.newbibles[number].register(self.plugin.upgrade_wizard)
|
||||
self.new_bibles[number] = BibleDB(self.media_item, path=self.path, name=name, file=filename[0])
|
||||
self.new_bibles[number].register(self.plugin.upgrade_wizard)
|
||||
metadata = old_bible.get_metadata()
|
||||
web_bible = False
|
||||
meta_data = {}
|
||||
@ -387,7 +387,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
# Copy the metadata
|
||||
meta_data[meta[u'key']] = meta[u'value']
|
||||
if meta[u'key'] != u'name' and meta[u'key'] != u'dbversion':
|
||||
self.newbibles[number].save_meta(meta[u'key'], meta[u'value'])
|
||||
self.new_bibles[number].save_meta(meta[u'key'], meta[u'value'])
|
||||
if meta[u'key'] == u'download_source':
|
||||
web_bible = True
|
||||
self.includeWebBible = True
|
||||
@ -403,8 +403,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
if not books:
|
||||
log.error(u'Upgrading books from %s - download name: "%s" failed' % (
|
||||
meta_data[u'download_source'], meta_data[u'download_name']))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
self.new_bibles[number].session.close()
|
||||
del self.new_bibles[number]
|
||||
critical_error_message_box(
|
||||
translate('BiblesPlugin.UpgradeWizardForm', 'Download Error'),
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
@ -419,14 +419,14 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
meta_data[u'download_source'].lower())
|
||||
if bible and bible[u'language_id']:
|
||||
language_id = bible[u'language_id']
|
||||
self.newbibles[number].save_meta(u'language_id',
|
||||
self.new_bibles[number].save_meta(u'language_id',
|
||||
language_id)
|
||||
else:
|
||||
language_id = self.newbibles[number].get_language(name)
|
||||
language_id = self.new_bibles[number].get_language(name)
|
||||
if not language_id:
|
||||
log.warn(u'Upgrading from "%s" failed' % filename[0])
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
self.new_bibles[number].session.close()
|
||||
del self.new_bibles[number]
|
||||
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name),
|
||||
self.progress_bar.maximum() - self.progress_bar.value())
|
||||
@ -439,17 +439,17 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
break
|
||||
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') % (number + 1, max_bibles, name, book))
|
||||
book_ref_id = self.newbibles[number].\
|
||||
book_ref_id = self.new_bibles[number].\
|
||||
get_book_ref_id_by_name(book, len(books), language_id)
|
||||
if not book_ref_id:
|
||||
log.warn(u'Upgrading books from %s - download name: "%s" aborted by user' % (
|
||||
meta_data[u'download_source'], meta_data[u'download_name']))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
self.new_bibles[number].session.close()
|
||||
del self.new_bibles[number]
|
||||
self.success[number] = False
|
||||
break
|
||||
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
|
||||
db_book = self.newbibles[number].create_book(book,
|
||||
db_book = self.new_bibles[number].create_book(book,
|
||||
book_ref_id, book_details[u'testament_id'])
|
||||
# Try to import already downloaded verses.
|
||||
oldbook = old_bible.get_book(book)
|
||||
@ -462,19 +462,19 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
if self.stop_import_flag:
|
||||
self.success[number] = False
|
||||
break
|
||||
self.newbibles[number].create_verse(db_book.id,
|
||||
self.new_bibles[number].create_verse(db_book.id,
|
||||
int(verse[u'chapter']),
|
||||
int(verse[u'verse']), unicode(verse[u'text']))
|
||||
self.application.process_events()
|
||||
self.newbibles[number].session.commit()
|
||||
self.new_bibles[number].session.commit()
|
||||
else:
|
||||
language_id = self.newbibles[number].get_object(BibleMeta, u'language_id')
|
||||
language_id = self.new_bibles[number].get_object(BibleMeta, u'language_id')
|
||||
if not language_id:
|
||||
language_id = self.newbibles[number].get_language(name)
|
||||
language_id = self.new_bibles[number].get_language(name)
|
||||
if not language_id:
|
||||
log.warn(u'Upgrading books from "%s" failed' % name)
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
self.new_bibles[number].session.close()
|
||||
del self.new_bibles[number]
|
||||
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name),
|
||||
self.progress_bar.maximum() - self.progress_bar.value())
|
||||
@ -489,41 +489,41 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nUpgrading %s ...') %
|
||||
(number + 1, max_bibles, name, book[u'name']))
|
||||
book_ref_id = self.newbibles[number].get_book_ref_id_by_name(book[u'name'], len(books), language_id)
|
||||
book_ref_id = self.new_bibles[number].get_book_ref_id_by_name(book[u'name'], len(books), language_id)
|
||||
if not book_ref_id:
|
||||
log.warn(u'Upgrading books from %s " failed - aborted by user' % name)
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
self.new_bibles[number].session.close()
|
||||
del self.new_bibles[number]
|
||||
self.success[number] = False
|
||||
break
|
||||
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
|
||||
db_book = self.newbibles[number].create_book(book[u'name'],
|
||||
db_book = self.new_bibles[number].create_book(book[u'name'],
|
||||
book_ref_id, book_details[u'testament_id'])
|
||||
verses = old_bible.get_verses(book[u'id'])
|
||||
if not verses:
|
||||
log.warn(u'No verses found to import for book "%s"', book[u'name'])
|
||||
self.newbibles[number].delete_book(db_book)
|
||||
self.new_bibles[number].delete_book(db_book)
|
||||
continue
|
||||
for verse in verses:
|
||||
if self.stop_import_flag:
|
||||
self.success[number] = False
|
||||
break
|
||||
self.newbibles[number].create_verse(db_book.id,
|
||||
self.new_bibles[number].create_verse(db_book.id,
|
||||
int(verse[u'chapter']),
|
||||
int(verse[u'verse']), unicode(verse[u'text']))
|
||||
self.application.process_events()
|
||||
self.newbibles[number].session.commit()
|
||||
self.new_bibles[number].session.commit()
|
||||
if not self.success.get(number, True):
|
||||
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed') % (number + 1, max_bibles, name),
|
||||
self.progress_bar.maximum() - self.progress_bar.value())
|
||||
else:
|
||||
self.success[number] = True
|
||||
self.newbibles[number].save_meta(u'name', name)
|
||||
self.new_bibles[number].save_meta(u'name', name)
|
||||
self.increment_progress_bar(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nComplete') % (number + 1, max_bibles, name))
|
||||
if number in self.newbibles:
|
||||
self.newbibles[number].session.close()
|
||||
if number in self.new_bibles:
|
||||
self.new_bibles[number].session.close()
|
||||
# Close the last bible's connection if possible.
|
||||
if old_bible is not None:
|
||||
old_bible.close_connection()
|
||||
|
@ -33,66 +33,66 @@ from openlp.core.lib import translate
|
||||
from openlp.core.lib.ui import create_button_box
|
||||
|
||||
class Ui_BookNameDialog(object):
|
||||
def setupUi(self, bookNameDialog):
|
||||
bookNameDialog.setObjectName(u'bookNameDialog')
|
||||
bookNameDialog.resize(400, 271)
|
||||
self.bookNameLayout = QtGui.QVBoxLayout(bookNameDialog)
|
||||
self.bookNameLayout.setSpacing(8)
|
||||
self.bookNameLayout.setMargin(8)
|
||||
self.bookNameLayout.setObjectName(u'bookNameLayout')
|
||||
self.infoLabel = QtGui.QLabel(bookNameDialog)
|
||||
self.infoLabel.setWordWrap(True)
|
||||
self.infoLabel.setObjectName(u'infoLabel')
|
||||
self.bookNameLayout.addWidget(self.infoLabel)
|
||||
self.correspondingLayout = QtGui.QGridLayout()
|
||||
self.correspondingLayout.setColumnStretch(1, 1)
|
||||
self.correspondingLayout.setSpacing(8)
|
||||
self.correspondingLayout.setObjectName(u'correspondingLayout')
|
||||
self.currentLabel = QtGui.QLabel(bookNameDialog)
|
||||
def setupUi(self, book_name_dialog):
|
||||
book_name_dialog.setObjectName(u'book_name_dialog')
|
||||
book_name_dialog.resize(400, 271)
|
||||
self.book_name_layout = QtGui.QVBoxLayout(book_name_dialog)
|
||||
self.book_name_layout.setSpacing(8)
|
||||
self.book_name_layout.setMargin(8)
|
||||
self.book_name_layout.setObjectName(u'book_name_layout')
|
||||
self.info_label = QtGui.QLabel(book_name_dialog)
|
||||
self.info_label.setWordWrap(True)
|
||||
self.info_label.setObjectName(u'info_label')
|
||||
self.book_name_layout.addWidget(self.info_label)
|
||||
self.corresponding_layout = QtGui.QGridLayout()
|
||||
self.corresponding_layout.setColumnStretch(1, 1)
|
||||
self.corresponding_layout.setSpacing(8)
|
||||
self.corresponding_layout.setObjectName(u'corresponding_layout')
|
||||
self.currentLabel = QtGui.QLabel(book_name_dialog)
|
||||
self.currentLabel.setObjectName(u'currentLabel')
|
||||
self.correspondingLayout.addWidget(self.currentLabel, 0, 0, 1, 1)
|
||||
self.currentBookLabel = QtGui.QLabel(bookNameDialog)
|
||||
self.currentBookLabel.setObjectName(u'currentBookLabel')
|
||||
self.correspondingLayout.addWidget(self.currentBookLabel, 0, 1, 1, 1)
|
||||
self.correspondingLabel = QtGui.QLabel(bookNameDialog)
|
||||
self.corresponding_layout.addWidget(self.currentLabel, 0, 0, 1, 1)
|
||||
self.current_book_label = QtGui.QLabel(book_name_dialog)
|
||||
self.current_book_label.setObjectName(u'current_book_label')
|
||||
self.corresponding_layout.addWidget(self.current_book_label, 0, 1, 1, 1)
|
||||
self.correspondingLabel = QtGui.QLabel(book_name_dialog)
|
||||
self.correspondingLabel.setObjectName(u'correspondingLabel')
|
||||
self.correspondingLayout.addWidget(self.correspondingLabel, 1, 0, 1, 1)
|
||||
self.correspondingComboBox = QtGui.QComboBox(bookNameDialog)
|
||||
self.correspondingComboBox.setObjectName(u'correspondingComboBox')
|
||||
self.correspondingLayout.addWidget(self.correspondingComboBox, 1, 1, 1, 1)
|
||||
self.bookNameLayout.addLayout(self.correspondingLayout)
|
||||
self.optionsGroupBox = QtGui.QGroupBox(bookNameDialog)
|
||||
self.optionsGroupBox.setObjectName(u'optionsGroupBox')
|
||||
self.optionsLayout = QtGui.QVBoxLayout(self.optionsGroupBox)
|
||||
self.optionsLayout.setSpacing(8)
|
||||
self.optionsLayout.setMargin(8)
|
||||
self.optionsLayout.setObjectName(u'optionsLayout')
|
||||
self.oldTestamentCheckBox = QtGui.QCheckBox(self.optionsGroupBox)
|
||||
self.oldTestamentCheckBox.setObjectName(u'oldTestamentCheckBox')
|
||||
self.oldTestamentCheckBox.setCheckState(QtCore.Qt.Checked)
|
||||
self.optionsLayout.addWidget(self.oldTestamentCheckBox)
|
||||
self.newTestamentCheckBox = QtGui.QCheckBox(self.optionsGroupBox)
|
||||
self.newTestamentCheckBox.setObjectName(u'newTestamentCheckBox')
|
||||
self.newTestamentCheckBox.setCheckState(QtCore.Qt.Checked)
|
||||
self.optionsLayout.addWidget(self.newTestamentCheckBox)
|
||||
self.apocryphaCheckBox = QtGui.QCheckBox(self.optionsGroupBox)
|
||||
self.apocryphaCheckBox.setObjectName(u'apocryphaCheckBox')
|
||||
self.apocryphaCheckBox.setCheckState(QtCore.Qt.Checked)
|
||||
self.optionsLayout.addWidget(self.apocryphaCheckBox)
|
||||
self.bookNameLayout.addWidget(self.optionsGroupBox)
|
||||
self.button_box = create_button_box(bookNameDialog, u'button_box', [u'cancel', u'ok'])
|
||||
self.bookNameLayout.addWidget(self.button_box)
|
||||
self.corresponding_layout.addWidget(self.correspondingLabel, 1, 0, 1, 1)
|
||||
self.corresponding_combo_box = QtGui.QComboBox(book_name_dialog)
|
||||
self.corresponding_combo_box.setObjectName(u'corresponding_combo_box')
|
||||
self.corresponding_layout.addWidget(self.corresponding_combo_box, 1, 1, 1, 1)
|
||||
self.book_name_layout.addLayout(self.corresponding_layout)
|
||||
self.options_group_box = QtGui.QGroupBox(book_name_dialog)
|
||||
self.options_group_box.setObjectName(u'options_group_box')
|
||||
self.options_layout = QtGui.QVBoxLayout(self.options_group_box)
|
||||
self.options_layout.setSpacing(8)
|
||||
self.options_layout.setMargin(8)
|
||||
self.options_layout.setObjectName(u'options_layout')
|
||||
self.old_testament_check_box = QtGui.QCheckBox(self.options_group_box)
|
||||
self.old_testament_check_box.setObjectName(u'old_testament_check_box')
|
||||
self.old_testament_check_box.setCheckState(QtCore.Qt.Checked)
|
||||
self.options_layout.addWidget(self.old_testament_check_box)
|
||||
self.new_testament_check_box = QtGui.QCheckBox(self.options_group_box)
|
||||
self.new_testament_check_box.setObjectName(u'new_testament_check_box')
|
||||
self.new_testament_check_box.setCheckState(QtCore.Qt.Checked)
|
||||
self.options_layout.addWidget(self.new_testament_check_box)
|
||||
self.apocrypha_check_box = QtGui.QCheckBox(self.options_group_box)
|
||||
self.apocrypha_check_box.setObjectName(u'apocrypha_check_box')
|
||||
self.apocrypha_check_box.setCheckState(QtCore.Qt.Checked)
|
||||
self.options_layout.addWidget(self.apocrypha_check_box)
|
||||
self.book_name_layout.addWidget(self.options_group_box)
|
||||
self.button_box = create_button_box(book_name_dialog, u'button_box', [u'cancel', u'ok'])
|
||||
self.book_name_layout.addWidget(self.button_box)
|
||||
|
||||
self.retranslateUi(bookNameDialog)
|
||||
self.retranslateUi(book_name_dialog)
|
||||
|
||||
def retranslateUi(self, bookNameDialog):
|
||||
bookNameDialog.setWindowTitle(translate('BiblesPlugin.BookNameDialog', 'Select Book Name'))
|
||||
self.infoLabel.setText(translate('BiblesPlugin.BookNameDialog',
|
||||
def retranslateUi(self, book_name_dialog):
|
||||
book_name_dialog.setWindowTitle(translate('BiblesPlugin.BookNameDialog', 'Select Book Name'))
|
||||
self.info_label.setText(translate('BiblesPlugin.BookNameDialog',
|
||||
'The following book name cannot be matched up internally. '
|
||||
'Please select the corresponding name from the list.'))
|
||||
self.currentLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Current name:'))
|
||||
self.correspondingLabel.setText(translate('BiblesPlugin.BookNameDialog', 'Corresponding name:'))
|
||||
self.optionsGroupBox.setTitle(translate('BiblesPlugin.BookNameDialog', 'Show Books From'))
|
||||
self.oldTestamentCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'Old Testament'))
|
||||
self.newTestamentCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'New Testament'))
|
||||
self.apocryphaCheckBox.setText(translate('BiblesPlugin.BookNameDialog', 'Apocrypha'))
|
||||
self.options_group_box.setTitle(translate('BiblesPlugin.BookNameDialog', 'Show Books From'))
|
||||
self.old_testament_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'Old Testament'))
|
||||
self.new_testament_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'New Testament'))
|
||||
self.apocrypha_check_box.setText(translate('BiblesPlugin.BookNameDialog', 'Apocrypha'))
|
||||
|
@ -66,61 +66,61 @@ class BookNameForm(QDialog, Ui_BookNameDialog):
|
||||
"""
|
||||
Set up the signals used in the booknameform.
|
||||
"""
|
||||
self.oldTestamentCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged)
|
||||
self.newTestamentCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged)
|
||||
self.apocryphaCheckBox.stateChanged.connect(self.onCheckBoxIndexChanged)
|
||||
self.old_testament_check_box.stateChanged.connect(self.onCheckBoxIndexChanged)
|
||||
self.new_testament_check_box.stateChanged.connect(self.onCheckBoxIndexChanged)
|
||||
self.apocrypha_check_box.stateChanged.connect(self.onCheckBoxIndexChanged)
|
||||
|
||||
def onCheckBoxIndexChanged(self, index):
|
||||
"""
|
||||
Reload Combobox if CheckBox state has changed
|
||||
"""
|
||||
self.reloadComboBox()
|
||||
self.reload_combo_box()
|
||||
|
||||
def reloadComboBox(self):
|
||||
def reload_combo_box(self):
|
||||
"""
|
||||
Reload the Combobox items
|
||||
"""
|
||||
self.correspondingComboBox.clear()
|
||||
self.corresponding_combo_box.clear()
|
||||
items = BiblesResourcesDB.get_books()
|
||||
for item in items:
|
||||
addBook = True
|
||||
add_book = True
|
||||
for book in self.books:
|
||||
if book.book_reference_id == item[u'id']:
|
||||
addBook = False
|
||||
add_book = False
|
||||
break
|
||||
if self.oldTestamentCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 1:
|
||||
addBook = False
|
||||
elif self.newTestamentCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 2:
|
||||
addBook = False
|
||||
elif self.apocryphaCheckBox.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 3:
|
||||
addBook = False
|
||||
if addBook:
|
||||
self.correspondingComboBox.addItem(self.book_names[item[u'abbreviation']])
|
||||
if self.old_testament_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 1:
|
||||
add_book = False
|
||||
elif self.new_testament_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 2:
|
||||
add_book = False
|
||||
elif self.apocrypha_check_box.checkState() == QtCore.Qt.Unchecked and item[u'testament_id'] == 3:
|
||||
add_book = False
|
||||
if add_book:
|
||||
self.corresponding_combo_box.addItem(self.book_names[item[u'abbreviation']])
|
||||
|
||||
def exec_(self, name, books, maxbooks):
|
||||
def exec_(self, name, books, max_books):
|
||||
self.books = books
|
||||
log.debug(maxbooks)
|
||||
if maxbooks <= 27:
|
||||
self.oldTestamentCheckBox.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.apocryphaCheckBox.setCheckState(QtCore.Qt.Unchecked)
|
||||
elif maxbooks <= 66:
|
||||
self.apocryphaCheckBox.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.reloadComboBox()
|
||||
self.currentBookLabel.setText(unicode(name))
|
||||
self.correspondingComboBox.setFocus()
|
||||
log.debug(max_books)
|
||||
if max_books <= 27:
|
||||
self.old_testament_check_box.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.apocrypha_check_box.setCheckState(QtCore.Qt.Unchecked)
|
||||
elif max_books <= 66:
|
||||
self.apocrypha_check_box.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.reload_combo_box()
|
||||
self.current_book_label.setText(unicode(name))
|
||||
self.corresponding_combo_box.setFocus()
|
||||
return QDialog.exec_(self)
|
||||
|
||||
def accept(self):
|
||||
if self.correspondingComboBox.currentText() == u'':
|
||||
if not self.corresponding_combo_box.currentText():
|
||||
critical_error_message_box(message=translate('BiblesPlugin.BookNameForm', 'You need to select a book.'))
|
||||
self.correspondingComboBox.setFocus()
|
||||
self.corresponding_combo_box.setFocus()
|
||||
return False
|
||||
else:
|
||||
cor_book = self.correspondingComboBox.currentText()
|
||||
cor_book = self.corresponding_combo_box.currentText()
|
||||
for character in u'\\.^$*+?{}[]()':
|
||||
cor_book = cor_book.replace(character, u'\\' + character)
|
||||
books = filter(lambda key:
|
||||
re.match(cor_book, unicode(self.book_names[key]), re.UNICODE), self.book_names.keys())
|
||||
books = filter(
|
||||
lambda key: re.match(cor_book, unicode(self.book_names[key]), re.UNICODE), self.book_names.keys())
|
||||
books = filter(None, map(BiblesResourcesDB.get_book, books))
|
||||
if books:
|
||||
self.book_id = books[0][u'id']
|
||||
|
@ -36,118 +36,118 @@ from openlp.plugins.bibles.lib.db import BiblesResourcesDB
|
||||
|
||||
|
||||
class Ui_EditBibleDialog(object):
|
||||
def setupUi(self, editBibleDialog):
|
||||
editBibleDialog.setObjectName(u'editBibleDialog')
|
||||
editBibleDialog.resize(520, 400)
|
||||
editBibleDialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png'))
|
||||
editBibleDialog.setModal(True)
|
||||
self.dialogLayout = QtGui.QVBoxLayout(editBibleDialog)
|
||||
self.dialogLayout.setSpacing(8)
|
||||
self.dialogLayout.setContentsMargins(8, 8, 8, 8)
|
||||
self.dialogLayout.setObjectName(u'dialog_layout')
|
||||
self.bibleTabWidget = QtGui.QTabWidget(editBibleDialog)
|
||||
self.bibleTabWidget.setObjectName(u'BibleTabWidget')
|
||||
def setupUi(self, edit_bible_dialog):
|
||||
edit_bible_dialog.setObjectName(u'edit_bible_dialog')
|
||||
edit_bible_dialog.resize(520, 400)
|
||||
edit_bible_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png'))
|
||||
edit_bible_dialog.setModal(True)
|
||||
self.dialog_layout = QtGui.QVBoxLayout(edit_bible_dialog)
|
||||
self.dialog_layout.setSpacing(8)
|
||||
self.dialog_layout.setContentsMargins(8, 8, 8, 8)
|
||||
self.dialog_layout.setObjectName(u'dialog_layout')
|
||||
self.bible_tab_widget = QtGui.QTabWidget(edit_bible_dialog)
|
||||
self.bible_tab_widget.setObjectName(u'BibleTabWidget')
|
||||
# Meta tab
|
||||
self.metaTab = QtGui.QWidget()
|
||||
self.metaTab.setObjectName(u'metaTab')
|
||||
self.metaTabLayout = QtGui.QVBoxLayout(self.metaTab)
|
||||
self.metaTabLayout.setObjectName(u'metaTabLayout')
|
||||
self.licenseDetailsGroupBox = QtGui.QGroupBox(self.metaTab)
|
||||
self.licenseDetailsGroupBox.setObjectName(u'licenseDetailsGroupBox')
|
||||
self.licenseDetailsLayout = QtGui.QFormLayout(self.licenseDetailsGroupBox)
|
||||
self.licenseDetailsLayout.setObjectName(u'licenseDetailsLayout')
|
||||
self.versionNameLabel = QtGui.QLabel(self.licenseDetailsGroupBox)
|
||||
self.versionNameLabel.setObjectName(u'versionNameLabel')
|
||||
self.versionNameEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox)
|
||||
self.versionNameEdit.setObjectName(u'versionNameEdit')
|
||||
self.versionNameLabel.setBuddy(self.versionNameEdit)
|
||||
self.licenseDetailsLayout.addRow(self.versionNameLabel, self.versionNameEdit)
|
||||
self.copyrightLabel = QtGui.QLabel(self.licenseDetailsGroupBox)
|
||||
self.copyrightLabel.setObjectName(u'copyrightLabel')
|
||||
self.copyrightEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox)
|
||||
self.copyrightEdit.setObjectName(u'copyright_edit')
|
||||
self.copyrightLabel.setBuddy(self.copyrightEdit)
|
||||
self.licenseDetailsLayout.addRow(self.copyrightLabel, self.copyrightEdit)
|
||||
self.permissionsLabel = QtGui.QLabel(self.licenseDetailsGroupBox)
|
||||
self.permissionsLabel.setObjectName(u'permissionsLabel')
|
||||
self.permissionsEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox)
|
||||
self.permissionsEdit.setObjectName(u'permissionsEdit')
|
||||
self.permissionsLabel.setBuddy(self.permissionsEdit)
|
||||
self.licenseDetailsLayout.addRow(self.permissionsLabel, self.permissionsEdit)
|
||||
self.metaTabLayout.addWidget(self.licenseDetailsGroupBox)
|
||||
self.languageSelectionGroupBox = QtGui.QGroupBox(self.metaTab)
|
||||
self.languageSelectionGroupBox.setObjectName(u'languageSelectionGroupBox')
|
||||
self.languageSelectionLayout = QtGui.QVBoxLayout(self.languageSelectionGroupBox)
|
||||
self.languageSelectionLabel = QtGui.QLabel(self.languageSelectionGroupBox)
|
||||
self.languageSelectionLabel.setObjectName(u'languageSelectionLabel')
|
||||
self.languageSelectionComboBox = QtGui.QComboBox(self.languageSelectionGroupBox)
|
||||
self.languageSelectionComboBox.setObjectName(u'languageSelectionComboBox')
|
||||
self.languageSelectionComboBox.addItems([u'', u'', u'', u''])
|
||||
self.languageSelectionLayout.addWidget(self.languageSelectionLabel)
|
||||
self.languageSelectionLayout.addWidget(self.languageSelectionComboBox)
|
||||
self.metaTabLayout.addWidget(self.languageSelectionGroupBox)
|
||||
self.metaTabLayout.addStretch()
|
||||
self.bibleTabWidget.addTab(self.metaTab, u'')
|
||||
self.meta_tab = QtGui.QWidget()
|
||||
self.meta_tab.setObjectName(u'meta_tab')
|
||||
self.meta_tab_layout = QtGui.QVBoxLayout(self.meta_tab)
|
||||
self.meta_tab_layout.setObjectName(u'meta_tab_layout')
|
||||
self.license_details_group_box = QtGui.QGroupBox(self.meta_tab)
|
||||
self.license_details_group_box.setObjectName(u'license_details_group_box')
|
||||
self.license_details_layout = QtGui.QFormLayout(self.license_details_group_box)
|
||||
self.license_details_layout.setObjectName(u'license_details_layout')
|
||||
self.version_name_label = QtGui.QLabel(self.license_details_group_box)
|
||||
self.version_name_label.setObjectName(u'version_name_label')
|
||||
self.version_name_edit = QtGui.QLineEdit(self.license_details_group_box)
|
||||
self.version_name_edit.setObjectName(u'version_name_edit')
|
||||
self.version_name_label.setBuddy(self.version_name_edit)
|
||||
self.license_details_layout.addRow(self.version_name_label, self.version_name_edit)
|
||||
self.copyright_label = QtGui.QLabel(self.license_details_group_box)
|
||||
self.copyright_label.setObjectName(u'copyright_label')
|
||||
self.copyright_edit = QtGui.QLineEdit(self.license_details_group_box)
|
||||
self.copyright_edit.setObjectName(u'copyright_edit')
|
||||
self.copyright_label.setBuddy(self.copyright_edit)
|
||||
self.license_details_layout.addRow(self.copyright_label, self.copyright_edit)
|
||||
self.permissions_label = QtGui.QLabel(self.license_details_group_box)
|
||||
self.permissions_label.setObjectName(u'permissions_label')
|
||||
self.permissions_edit = QtGui.QLineEdit(self.license_details_group_box)
|
||||
self.permissions_edit.setObjectName(u'permissions_edit')
|
||||
self.permissions_label.setBuddy(self.permissions_edit)
|
||||
self.license_details_layout.addRow(self.permissions_label, self.permissions_edit)
|
||||
self.meta_tab_layout.addWidget(self.license_details_group_box)
|
||||
self.language_selection_group_box = QtGui.QGroupBox(self.meta_tab)
|
||||
self.language_selection_group_box.setObjectName(u'language_selection_group_box')
|
||||
self.language_selection_layout = QtGui.QVBoxLayout(self.language_selection_group_box)
|
||||
self.language_selection_label = QtGui.QLabel(self.language_selection_group_box)
|
||||
self.language_selection_label.setObjectName(u'language_selection_label')
|
||||
self.language_selection_combo_box = QtGui.QComboBox(self.language_selection_group_box)
|
||||
self.language_selection_combo_box.setObjectName(u'language_selection_combo_box')
|
||||
self.language_selection_combo_box.addItems([u'', u'', u'', u''])
|
||||
self.language_selection_layout.addWidget(self.language_selection_label)
|
||||
self.language_selection_layout.addWidget(self.language_selection_combo_box)
|
||||
self.meta_tab_layout.addWidget(self.language_selection_group_box)
|
||||
self.meta_tab_layout.addStretch()
|
||||
self.bible_tab_widget.addTab(self.meta_tab, u'')
|
||||
# Book name tab
|
||||
self.bookNameTab = QtGui.QWidget()
|
||||
self.bookNameTab.setObjectName(u'bookNameTab')
|
||||
self.bookNameTabLayout = QtGui.QVBoxLayout(self.bookNameTab)
|
||||
self.bookNameTabLayout.setObjectName(u'bookNameTabLayout')
|
||||
self.bookNameNotice = QtGui.QLabel(self.bookNameTab)
|
||||
self.bookNameNotice.setObjectName(u'bookNameNotice')
|
||||
self.bookNameNotice.setWordWrap(True)
|
||||
self.bookNameTabLayout.addWidget(self.bookNameNotice)
|
||||
self.scrollArea = QtGui.QScrollArea(self.bookNameTab)
|
||||
self.scrollArea.setWidgetResizable(True)
|
||||
self.scrollArea.setObjectName(u'scrollArea')
|
||||
self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.bookNameWidget = QtGui.QWidget(self.scrollArea)
|
||||
self.bookNameWidget.setObjectName(u'bookNameWidget')
|
||||
self.bookNameWidgetLayout = QtGui.QFormLayout(self.bookNameWidget)
|
||||
self.bookNameWidgetLayout.setObjectName(u'bookNameWidgetLayout')
|
||||
self.bookNameLabel = {}
|
||||
self.bookNameEdit= {}
|
||||
self.book_name_tab = QtGui.QWidget()
|
||||
self.book_name_tab.setObjectName(u'book_name_tab')
|
||||
self.book_name_tab_layout = QtGui.QVBoxLayout(self.book_name_tab)
|
||||
self.book_name_tab_layout.setObjectName(u'book_name_tab_layout')
|
||||
self.book_name_notice = QtGui.QLabel(self.book_name_tab)
|
||||
self.book_name_notice.setObjectName(u'book_name_notice')
|
||||
self.book_name_notice.setWordWrap(True)
|
||||
self.book_name_tab_layout.addWidget(self.book_name_notice)
|
||||
self.scroll_area = QtGui.QScrollArea(self.book_name_tab)
|
||||
self.scroll_area.setWidgetResizable(True)
|
||||
self.scroll_area.setObjectName(u'scroll_area')
|
||||
self.scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.book_name_widget = QtGui.QWidget(self.scroll_area)
|
||||
self.book_name_widget.setObjectName(u'book_name_widget')
|
||||
self.book_name_widget_layout = QtGui.QFormLayout(self.book_name_widget)
|
||||
self.book_name_widget_layout.setObjectName(u'book_name_widget_layout')
|
||||
self.book_name_label = {}
|
||||
self.book_name_edit= {}
|
||||
for book in BiblesResourcesDB.get_books():
|
||||
self.bookNameLabel[book[u'abbreviation']] = QtGui.QLabel(self.bookNameWidget)
|
||||
self.bookNameLabel[book[u'abbreviation']].setObjectName(u'bookNameLabel[%s]' % book[u'abbreviation'])
|
||||
self.bookNameEdit[book[u'abbreviation']] = QtGui.QLineEdit(self.bookNameWidget)
|
||||
self.bookNameEdit[book[u'abbreviation']].setObjectName(u'bookNameEdit[%s]' % book[u'abbreviation'])
|
||||
self.bookNameWidgetLayout.addRow(
|
||||
self.bookNameLabel[book[u'abbreviation']],
|
||||
self.bookNameEdit[book[u'abbreviation']])
|
||||
self.scrollArea.setWidget(self.bookNameWidget)
|
||||
self.bookNameTabLayout.addWidget(self.scrollArea)
|
||||
self.bookNameTabLayout.addStretch()
|
||||
self.bibleTabWidget.addTab(self.bookNameTab, u'')
|
||||
self.book_name_label[book[u'abbreviation']] = QtGui.QLabel(self.book_name_widget)
|
||||
self.book_name_label[book[u'abbreviation']].setObjectName(u'book_name_label[%s]' % book[u'abbreviation'])
|
||||
self.book_name_edit[book[u'abbreviation']] = QtGui.QLineEdit(self.book_name_widget)
|
||||
self.book_name_edit[book[u'abbreviation']].setObjectName(u'book_name_edit[%s]' % book[u'abbreviation'])
|
||||
self.book_name_widget_layout.addRow(
|
||||
self.book_name_label[book[u'abbreviation']],
|
||||
self.book_name_edit[book[u'abbreviation']])
|
||||
self.scroll_area.setWidget(self.book_name_widget)
|
||||
self.book_name_tab_layout.addWidget(self.scroll_area)
|
||||
self.book_name_tab_layout.addStretch()
|
||||
self.bible_tab_widget.addTab(self.book_name_tab, u'')
|
||||
# Last few bits
|
||||
self.dialogLayout.addWidget(self.bibleTabWidget)
|
||||
self.button_box = create_button_box(editBibleDialog, u'button_box', [u'cancel', u'save'])
|
||||
self.dialogLayout.addWidget(self.button_box)
|
||||
self.retranslateUi(editBibleDialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(editBibleDialog)
|
||||
self.dialog_layout.addWidget(self.bible_tab_widget)
|
||||
self.button_box = create_button_box(edit_bible_dialog, u'button_box', [u'cancel', u'save'])
|
||||
self.dialog_layout.addWidget(self.button_box)
|
||||
self.retranslateUi(edit_bible_dialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(edit_bible_dialog)
|
||||
|
||||
def retranslateUi(self, editBibleDialog):
|
||||
def retranslateUi(self, edit_bible_dialog):
|
||||
self.book_names = BibleStrings().BookNames
|
||||
editBibleDialog.setWindowTitle(translate('BiblesPlugin.EditBibleForm', 'Bible Editor'))
|
||||
edit_bible_dialog.setWindowTitle(translate('BiblesPlugin.EditBibleForm', 'Bible Editor'))
|
||||
# Meta tab
|
||||
self.bibleTabWidget.setTabText( self.bibleTabWidget.indexOf(self.metaTab),
|
||||
self.bible_tab_widget.setTabText( self.bible_tab_widget.indexOf(self.meta_tab),
|
||||
translate('SongsPlugin.EditBibleForm', 'Meta Data'))
|
||||
self.licenseDetailsGroupBox.setTitle(translate('BiblesPlugin.EditBibleForm', 'License Details'))
|
||||
self.versionNameLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Version name:'))
|
||||
self.copyrightLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Copyright:'))
|
||||
self.permissionsLabel.setText(translate('BiblesPlugin.EditBibleForm', 'Permissions:'))
|
||||
self.languageSelectionGroupBox.setTitle(translate('BiblesPlugin.EditBibleForm', 'Default Bible Language'))
|
||||
self.languageSelectionLabel.setText(translate('BiblesPlugin.EditBibleForm',
|
||||
self.license_details_group_box.setTitle(translate('BiblesPlugin.EditBibleForm', 'License Details'))
|
||||
self.version_name_label.setText(translate('BiblesPlugin.EditBibleForm', 'Version name:'))
|
||||
self.copyright_label.setText(translate('BiblesPlugin.EditBibleForm', 'Copyright:'))
|
||||
self.permissions_label.setText(translate('BiblesPlugin.EditBibleForm', 'Permissions:'))
|
||||
self.language_selection_group_box.setTitle(translate('BiblesPlugin.EditBibleForm', 'Default Bible Language'))
|
||||
self.language_selection_label.setText(translate('BiblesPlugin.EditBibleForm',
|
||||
'Book name language in search field, search results and on display:'))
|
||||
self.languageSelectionComboBox.setItemText(0, translate('BiblesPlugin.EditBibleForm', 'Global Settings'))
|
||||
self.languageSelectionComboBox.setItemText(LanguageSelection.Bible + 1,
|
||||
self.language_selection_combo_box.setItemText(0, translate('BiblesPlugin.EditBibleForm', 'Global Settings'))
|
||||
self.language_selection_combo_box.setItemText(LanguageSelection.Bible + 1,
|
||||
translate('BiblesPlugin.EditBibleForm', 'Bible Language'))
|
||||
self.languageSelectionComboBox.setItemText(LanguageSelection.Application + 1,
|
||||
self.language_selection_combo_box.setItemText(LanguageSelection.Application + 1,
|
||||
translate('BiblesPlugin.EditBibleForm', 'Application Language'))
|
||||
self.languageSelectionComboBox.setItemText(LanguageSelection.English + 1,
|
||||
self.language_selection_combo_box.setItemText(LanguageSelection.English + 1,
|
||||
translate('BiblesPlugin.EditBibleForm', 'English'))
|
||||
# Book name tab
|
||||
self.bibleTabWidget.setTabText(self.bibleTabWidget.indexOf(self.bookNameTab),
|
||||
self.bible_tab_widget.setTabText(self.bible_tab_widget.indexOf(self.book_name_tab),
|
||||
translate('SongsPlugin.EditBibleForm', 'Custom Book Names'))
|
||||
for book in BiblesResourcesDB.get_books():
|
||||
self.bookNameLabel[book[u'abbreviation']].setText(u'%s:' % unicode(self.book_names[book[u'abbreviation']]))
|
||||
self.book_name_label[book[u'abbreviation']].setText(u'%s:' % unicode(self.book_names[book[u'abbreviation']]))
|
||||
|
@ -65,33 +65,33 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
"""
|
||||
log.debug(u'Load Bible')
|
||||
self.bible = bible
|
||||
self.versionNameEdit.setText(self.manager.get_meta_data(self.bible, u'name').value)
|
||||
self.copyrightEdit.setText(self.manager.get_meta_data(self.bible, u'copyright').value)
|
||||
self.permissionsEdit.setText(self.manager.get_meta_data(self.bible, u'permissions').value)
|
||||
self.version_name_edit.setText(self.manager.get_meta_data(self.bible, u'name').value)
|
||||
self.copyright_edit.setText(self.manager.get_meta_data(self.bible, u'copyright').value)
|
||||
self.permissions_edit.setText(self.manager.get_meta_data(self.bible, u'permissions').value)
|
||||
book_name_language = self.manager.get_meta_data(self.bible, u'book_name_language')
|
||||
if book_name_language and book_name_language.value != u'None':
|
||||
self.languageSelectionComboBox.setCurrentIndex(int(book_name_language.value) + 1)
|
||||
self.language_selection_combo_box.setCurrentIndex(int(book_name_language.value) + 1)
|
||||
self.books = {}
|
||||
self.webbible = self.manager.get_meta_data(self.bible, u'download_source')
|
||||
if self.webbible:
|
||||
self.bookNameNotice.setText(translate('BiblesPlugin.EditBibleForm',
|
||||
self.book_name_notice.setText(translate('BiblesPlugin.EditBibleForm',
|
||||
'This is a Web Download Bible.\nIt is not possible to customize the Book Names.'))
|
||||
self.scrollArea.hide()
|
||||
self.scroll_area.hide()
|
||||
else:
|
||||
self.bookNameNotice.setText(translate('BiblesPlugin.EditBibleForm',
|
||||
self.book_name_notice.setText(translate('BiblesPlugin.EditBibleForm',
|
||||
'To use the customized book names, "Bible language" must be selected on the Meta Data tab or, '
|
||||
'if "Global settings" is selected, on the Bible page in Configure OpenLP.'))
|
||||
for book in BiblesResourcesDB.get_books():
|
||||
self.books[book[u'abbreviation']] = self.manager.get_book_by_id(self.bible, book[u'id'])
|
||||
if self.books[book[u'abbreviation']] and not self.webbible:
|
||||
self.bookNameEdit[book[u'abbreviation']].setText(self.books[book[u'abbreviation']].name)
|
||||
self.book_name_edit[book[u'abbreviation']].setText(self.books[book[u'abbreviation']].name)
|
||||
else:
|
||||
# It is necessary to remove the Widget otherwise there still
|
||||
# exists the vertical spacing in QFormLayout
|
||||
self.bookNameWidgetLayout.removeWidget(self.bookNameLabel[book[u'abbreviation']])
|
||||
self.bookNameLabel[book[u'abbreviation']].hide()
|
||||
self.bookNameWidgetLayout.removeWidget(self.bookNameEdit[book[u'abbreviation']])
|
||||
self.bookNameEdit[book[u'abbreviation']].hide()
|
||||
self.book_name_widget_layout.removeWidget(self.book_name_label[book[u'abbreviation']])
|
||||
self.book_name_label[book[u'abbreviation']].hide()
|
||||
self.book_name_widget_layout.removeWidget(self.book_name_edit[book[u'abbreviation']])
|
||||
self.book_name_edit[book[u'abbreviation']].hide()
|
||||
|
||||
def reject(self):
|
||||
"""
|
||||
@ -106,10 +106,10 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
Exit Dialog and save data
|
||||
"""
|
||||
log.debug(u'BibleEditForm.accept')
|
||||
version = self.versionNameEdit.text()
|
||||
copyright = self.copyrightEdit.text()
|
||||
permissions = self.permissionsEdit.text()
|
||||
book_name_language = self.languageSelectionComboBox.currentIndex() - 1
|
||||
version = self.version_name_edit.text()
|
||||
copyright = self.copyright_edit.text()
|
||||
permissions = self.permissions_edit.text()
|
||||
book_name_language = self.language_selection_combo_box.currentIndex() - 1
|
||||
if book_name_language == -1:
|
||||
book_name_language = None
|
||||
if not self.validateMeta(version, copyright):
|
||||
@ -118,7 +118,7 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
custom_names = {}
|
||||
for abbr, book in self.books.iteritems():
|
||||
if book:
|
||||
custom_names[abbr] = self.bookNameEdit[abbr].text()
|
||||
custom_names[abbr] = self.book_name_edit[abbr].text()
|
||||
if book.name != custom_names[abbr]:
|
||||
if not self.validateBook(custom_names[abbr], abbr):
|
||||
return
|
||||
@ -139,19 +139,19 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
Validate the Meta before saving.
|
||||
"""
|
||||
if not name:
|
||||
self.versionNameEdit.setFocus()
|
||||
self.version_name_edit.setFocus()
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
translate('BiblesPlugin.BibleEditForm', 'You need to specify a version name for your Bible.'))
|
||||
return False
|
||||
elif not copyright:
|
||||
self.copyrightEdit.setFocus()
|
||||
self.copyright_edit.setFocus()
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
translate('BiblesPlugin.BibleEditForm',
|
||||
'You need to set a copyright for your Bible. Bibles in the Public Domain need to be marked as such.'))
|
||||
return False
|
||||
elif self.manager.exists(name) and self.manager.get_meta_data(self.bible, u'name').value != \
|
||||
name:
|
||||
self.versionNameEdit.setFocus()
|
||||
self.version_name_edit.setFocus()
|
||||
critical_error_message_box(translate('BiblesPlugin.BibleEditForm', 'Bible Exists'),
|
||||
translate('BiblesPlugin.BibleEditForm', 'This Bible already exists. Please import '
|
||||
'a different Bible or first delete the existing one.'))
|
||||
@ -164,13 +164,13 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
"""
|
||||
book_regex = re.compile(u'[\d]*[^\d]+$')
|
||||
if not new_book_name:
|
||||
self.bookNameEdit[abbreviation].setFocus()
|
||||
self.book_name_edit[abbreviation].setFocus()
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
translate('BiblesPlugin.BibleEditForm', 'You need to specify a book name for "%s".') %
|
||||
self.book_names[abbreviation])
|
||||
return False
|
||||
elif not book_regex.match(new_book_name):
|
||||
self.bookNameEdit[abbreviation].setFocus()
|
||||
self.book_name_edit[abbreviation].setFocus()
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
translate('BiblesPlugin.BibleEditForm',
|
||||
'The book name "%s" is not correct.\nNumbers can only be used at the beginning and must\nbe '
|
||||
@ -180,8 +180,8 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
if book:
|
||||
if abbr == abbreviation:
|
||||
continue
|
||||
if self.bookNameEdit[abbr].text() == new_book_name:
|
||||
self.bookNameEdit[abbreviation].setFocus()
|
||||
if self.book_name_edit[abbr].text() == new_book_name:
|
||||
self.book_name_edit[abbreviation].setFocus()
|
||||
critical_error_message_box(
|
||||
translate('BiblesPlugin.BibleEditForm', 'Duplicate Book Name'),
|
||||
translate('BiblesPlugin.BibleEditForm', 'The Book Name "%s" has been entered more than once.')
|
||||
|
@ -33,44 +33,44 @@ from openlp.core.lib import translate
|
||||
from openlp.core.lib.ui import create_button_box
|
||||
|
||||
class Ui_LanguageDialog(object):
|
||||
def setupUi(self, languageDialog):
|
||||
languageDialog.setObjectName(u'languageDialog')
|
||||
languageDialog.resize(400, 165)
|
||||
self.languageLayout = QtGui.QVBoxLayout(languageDialog)
|
||||
self.languageLayout.setSpacing(8)
|
||||
self.languageLayout.setMargin(8)
|
||||
self.languageLayout.setObjectName(u'languageLayout')
|
||||
self.bibleLabel = QtGui.QLabel(languageDialog)
|
||||
self.bibleLabel.setObjectName(u'bibleLabel')
|
||||
self.languageLayout.addWidget(self.bibleLabel)
|
||||
self.infoLabel = QtGui.QLabel(languageDialog)
|
||||
self.infoLabel.setWordWrap(True)
|
||||
self.infoLabel.setObjectName(u'infoLabel')
|
||||
self.languageLayout.addWidget(self.infoLabel)
|
||||
self.languageHBoxLayout = QtGui.QHBoxLayout()
|
||||
self.languageHBoxLayout.setSpacing(8)
|
||||
self.languageHBoxLayout.setObjectName(u'languageHBoxLayout')
|
||||
self.languageLabel = QtGui.QLabel(languageDialog)
|
||||
self.languageLabel.setObjectName(u'languageLabel')
|
||||
self.languageHBoxLayout.addWidget(self.languageLabel)
|
||||
self.languageComboBox = QtGui.QComboBox(languageDialog)
|
||||
def setupUi(self, language_dialog):
|
||||
language_dialog.setObjectName(u'language_dialog')
|
||||
language_dialog.resize(400, 165)
|
||||
self.language_layout = QtGui.QVBoxLayout(language_dialog)
|
||||
self.language_layout.setSpacing(8)
|
||||
self.language_layout.setMargin(8)
|
||||
self.language_layout.setObjectName(u'language_layout')
|
||||
self.bible_label = QtGui.QLabel(language_dialog)
|
||||
self.bible_label.setObjectName(u'bible_label')
|
||||
self.language_layout.addWidget(self.bible_label)
|
||||
self.info_label = QtGui.QLabel(language_dialog)
|
||||
self.info_label.setWordWrap(True)
|
||||
self.info_label.setObjectName(u'info_label')
|
||||
self.language_layout.addWidget(self.info_label)
|
||||
self.language_h_box_layout = QtGui.QHBoxLayout()
|
||||
self.language_h_box_layout.setSpacing(8)
|
||||
self.language_h_box_layout.setObjectName(u'language_h_box_layout')
|
||||
self.language_label = QtGui.QLabel(language_dialog)
|
||||
self.language_label.setObjectName(u'language_label')
|
||||
self.language_h_box_layout.addWidget(self.language_label)
|
||||
self.language_combo_box = QtGui.QComboBox(language_dialog)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.languageComboBox.sizePolicy().hasHeightForWidth())
|
||||
self.languageComboBox.setSizePolicy(sizePolicy)
|
||||
self.languageComboBox.setObjectName(u'languageComboBox')
|
||||
self.languageHBoxLayout.addWidget(self.languageComboBox)
|
||||
self.languageLayout.addLayout(self.languageHBoxLayout)
|
||||
self.button_box = create_button_box(languageDialog, u'button_box', [u'cancel', u'ok'])
|
||||
self.languageLayout.addWidget(self.button_box)
|
||||
sizePolicy.setHeightForWidth(self.language_combo_box.sizePolicy().hasHeightForWidth())
|
||||
self.language_combo_box.setSizePolicy(sizePolicy)
|
||||
self.language_combo_box.setObjectName(u'language_combo_box')
|
||||
self.language_h_box_layout.addWidget(self.language_combo_box)
|
||||
self.language_layout.addLayout(self.language_h_box_layout)
|
||||
self.button_box = create_button_box(language_dialog, u'button_box', [u'cancel', u'ok'])
|
||||
self.language_layout.addWidget(self.button_box)
|
||||
|
||||
self.retranslateUi(languageDialog)
|
||||
self.retranslateUi(language_dialog)
|
||||
|
||||
def retranslateUi(self, languageDialog):
|
||||
languageDialog.setWindowTitle(translate('BiblesPlugin.LanguageDialog', 'Select Language'))
|
||||
self.bibleLabel.setText(translate('BiblesPlugin.LanguageDialog', ''))
|
||||
self.infoLabel.setText(translate('BiblesPlugin.LanguageDialog',
|
||||
def retranslateUi(self, language_dialog):
|
||||
language_dialog.setWindowTitle(translate('BiblesPlugin.LanguageDialog', 'Select Language'))
|
||||
self.bible_label.setText(translate('BiblesPlugin.LanguageDialog', ''))
|
||||
self.info_label.setText(translate('BiblesPlugin.LanguageDialog',
|
||||
'OpenLP is unable to determine the language of this translation of the Bible. Please select the language '
|
||||
'from the list below.'))
|
||||
self.languageLabel.setText(translate('BiblesPlugin.LanguageDialog', 'Language:'))
|
||||
self.language_label.setText(translate('BiblesPlugin.LanguageDialog', 'Language:'))
|
||||
|
@ -40,8 +40,10 @@ from openlp.plugins.bibles.forms.languagedialog import \
|
||||
Ui_LanguageDialog
|
||||
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LanguageForm(QDialog, Ui_LanguageDialog):
|
||||
"""
|
||||
Class to manage a dialog which ask the user for a language.
|
||||
@ -56,19 +58,17 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
|
||||
self.setupUi(self)
|
||||
|
||||
def exec_(self, bible_name):
|
||||
self.languageComboBox.addItem(u'')
|
||||
self.language_combo_box.addItem(u'')
|
||||
if bible_name:
|
||||
self.bibleLabel.setText(unicode(bible_name))
|
||||
self.bible_label.setText(unicode(bible_name))
|
||||
items = BiblesResourcesDB.get_languages()
|
||||
self.languageComboBox.addItems([item[u'name'] for item in items])
|
||||
self.language_combo_box.addItems([item[u'name'] for item in items])
|
||||
return QDialog.exec_(self)
|
||||
|
||||
def accept(self):
|
||||
if not self.languageComboBox.currentText():
|
||||
critical_error_message_box(
|
||||
message=translate('BiblesPlugin.LanguageForm',
|
||||
'You need to choose a language.'))
|
||||
self.languageComboBox.setFocus()
|
||||
if not self.language_combo_box.currentText():
|
||||
critical_error_message_box(message=translate('BiblesPlugin.LanguageForm', 'You need to choose a language.'))
|
||||
self.language_combo_box.setFocus()
|
||||
return False
|
||||
else:
|
||||
return QDialog.accept(self)
|
||||
|
@ -38,9 +38,11 @@ from openlp.core.lib import Settings, translate
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
REFERENCE_MATCHES = {}
|
||||
REFERENCE_SEPARATORS = {}
|
||||
|
||||
|
||||
class LayoutStyle(object):
|
||||
"""
|
||||
An enumeration for bible screen layout styles.
|
||||
@ -62,8 +64,7 @@ class DisplayStyle(object):
|
||||
|
||||
class LanguageSelection(object):
|
||||
"""
|
||||
An enumeration for bible bookname language.
|
||||
And standard strings for use throughout the bibles plugin.
|
||||
An enumeration for bible bookname language. And standard strings for use throughout the bibles plugin.
|
||||
"""
|
||||
Bible = 0
|
||||
Application = 1
|
||||
@ -178,8 +179,7 @@ class BibleStrings(object):
|
||||
|
||||
def update_reference_separators():
|
||||
"""
|
||||
Updates separators and matches for parsing and formating scripture
|
||||
references.
|
||||
Updates separators and matches for parsing and formating scripture references.
|
||||
"""
|
||||
default_separators = translate('BiblesPlugin',
|
||||
':|v|V|verse|verses;;-|to;;,|and;;end Double-semicolon delimited separators for parsing references. '
|
||||
@ -245,9 +245,8 @@ def get_reference_match(match_type):
|
||||
|
||||
def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
||||
"""
|
||||
This is the next generation über-awesome function that takes a person's
|
||||
typed in string and converts it to a list of references to be queried from
|
||||
the Bible database files.
|
||||
This is the next generation über-awesome function that takes a person's typed in string and converts it to a list
|
||||
of references to be queried from the Bible database files.
|
||||
|
||||
``reference``
|
||||
A string. The Bible reference to parse.
|
||||
@ -256,16 +255,14 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
||||
A object. The Bible database object.
|
||||
|
||||
``language_selection``
|
||||
An int. The language selection the user has choosen in settings
|
||||
section.
|
||||
An int. The language selection the user has choosen in settings section.
|
||||
|
||||
``book_ref_id``
|
||||
A string. The book reference id.
|
||||
|
||||
Returns ``None`` or a reference list.
|
||||
|
||||
The reference list is a list of tuples, with each tuple structured like
|
||||
this::
|
||||
The reference list is a list of tuples, with each tuple structured like this::
|
||||
|
||||
(book, chapter, from_verse, to_verse)
|
||||
|
||||
@ -275,8 +272,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
||||
|
||||
**Reference string details:**
|
||||
|
||||
Each reference starts with the book name and a chapter number. These are
|
||||
both mandatory.
|
||||
Each reference starts with the book name and a chapter number. These are both mandatory.
|
||||
|
||||
* ``John 3`` refers to Gospel of John chapter 3
|
||||
|
||||
@ -289,38 +285,31 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
||||
* ``John 3:16`` refers to John chapter 3 verse 16
|
||||
* ``John 3:16-4:3`` refers to John chapter 3 verse 16 to chapter 4 verse 3
|
||||
|
||||
After a verse reference all further single values are treat as verse in
|
||||
the last selected chapter.
|
||||
After a verse reference all further single values are treat as verse in the last selected chapter.
|
||||
|
||||
* ``John 3:16-18`` refers to John chapter 3 verses 16 to 18
|
||||
|
||||
After a list separator it is possible to refer to additional verses. They
|
||||
are build analog to the first ones. This way it is possible to define each
|
||||
number of verse references. It is not possible to refer to verses in
|
||||
additional books.
|
||||
After a list separator it is possible to refer to additional verses. They are build analog to the first ones. This
|
||||
way it is possible to define each number of verse references. It is not possible to refer to verses in additional
|
||||
books.
|
||||
|
||||
* ``John 3:16,18`` refers to John chapter 3 verses 16 and 18
|
||||
* ``John 3:16-18,20`` refers to John chapter 3 verses 16 to 18 and 20
|
||||
* ``John 3:16-18,4:1`` refers to John chapter 3 verses 16 to 18 and
|
||||
chapter 4 verse 1
|
||||
* ``John 3:16-18,4:1`` refers to John chapter 3 verses 16 to 18 and chapter 4 verse 1
|
||||
|
||||
If there is a range separator without further verse declaration the last
|
||||
refered chapter is addressed until the end.
|
||||
If there is a range separator without further verse declaration the last refered chapter is addressed until the end.
|
||||
|
||||
``range_regex`` is a regular expression which matches for verse range
|
||||
declarations:
|
||||
``range_regex`` is a regular expression which matches for verse range declarations:
|
||||
|
||||
``(?:(?P<from_chapter>[0-9]+)%(sep_v)s)?``
|
||||
It starts with a optional chapter reference ``from_chapter`` followed by
|
||||
a verse separator.
|
||||
It starts with a optional chapter reference ``from_chapter`` followed by a verse separator.
|
||||
|
||||
``(?P<from_verse>[0-9]+)``
|
||||
The verse reference ``from_verse`` is manditory
|
||||
|
||||
``(?P<range_to>%(sep_r)s(?:`` ... ``|%(sep_e)s)?)?``
|
||||
A ``range_to`` declaration is optional. It starts with a range separator
|
||||
and contains optional a chapter and verse declaration or a end
|
||||
separator.
|
||||
A ``range_to`` declaration is optional. It starts with a range separator and contains optional a chapter and
|
||||
verse declaration or a end separator.
|
||||
|
||||
``(?:(?P<to_chapter>[0-9]+)%(sep_v)s)?``
|
||||
The ``to_chapter`` reference with separator is equivalent to group 1.
|
||||
@ -328,17 +317,15 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
||||
``(?P<to_verse>[0-9]+)``
|
||||
The ``to_verse`` reference is equivalent to group 2.
|
||||
|
||||
The full reference is matched against get_reference_match(u'full'). This
|
||||
regular expression looks like this:
|
||||
The full reference is matched against get_reference_match(u'full'). This regular expression looks like this:
|
||||
|
||||
``^\s*(?!\s)(?P<book>[\d]*[^\d]+)(?<!\s)\s*``
|
||||
The ``book`` group starts with the first non-whitespace character. There
|
||||
are optional leading digits followed by non-digits. The group ends
|
||||
before the whitspace in front of the next digit.
|
||||
The ``book`` group starts with the first non-whitespace character. There are optional leading digits followed by
|
||||
non-digits. The group ends before the whitspace in front of the next digit.
|
||||
|
||||
``(?P<ranges>(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$``
|
||||
The second group contains all ``ranges``. This can be multiple
|
||||
declarations of range_regex separated by a list separator.
|
||||
The second group contains all ``ranges``. This can be multiple declarations of range_regex separated by a list
|
||||
separator.
|
||||
|
||||
"""
|
||||
log.debug(u'parse_reference("%s")', reference)
|
||||
|
@ -27,8 +27,7 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`cvsbible` modules provides a facility to import bibles from a set of
|
||||
CSV files.
|
||||
The :mod:`cvsbible` modules provides a facility to import bibles from a set of CSV files.
|
||||
|
||||
The module expects two mandatory files containing the books and the verses.
|
||||
|
||||
@ -55,8 +54,7 @@ There are two acceptable formats of the verses file. They are:
|
||||
or
|
||||
"Genesis",1,2,"And the earth was without form, and void; and...."
|
||||
|
||||
All CSV files are expected to use a comma (',') as the delimiter and double
|
||||
quotes ('"') as the quote symbol.
|
||||
All CSV files are expected to use a comma (',') as the delimiter and double quotes ('"') as the quote symbol.
|
||||
"""
|
||||
import logging
|
||||
import chardet
|
||||
@ -65,8 +63,10 @@ import csv
|
||||
from openlp.core.lib import translate
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CSVBible(BibleDB):
|
||||
"""
|
||||
This class provides a specialisation for importing of CSV Bibles.
|
||||
@ -75,9 +75,8 @@ class CSVBible(BibleDB):
|
||||
|
||||
def __init__(self, parent, **kwargs):
|
||||
"""
|
||||
Loads a Bible from a set of CSV files.
|
||||
This class assumes the files contain all the information and
|
||||
a clean bible is being loaded.
|
||||
Loads a Bible from a set of CSV files. This class assumes the files contain all the information and a clean
|
||||
bible is being loaded.
|
||||
"""
|
||||
log.info(self.__class__.__name__)
|
||||
BibleDB.__init__(self, parent, **kwargs)
|
||||
|
@ -511,7 +511,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
language = None
|
||||
language_form = LanguageForm(self.wizard)
|
||||
if language_form.exec_(bible_name):
|
||||
language = unicode(language_form.languageComboBox.currentText())
|
||||
language = unicode(language_form.language_combo_box.currentText())
|
||||
if not language:
|
||||
return False
|
||||
language = BiblesResourcesDB.get_language(language)
|
||||
|
@ -27,8 +27,7 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`http` module enables OpenLP to retrieve scripture from bible
|
||||
websites.
|
||||
The :mod:`http` module enables OpenLP to retrieve scripture from bible websites.
|
||||
"""
|
||||
import logging
|
||||
import re
|
||||
@ -36,7 +35,7 @@ import socket
|
||||
import urllib
|
||||
from HTMLParser import HTMLParseError
|
||||
|
||||
from BeautifulSoup import BeautifulSoup, NavigableString, Tag
|
||||
from bs4 import BeautifulSoup, NavigableString, Tag
|
||||
|
||||
from openlp.core.lib import Registry, translate
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
@ -44,6 +43,9 @@ from openlp.core.utils import get_web_page
|
||||
from openlp.plugins.bibles.lib import SearchResults
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB, Book
|
||||
|
||||
CLEANER_REGEX = re.compile(r' |<br />|\'\+\'')
|
||||
FIX_PUNKCTUATION_REGEX = re.compile(r'[ ]+([.,;])')
|
||||
REDUCE_SPACES_REGEX = re.compile(r'[ ]{2,}')
|
||||
UGLY_CHARS = {
|
||||
u'\u2014': u' - ',
|
||||
u'\u2018': u'\'',
|
||||
@ -52,9 +54,12 @@ UGLY_CHARS = {
|
||||
u'\u201d': u'"',
|
||||
u' ': u' '
|
||||
}
|
||||
VERSE_NUMBER_REGEX = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*')
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BGExtract(object):
|
||||
"""
|
||||
Extract verses from BibleGateway
|
||||
@ -78,9 +83,9 @@ class BGExtract(object):
|
||||
An HTML class attribute for further qualification.
|
||||
"""
|
||||
if class_:
|
||||
all_tags = parent.findAll(tag, class_)
|
||||
all_tags = parent.find_all(tag, class_)
|
||||
else:
|
||||
all_tags = parent.findAll(tag)
|
||||
all_tags = parent.find_all(tag)
|
||||
for element in all_tags:
|
||||
element.extract()
|
||||
|
||||
@ -93,14 +98,15 @@ class BGExtract(object):
|
||||
"""
|
||||
if isinstance(tag, NavigableString):
|
||||
return None, unicode(tag)
|
||||
elif tag.get('class') == 'versenum' or tag.get('class') == 'versenum mid-line':
|
||||
elif tag.get('class')[0] == "versenum" or tag.get('class')[0] == 'versenum mid-line':
|
||||
verse = unicode(tag.string).replace('[', '').replace(']', '').strip()
|
||||
return verse, None
|
||||
elif tag.get('class') == 'chapternum':
|
||||
elif tag.get('class')[0] == 'chapternum':
|
||||
verse = '1'
|
||||
return verse, None
|
||||
else:
|
||||
verse, text = None, ''
|
||||
verse = None
|
||||
text = ''
|
||||
for child in tag.contents:
|
||||
c_verse, c_text = self._extract_verse(child)
|
||||
if c_verse:
|
||||
@ -137,7 +143,8 @@ class BGExtract(object):
|
||||
tags = tags[::-1]
|
||||
current_text = ''
|
||||
for tag in tags:
|
||||
verse, text = None, ''
|
||||
verse = None
|
||||
text = ''
|
||||
for child in tag.contents:
|
||||
c_verse, c_text = self._extract_verse(child)
|
||||
if c_verse:
|
||||
@ -173,8 +180,8 @@ class BGExtract(object):
|
||||
|
||||
def _extract_verses_old(self, div):
|
||||
"""
|
||||
Use the old style of parsing for those Bibles on BG who mysteriously
|
||||
have not been migrated to the new (still broken) HTML.
|
||||
Use the old style of parsing for those Bibles on BG who mysteriously have not been migrated to the new (still
|
||||
broken) HTML.
|
||||
|
||||
``div``
|
||||
The parent div.
|
||||
@ -185,13 +192,12 @@ class BGExtract(object):
|
||||
if first_verse and first_verse.contents:
|
||||
verse_list[1] = unicode(first_verse.contents[0])
|
||||
for verse in div(u'sup', u'versenum'):
|
||||
raw_verse_num = verse.next
|
||||
raw_verse_num = verse.next_element
|
||||
clean_verse_num = 0
|
||||
# Not all verses exist in all translations and may or may not be
|
||||
# represented by a verse number. If they are not fine, if they are
|
||||
# it will probably be in a format that breaks int(). We will then
|
||||
# have no idea what garbage may be sucked in to the verse text so
|
||||
# if we do not get a clean int() then ignore the verse completely.
|
||||
# Not all verses exist in all translations and may or may not be represented by a verse number. If they are
|
||||
# not fine, if they are it will probably be in a format that breaks int(). We will then have no idea what
|
||||
# garbage may be sucked in to the verse text so if we do not get a clean int() then ignore the verse
|
||||
# completely.
|
||||
try:
|
||||
clean_verse_num = int(str(raw_verse_num))
|
||||
except ValueError:
|
||||
@ -201,16 +207,16 @@ class BGExtract(object):
|
||||
except TypeError:
|
||||
log.warn(u'Illegal verse number: %s', unicode(raw_verse_num))
|
||||
if clean_verse_num:
|
||||
verse_text = raw_verse_num.next
|
||||
part = raw_verse_num.next.next
|
||||
while not (isinstance(part, Tag) and part.get(u'class') == u'versenum'):
|
||||
verse_text = raw_verse_num.next_element
|
||||
part = raw_verse_num.next_element.next_element
|
||||
while not (isinstance(part, Tag) and part.get(u'class')[0] == u'versenum'):
|
||||
# While we are still in the same verse grab all the text.
|
||||
if isinstance(part, NavigableString):
|
||||
verse_text += part
|
||||
if isinstance(part.next, Tag) and part.next.name == u'div':
|
||||
if isinstance(part.next_element, Tag) and part.next_element.name == u'div':
|
||||
# Run out of verses so stop.
|
||||
break
|
||||
part = part.next
|
||||
part = part.next_element
|
||||
verse_list[clean_verse_num] = unicode(verse_text)
|
||||
return verse_list
|
||||
|
||||
@ -230,15 +236,14 @@ class BGExtract(object):
|
||||
log.debug(u'BGExtract.get_bible_chapter("%s", "%s", "%s")', version, book_name, chapter)
|
||||
url_book_name = urllib.quote(book_name.encode("utf-8"))
|
||||
url_params = u'search=%s+%s&version=%s' % (url_book_name, chapter, version)
|
||||
cleaner = [(re.compile(' |<br />|\'\+\''), lambda match: '')]
|
||||
soup = get_soup_for_bible_ref(
|
||||
u'http://www.biblegateway.com/passage/?%s' % url_params,
|
||||
pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='', cleaner=cleaner)
|
||||
pre_parse_regex=r'<meta name.*?/>', pre_parse_substitute='')
|
||||
if not soup:
|
||||
return None
|
||||
div = soup.find('div', 'result-text-style-normal')
|
||||
self._clean_soup(div)
|
||||
span_list = div.findAll('span', 'text')
|
||||
span_list = div.find_all('span', 'text')
|
||||
log.debug('Span list: %s', span_list)
|
||||
if not span_list:
|
||||
# If we don't get any spans then we must have the old HTML format
|
||||
@ -282,7 +287,7 @@ class BGExtract(object):
|
||||
self.application.process_events()
|
||||
content = soup.find(u'table', u'infotable')
|
||||
if content:
|
||||
content = content.findAll(u'tr')
|
||||
content = content.find_all(u'tr')
|
||||
if not content:
|
||||
log.error(u'No books found in the Biblegateway response.')
|
||||
send_error_message(u'parse')
|
||||
@ -341,19 +346,17 @@ class BSExtract(object):
|
||||
log.error(u'No verses found in the Bibleserver response.')
|
||||
send_error_message(u'parse')
|
||||
return None
|
||||
content = content.find(u'div').findAll(u'div')
|
||||
verse_number = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse.*')
|
||||
content = content.find(u'div').find_all(u'div')
|
||||
verses = {}
|
||||
for verse in content:
|
||||
self.application.process_events()
|
||||
versenumber = int(verse_number.sub(r'\3', verse[u'class']))
|
||||
versenumber = int(VERSE_NUMBER_REGEX.sub(r'\3', u' '.join(verse[u'class'])))
|
||||
verses[versenumber] = verse.contents[1].rstrip(u'\n')
|
||||
return SearchResults(book_name, chapter, verses)
|
||||
|
||||
def get_books_from_http(self, version):
|
||||
"""
|
||||
Load a list of all books a Bible contains from Bibleserver mobile
|
||||
website.
|
||||
Load a list of all books a Bible contains from Bibleserver mobile website.
|
||||
|
||||
``version``
|
||||
The version of the Bible like NIV for New International Version
|
||||
@ -369,9 +372,19 @@ class BSExtract(object):
|
||||
log.error(u'No books found in the Bibleserver response.')
|
||||
send_error_message(u'parse')
|
||||
return None
|
||||
content = content.findAll(u'li')
|
||||
content = content.find_all(u'li')
|
||||
return [book.contents[0].contents[0] for book in content]
|
||||
|
||||
def _get_application(self):
|
||||
"""
|
||||
Adds the openlp to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, u'_application'):
|
||||
self._application = Registry().get(u'application')
|
||||
return self._application
|
||||
|
||||
application = property(_get_application)
|
||||
|
||||
|
||||
class CWExtract(object):
|
||||
"""
|
||||
@ -404,14 +417,12 @@ class CWExtract(object):
|
||||
if not soup:
|
||||
return None
|
||||
self.application.process_events()
|
||||
html_verses = soup.findAll(u'span', u'versetext')
|
||||
html_verses = soup.find_all(u'span', u'versetext')
|
||||
if not html_verses:
|
||||
log.error(u'No verses found in the CrossWalk response.')
|
||||
send_error_message(u'parse')
|
||||
return None
|
||||
verses = {}
|
||||
reduce_spaces = re.compile(r'[ ]{2,}')
|
||||
fix_punctuation = re.compile(r'[ ]+([.,;])')
|
||||
for verse in html_verses:
|
||||
self.application.process_events()
|
||||
verse_number = int(verse.contents[0].contents[0])
|
||||
@ -432,11 +443,10 @@ class CWExtract(object):
|
||||
if isinstance(subsub, NavigableString):
|
||||
verse_text += subsub
|
||||
self.application.process_events()
|
||||
# Fix up leading and trailing spaces, multiple spaces, and spaces
|
||||
# between text and , and .
|
||||
# Fix up leading and trailing spaces, multiple spaces, and spaces between text and , and .
|
||||
verse_text = verse_text.strip(u'\n\r\t ')
|
||||
verse_text = reduce_spaces.sub(u' ', verse_text)
|
||||
verse_text = fix_punctuation.sub(r'\1', verse_text)
|
||||
verse_text = REDUCE_SPACES_REGEX.sub(u' ', verse_text)
|
||||
verse_text = FIX_PUNKCTUATION_REGEX.sub(r'\1', verse_text)
|
||||
verses[verse_number] = verse_text
|
||||
return SearchResults(book_name, chapter, verses)
|
||||
|
||||
@ -458,7 +468,7 @@ class CWExtract(object):
|
||||
log.error(u'No books found in the Crosswalk response.')
|
||||
send_error_message(u'parse')
|
||||
return None
|
||||
content = content.findAll(u'li')
|
||||
content = content.find_all(u'li')
|
||||
books = []
|
||||
for book in content:
|
||||
book = book.find(u'a')
|
||||
@ -481,9 +491,8 @@ class HTTPBible(BibleDB):
|
||||
|
||||
def __init__(self, parent, **kwargs):
|
||||
"""
|
||||
Finds all the bibles defined for the system
|
||||
Creates an Interface Object for each bible containing connection
|
||||
information
|
||||
Finds all the bibles defined for the system. Creates an Interface Object for each bible containing connection
|
||||
information.
|
||||
|
||||
Throws Exception if no Bibles are found.
|
||||
|
||||
@ -492,8 +501,7 @@ class HTTPBible(BibleDB):
|
||||
BibleDB.__init__(self, parent, **kwargs)
|
||||
self.download_source = kwargs[u'download_source']
|
||||
self.download_name = kwargs[u'download_name']
|
||||
# TODO: Clean up proxy stuff. We probably want one global proxy per
|
||||
# connection type (HTTP and HTTPS) at most.
|
||||
# TODO: Clean up proxy stuff. We probably want one global proxy per connection type (HTTP and HTTPS) at most.
|
||||
self.proxy_server = None
|
||||
self.proxy_username = None
|
||||
self.proxy_password = None
|
||||
@ -508,8 +516,8 @@ class HTTPBible(BibleDB):
|
||||
|
||||
def do_import(self, bible_name=None):
|
||||
"""
|
||||
Run the import. This method overrides the parent class method. Returns
|
||||
``True`` on success, ``False`` on failure.
|
||||
Run the import. This method overrides the parent class method. Returns ``True`` on success, ``False`` on
|
||||
failure.
|
||||
"""
|
||||
self.wizard.progress_bar.setMaximum(68)
|
||||
self.wizard.increment_progress_bar(translate('BiblesPlugin.HTTPBible', 'Registering Bible and loading books...'))
|
||||
@ -549,8 +557,7 @@ class HTTPBible(BibleDB):
|
||||
if self.stop_import_flag:
|
||||
break
|
||||
self.wizard.increment_progress_bar(translate(
|
||||
'BiblesPlugin.HTTPBible', 'Importing %s...',
|
||||
'Importing <book name>...') % book)
|
||||
'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing <book name>...') % book)
|
||||
book_ref_id = self.get_book_ref_id_by_name(book, len(books), language_id)
|
||||
if not book_ref_id:
|
||||
log.exception(u'Importing books from %s - download name: "%s" '\
|
||||
@ -567,22 +574,19 @@ class HTTPBible(BibleDB):
|
||||
|
||||
def get_verses(self, reference_list, show_error=True):
|
||||
"""
|
||||
A reimplementation of the ``BibleDB.get_verses`` method, this one is
|
||||
specifically for web Bibles. It first checks to see if the particular
|
||||
chapter exists in the DB, and if not it pulls it from the web. If the
|
||||
chapter DOES exist, it simply pulls the verses from the DB using the
|
||||
ancestor method.
|
||||
A reimplementation of the ``BibleDB.get_verses`` method, this one is specifically for web Bibles. It first
|
||||
checks to see if the particular chapter exists in the DB, and if not it pulls it from the web. If the chapter
|
||||
DOES exist, it simply pulls the verses from the DB using the ancestor method.
|
||||
|
||||
``reference_list``
|
||||
This is the list of references the media manager item wants. It is
|
||||
a list of tuples, with the following format::
|
||||
This is the list of references the media manager item wants. It is a list of tuples, with the following
|
||||
format::
|
||||
|
||||
(book_reference_id, chapter, start_verse, end_verse)
|
||||
|
||||
Therefore, when you are looking for multiple items, simply break
|
||||
them up into references like this, bundle them into a list. This
|
||||
function then runs through the list, and returns an amalgamated
|
||||
list of ``Verse`` objects. For example::
|
||||
Therefore, when you are looking for multiple items, simply break them up into references like this, bundle
|
||||
them into a list. This function then runs through the list, and returns an amalgamated list of ``Verse``
|
||||
objects. For example::
|
||||
|
||||
[(u'35', 1, 1, 1), (u'35', 2, 2, 3)]
|
||||
"""
|
||||
@ -671,8 +675,7 @@ class HTTPBible(BibleDB):
|
||||
|
||||
application = property(_get_application)
|
||||
|
||||
def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
|
||||
pre_parse_substitute=None, cleaner=None):
|
||||
def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre_parse_substitute=None):
|
||||
"""
|
||||
Gets a webpage and returns a parsed and optionally cleaned soup or None.
|
||||
|
||||
@ -683,14 +686,11 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
|
||||
An optional HTTP header to pass to the bible web server.
|
||||
|
||||
``pre_parse_regex``
|
||||
A regular expression to run on the webpage. Allows manipulation of the
|
||||
webpage before passing to BeautifulSoup for parsing.
|
||||
A regular expression to run on the webpage. Allows manipulation of the webpage before passing to BeautifulSoup
|
||||
for parsing.
|
||||
|
||||
``pre_parse_substitute``
|
||||
The text to replace any matches to the regular expression with.
|
||||
|
||||
``cleaner``
|
||||
An optional regex to use during webpage parsing.
|
||||
"""
|
||||
if not reference_url:
|
||||
return None
|
||||
@ -703,10 +703,8 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
|
||||
page_source = re.sub(pre_parse_regex, pre_parse_substitute, page_source)
|
||||
soup = None
|
||||
try:
|
||||
if cleaner:
|
||||
soup = BeautifulSoup(page_source, markupMassage=cleaner)
|
||||
else:
|
||||
soup = BeautifulSoup(page_source)
|
||||
CLEANER_REGEX.sub(u'', unicode(soup))
|
||||
except HTMLParseError:
|
||||
log.exception(u'BeautifulSoup could not parse the bible page.')
|
||||
if not soup:
|
||||
@ -715,6 +713,7 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
|
||||
Registry().get(u'application').process_events()
|
||||
return soup
|
||||
|
||||
|
||||
def send_error_message(error_type):
|
||||
"""
|
||||
Send a standard error message informing the user of an issue.
|
||||
|
@ -38,28 +38,21 @@ from csvbible import CSVBible
|
||||
from http import HTTPBible
|
||||
from opensong import OpenSongBible
|
||||
from osis import OSISBible
|
||||
# Imports that might fail.
|
||||
try:
|
||||
from openlp1 import OpenLP1Bible
|
||||
HAS_OPENLP1 = True
|
||||
except ImportError:
|
||||
HAS_OPENLP1 = False
|
||||
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BibleFormat(object):
|
||||
"""
|
||||
This is a special enumeration class that holds the various types of Bibles,
|
||||
plus a few helper functions to facilitate generic handling of Bible types
|
||||
for importing.
|
||||
This is a special enumeration class that holds the various types of Bibles.
|
||||
"""
|
||||
_format_availability = {}
|
||||
Unknown = -1
|
||||
OSIS = 0
|
||||
CSV = 1
|
||||
OpenSong = 2
|
||||
WebDownload = 3
|
||||
OpenLP1 = 4
|
||||
|
||||
@staticmethod
|
||||
def get_class(format):
|
||||
@ -77,8 +70,6 @@ class BibleFormat(object):
|
||||
return OpenSongBible
|
||||
elif format == BibleFormat.WebDownload:
|
||||
return HTTPBible
|
||||
elif format == BibleFormat.OpenLP1:
|
||||
return OpenLP1Bible
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -92,17 +83,8 @@ class BibleFormat(object):
|
||||
BibleFormat.CSV,
|
||||
BibleFormat.OpenSong,
|
||||
BibleFormat.WebDownload,
|
||||
BibleFormat.OpenLP1
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def set_availability(format, available):
|
||||
BibleFormat._format_availability[format] = available
|
||||
|
||||
@staticmethod
|
||||
def get_availability(format):
|
||||
return BibleFormat._format_availability.get(format, True)
|
||||
|
||||
|
||||
class BibleManager(object):
|
||||
"""
|
||||
@ -463,6 +445,5 @@ class BibleManager(object):
|
||||
|
||||
main_window = property(_get_main_window)
|
||||
|
||||
BibleFormat.set_availability(BibleFormat.OpenLP1, HAS_OPENLP1)
|
||||
|
||||
__all__ = [u'BibleFormat']
|
||||
|
@ -36,7 +36,7 @@ from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, Servic
|
||||
from openlp.core.lib.searchedit import SearchEdit
|
||||
from openlp.core.lib.ui import set_case_insensitive_completer, create_horizontal_adjusting_combo_box, \
|
||||
critical_error_message_box, find_and_set_in_combo_box, build_icon
|
||||
from openlp.core.utils import locale_compare
|
||||
from openlp.core.utils import get_locale_key
|
||||
from openlp.plugins.bibles.forms import BibleImportForm, EditBibleForm
|
||||
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, VerseReferenceList, get_reference_separator, \
|
||||
LanguageSelection, BibleStrings
|
||||
@ -61,8 +61,8 @@ class BibleMediaItem(MediaManagerItem):
|
||||
|
||||
def __init__(self, parent, plugin):
|
||||
self.icon_path = u'songs/song'
|
||||
self.lockIcon = build_icon(u':/bibles/bibles_search_lock.png')
|
||||
self.unlockIcon = build_icon(u':/bibles/bibles_search_unlock.png')
|
||||
self.lock_icon = build_icon(u':/bibles/bibles_search_lock.png')
|
||||
self.unlock_icon = build_icon(u':/bibles/bibles_search_unlock.png')
|
||||
MediaManagerItem.__init__(self, parent, plugin)
|
||||
# Place to store the search results for both bibles.
|
||||
self.settings = self.plugin.settings_tab
|
||||
@ -73,7 +73,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.check_search_result()
|
||||
Registry().register_function(u'bibles_load_list', self.reload_bibles)
|
||||
|
||||
def __checkSecondBible(self, bible, second_bible):
|
||||
def __check_second_bible(self, bible, second_bible):
|
||||
"""
|
||||
Check if the first item is a second bible item or not.
|
||||
"""
|
||||
@ -84,7 +84,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.displayResults(bible, second_bible)
|
||||
return
|
||||
else:
|
||||
item_second_bible = self._decodeQtObject(bitem, 'second_bible')
|
||||
item_second_bible = self._decode_qt_object(bitem, 'second_bible')
|
||||
if item_second_bible and second_bible or not item_second_bible and not second_bible:
|
||||
self.displayResults(bible, second_bible)
|
||||
elif critical_error_message_box(
|
||||
@ -95,7 +95,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.list_view.clear()
|
||||
self.displayResults(bible, second_bible)
|
||||
|
||||
def _decodeQtObject(self, bitem, key):
|
||||
def _decode_qt_object(self, bitem, key):
|
||||
reference = bitem.data(QtCore.Qt.UserRole)
|
||||
obj = reference[unicode(key)]
|
||||
return unicode(obj).strip()
|
||||
@ -159,7 +159,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
search_button_layout.setObjectName(prefix + u'search_button_layout')
|
||||
search_button_layout.addStretch()
|
||||
lockButton = QtGui.QToolButton(tab)
|
||||
lockButton.setIcon(self.unlockIcon)
|
||||
lockButton.setIcon(self.unlock_icon)
|
||||
lockButton.setCheckable(True)
|
||||
lockButton.setObjectName(prefix + u'LockButton')
|
||||
search_button_layout.addWidget(lockButton)
|
||||
@ -250,7 +250,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.quickSearchEdit.returnPressed.connect(self.onQuickSearchButton)
|
||||
self.searchTabBar.currentChanged.connect(self.onSearchTabBarCurrentChanged)
|
||||
|
||||
def onFocus(self):
|
||||
def on_focus(self):
|
||||
if self.quickTab.isVisible():
|
||||
self.quickSearchEdit.setFocus()
|
||||
else:
|
||||
@ -325,7 +325,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
# Get all bibles and sort the list.
|
||||
bibles = self.plugin.manager.get_bibles().keys()
|
||||
bibles = filter(None, bibles)
|
||||
bibles.sort(cmp=locale_compare)
|
||||
bibles.sort(key=get_locale_key)
|
||||
# Load the bibles into the combo boxes.
|
||||
self.quickVersionComboBox.addItems(bibles)
|
||||
self.quickSecondComboBox.addItems(bibles)
|
||||
@ -461,7 +461,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
for book in book_data:
|
||||
data = BiblesResourcesDB.get_book_by_id(book.book_reference_id)
|
||||
books.append(data[u'name'] + u' ')
|
||||
books.sort(cmp=locale_compare)
|
||||
books.sort(key=get_locale_key)
|
||||
set_case_insensitive_completer(books, self.quickSearchEdit)
|
||||
|
||||
def on_import_click(self):
|
||||
@ -509,9 +509,9 @@ class BibleMediaItem(MediaManagerItem):
|
||||
|
||||
def onLockButtonToggled(self, checked):
|
||||
if checked:
|
||||
self.sender().setIcon(self.lockIcon)
|
||||
self.sender().setIcon(self.lock_icon)
|
||||
else:
|
||||
self.sender().setIcon(self.unlockIcon)
|
||||
self.sender().setIcon(self.unlock_icon)
|
||||
|
||||
def onQuickStyleComboBoxChanged(self):
|
||||
self.settings.layout_style = self.quickStyleComboBox.currentIndex()
|
||||
@ -633,7 +633,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
if not self.advancedLockButton.isChecked():
|
||||
self.list_view.clear()
|
||||
if self.list_view.count() != 0:
|
||||
self.__checkSecondBible(bible, second_bible)
|
||||
self.__check_second_bible(bible, second_bible)
|
||||
elif self.search_results:
|
||||
self.displayResults(bible, second_bible)
|
||||
self.advancedSearchButton.setEnabled(True)
|
||||
@ -689,7 +689,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
if not self.quickLockButton.isChecked():
|
||||
self.list_view.clear()
|
||||
if self.list_view.count() != 0 and self.search_results:
|
||||
self.__checkSecondBible(bible, second_bible)
|
||||
self.__check_second_bible(bible, second_bible)
|
||||
elif self.search_results:
|
||||
self.displayResults(bible, second_bible)
|
||||
self.quickSearchButton.setEnabled(True)
|
||||
@ -787,19 +787,19 @@ class BibleMediaItem(MediaManagerItem):
|
||||
raw_title = []
|
||||
verses = VerseReferenceList()
|
||||
for bitem in items:
|
||||
book = self._decodeQtObject(bitem, 'book')
|
||||
chapter = int(self._decodeQtObject(bitem, 'chapter'))
|
||||
verse = int(self._decodeQtObject(bitem, 'verse'))
|
||||
bible = self._decodeQtObject(bitem, 'bible')
|
||||
version = self._decodeQtObject(bitem, 'version')
|
||||
copyright = self._decodeQtObject(bitem, 'copyright')
|
||||
permissions = self._decodeQtObject(bitem, 'permissions')
|
||||
text = self._decodeQtObject(bitem, 'text')
|
||||
second_bible = self._decodeQtObject(bitem, 'second_bible')
|
||||
second_version = self._decodeQtObject(bitem, 'second_version')
|
||||
second_copyright = self._decodeQtObject(bitem, 'second_copyright')
|
||||
second_permissions = self._decodeQtObject(bitem, 'second_permissions')
|
||||
second_text = self._decodeQtObject(bitem, 'second_text')
|
||||
book = self._decode_qt_object(bitem, 'book')
|
||||
chapter = int(self._decode_qt_object(bitem, 'chapter'))
|
||||
verse = int(self._decode_qt_object(bitem, 'verse'))
|
||||
bible = self._decode_qt_object(bitem, 'bible')
|
||||
version = self._decode_qt_object(bitem, 'version')
|
||||
copyright = self._decode_qt_object(bitem, 'copyright')
|
||||
permissions = self._decode_qt_object(bitem, 'permissions')
|
||||
text = self._decode_qt_object(bitem, 'text')
|
||||
second_bible = self._decode_qt_object(bitem, 'second_bible')
|
||||
second_version = self._decode_qt_object(bitem, 'second_version')
|
||||
second_copyright = self._decode_qt_object(bitem, 'second_copyright')
|
||||
second_permissions = self._decode_qt_object(bitem, 'second_permissions')
|
||||
second_text = self._decode_qt_object(bitem, 'second_text')
|
||||
verses.add(book, chapter, verse, version, copyright, permissions)
|
||||
verse_text = self.formatVerse(old_chapter, chapter, verse)
|
||||
if second_bible:
|
||||
@ -868,13 +868,13 @@ class BibleMediaItem(MediaManagerItem):
|
||||
"""
|
||||
verse_separator = get_reference_separator(u'sep_v_display')
|
||||
range_separator = get_reference_separator(u'sep_r_display')
|
||||
old_chapter = self._decodeQtObject(old_bitem, 'chapter')
|
||||
old_verse = self._decodeQtObject(old_bitem, 'verse')
|
||||
start_book = self._decodeQtObject(start_bitem, 'book')
|
||||
start_chapter = self._decodeQtObject(start_bitem, 'chapter')
|
||||
start_verse = self._decodeQtObject(start_bitem, 'verse')
|
||||
start_bible = self._decodeQtObject(start_bitem, 'bible')
|
||||
start_second_bible = self._decodeQtObject(start_bitem, 'second_bible')
|
||||
old_chapter = self._decode_qt_object(old_bitem, 'chapter')
|
||||
old_verse = self._decode_qt_object(old_bitem, 'verse')
|
||||
start_book = self._decode_qt_object(start_bitem, 'book')
|
||||
start_chapter = self._decode_qt_object(start_bitem, 'chapter')
|
||||
start_verse = self._decode_qt_object(start_bitem, 'verse')
|
||||
start_bible = self._decode_qt_object(start_bitem, 'bible')
|
||||
start_second_bible = self._decode_qt_object(start_bitem, 'second_bible')
|
||||
if start_second_bible:
|
||||
bibles = u'%s, %s' % (start_bible, start_second_bible)
|
||||
else:
|
||||
@ -902,16 +902,16 @@ class BibleMediaItem(MediaManagerItem):
|
||||
The item we were previously dealing with.
|
||||
"""
|
||||
# Get all the necessary meta data.
|
||||
book = self._decodeQtObject(bitem, 'book')
|
||||
chapter = int(self._decodeQtObject(bitem, 'chapter'))
|
||||
verse = int(self._decodeQtObject(bitem, 'verse'))
|
||||
bible = self._decodeQtObject(bitem, 'bible')
|
||||
second_bible = self._decodeQtObject(bitem, 'second_bible')
|
||||
old_book = self._decodeQtObject(old_bitem, 'book')
|
||||
old_chapter = int(self._decodeQtObject(old_bitem, 'chapter'))
|
||||
old_verse = int(self._decodeQtObject(old_bitem, 'verse'))
|
||||
old_bible = self._decodeQtObject(old_bitem, 'bible')
|
||||
old_second_bible = self._decodeQtObject(old_bitem, 'second_bible')
|
||||
book = self._decode_qt_object(bitem, 'book')
|
||||
chapter = int(self._decode_qt_object(bitem, 'chapter'))
|
||||
verse = int(self._decode_qt_object(bitem, 'verse'))
|
||||
bible = self._decode_qt_object(bitem, 'bible')
|
||||
second_bible = self._decode_qt_object(bitem, 'second_bible')
|
||||
old_book = self._decode_qt_object(old_bitem, 'book')
|
||||
old_chapter = int(self._decode_qt_object(old_bitem, 'chapter'))
|
||||
old_verse = int(self._decode_qt_object(old_bitem, 'verse'))
|
||||
old_bible = self._decode_qt_object(old_bitem, 'bible')
|
||||
old_second_bible = self._decode_qt_object(old_bitem, 'second_bible')
|
||||
if old_bible != bible or old_second_bible != second_bible or old_book != book:
|
||||
# The bible, second bible or book has changed.
|
||||
return True
|
||||
|
@ -1,113 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import sqlite
|
||||
import sys
|
||||
|
||||
from openlp.core.ui.wizard import WizardStrings
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class OpenLP1Bible(BibleDB):
|
||||
"""
|
||||
This class provides the OpenLPv1 bible importer.
|
||||
"""
|
||||
def __init__(self, parent, **kwargs):
|
||||
"""
|
||||
Constructor.
|
||||
"""
|
||||
log.debug(self.__class__.__name__)
|
||||
BibleDB.__init__(self, parent, **kwargs)
|
||||
self.filename = kwargs[u'filename']
|
||||
|
||||
def do_import(self, bible_name=None):
|
||||
"""
|
||||
Imports an openlp.org v1 bible.
|
||||
"""
|
||||
connection = None
|
||||
cursor = None
|
||||
try:
|
||||
connection = sqlite.connect(self.filename.encode(sys.getfilesystemencoding()))
|
||||
cursor = connection.cursor()
|
||||
except sqlite.DatabaseError:
|
||||
log.exception(u'File "%s" is encrypted or not a sqlite database, '
|
||||
'therefore not an openlp.org 1.x database either' % self.filename)
|
||||
# Please add an user error here!
|
||||
# This file is not an openlp.org 1.x bible database.
|
||||
return False
|
||||
#Create the bible language
|
||||
language_id = self.get_language(bible_name)
|
||||
if not language_id:
|
||||
log.exception(u'Importing books from "%s" failed' % self.filename)
|
||||
return False
|
||||
# Create all books.
|
||||
try:
|
||||
cursor.execute(u'SELECT id, testament_id, name, abbreviation FROM book')
|
||||
except sqlite.DatabaseError as error:
|
||||
log.exception(u'DatabaseError: %s' % error)
|
||||
# Please add an user error here!
|
||||
# This file is not an openlp.org 1.x bible database.
|
||||
return False
|
||||
books = cursor.fetchall()
|
||||
self.wizard.progress_bar.setMaximum(len(books) + 1)
|
||||
for book in books:
|
||||
if self.stop_import_flag:
|
||||
connection.close()
|
||||
return False
|
||||
book_id = int(book[0])
|
||||
testament_id = int(book[1])
|
||||
name = unicode(book[2], u'cp1252')
|
||||
abbreviation = unicode(book[3], u'cp1252')
|
||||
book_ref_id = self.get_book_ref_id_by_name(name, len(books),
|
||||
language_id)
|
||||
if not book_ref_id:
|
||||
log.exception(u'Importing books from "%s" failed' % self.filename)
|
||||
return False
|
||||
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
|
||||
db_book = self.create_book(name, book_ref_id, book_details[u'testament_id'])
|
||||
# Update the progess bar.
|
||||
self.wizard.increment_progress_bar(WizardStrings.ImportingType % name)
|
||||
# Import the verses for this book.
|
||||
cursor.execute(u'SELECT chapter, verse, text || \'\' AS text FROM '
|
||||
'verse WHERE book_id=%s' % book_id)
|
||||
verses = cursor.fetchall()
|
||||
for verse in verses:
|
||||
if self.stop_import_flag:
|
||||
connection.close()
|
||||
return False
|
||||
chapter = int(verse[0])
|
||||
verse_number = int(verse[1])
|
||||
text = unicode(verse[2], u'cp1252')
|
||||
self.create_verse(db_book.id, chapter, verse_number, text)
|
||||
self.application.process_events()
|
||||
self.session.commit()
|
||||
connection.close()
|
||||
return True
|
@ -34,16 +34,18 @@ from openlp.core.lib import translate
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OpenSongBible(BibleDB):
|
||||
"""
|
||||
OpenSong Bible format importer class.
|
||||
"""
|
||||
def __init__(self, parent, **kwargs):
|
||||
"""
|
||||
Constructor to create and set up an instance of the OpenSongBible
|
||||
class. This class is used to import Bibles from OpenSong's XML format.
|
||||
Constructor to create and set up an instance of the OpenSongBible class. This class is used to import Bibles
|
||||
from OpenSong's XML format.
|
||||
"""
|
||||
log.debug(self.__class__.__name__)
|
||||
BibleDB.__init__(self, parent, **kwargs)
|
||||
@ -75,9 +77,8 @@ class OpenSongBible(BibleDB):
|
||||
file = None
|
||||
success = True
|
||||
try:
|
||||
# NOTE: We don't need to do any of the normal encoding detection
|
||||
# here, because lxml does it's own encoding detection, and the two
|
||||
# mechanisms together interfere with each other.
|
||||
# NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding
|
||||
# detection, and the two mechanisms together interfere with each other.
|
||||
file = open(self.filename, u'r')
|
||||
opensong = objectify.parse(file)
|
||||
bible = opensong.getroot()
|
||||
@ -116,16 +117,11 @@ class OpenSongBible(BibleDB):
|
||||
if len(verse_parts) > 1:
|
||||
number = int(verse_parts[0])
|
||||
except TypeError:
|
||||
log.warn(u'Illegal verse number: %s',
|
||||
unicode(verse.attrib[u'n']))
|
||||
log.warn(u'Illegal verse number: %s', unicode(verse.attrib[u'n']))
|
||||
verse_number = number
|
||||
else:
|
||||
verse_number += 1
|
||||
self.create_verse(
|
||||
db_book.id,
|
||||
chapter_number,
|
||||
verse_number,
|
||||
self.get_text(verse))
|
||||
self.create_verse(db_book.id, chapter_number, verse_number, self.get_text(verse))
|
||||
self.wizard.increment_progress_bar(translate('BiblesPlugin.Opensong', 'Importing %s %s...',
|
||||
'Importing <book name> <chapter>...')) % (db_book.name, chapter_number)
|
||||
self.session.commit()
|
||||
|
@ -39,9 +39,11 @@ from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def replacement(match):
|
||||
return match.group(2).upper()
|
||||
|
||||
|
||||
class OSISBible(BibleDB):
|
||||
"""
|
||||
`OSIS <http://www.bibletechnologies.net/>`_ Bible format importer class.
|
||||
@ -53,8 +55,7 @@ class OSISBible(BibleDB):
|
||||
BibleDB.__init__(self, parent, **kwargs)
|
||||
self.filename = kwargs[u'filename']
|
||||
self.language_regex = re.compile(r'<language.*>(.*?)</language>')
|
||||
self.verse_regex = re.compile(
|
||||
r'<verse osisID="([a-zA-Z0-9 ]*).([0-9]*).([0-9]*)">(.*?)</verse>')
|
||||
self.verse_regex = re.compile(r'<verse osisID="([a-zA-Z0-9 ]*).([0-9]*).([0-9]*)">(.*?)</verse>')
|
||||
self.note_regex = re.compile(r'<note(.*?)>(.*?)</note>')
|
||||
self.title_regex = re.compile(r'<title(.*?)>(.*?)</title>')
|
||||
self.milestone_regex = re.compile(r'<milestone(.*?)/>')
|
||||
@ -68,8 +69,7 @@ class OSISBible(BibleDB):
|
||||
self.q1_regex = re.compile(r'<q(.*?)level="1"(.*?)>')
|
||||
self.q2_regex = re.compile(r'<q(.*?)level="2"(.*?)>')
|
||||
self.trans_regex = re.compile(r'<transChange(.*?)>(.*?)</transChange>')
|
||||
self.divine_name_regex = re.compile(
|
||||
r'<divineName(.*?)>(.*?)</divineName>')
|
||||
self.divine_name_regex = re.compile(r'<divineName(.*?)>(.*?)</divineName>')
|
||||
self.spaces_regex = re.compile(r'([ ]{2,})')
|
||||
filepath = os.path.join(
|
||||
AppLocation.get_directory(AppLocation.PluginsDir), u'bibles', u'resources', u'osisbooks.csv')
|
||||
@ -158,10 +158,8 @@ class OSISBible(BibleDB):
|
||||
self.wizard.increment_progress_bar(translate('BiblesPlugin.OsisImport', 'Importing %s %s...',
|
||||
'Importing <book name> <chapter>...') % (book_details[u'name'], chapter))
|
||||
last_chapter = chapter
|
||||
# All of this rigmarol below is because the mod2osis
|
||||
# tool from the Sword library embeds XML in the OSIS
|
||||
# but neglects to enclose the verse text (with XML) in
|
||||
# <[CDATA[ ]]> tags.
|
||||
# All of this rigmarol below is because the mod2osis tool from the Sword library embeds XML in the
|
||||
# OSIS but neglects to enclose the verse text (with XML) in <[CDATA[ ]]> tags.
|
||||
verse_text = self.note_regex.sub(u'', verse_text)
|
||||
verse_text = self.title_regex.sub(u'', verse_text)
|
||||
verse_text = self.milestone_regex.sub(u'', verse_text)
|
||||
|
@ -27,8 +27,7 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`upgrade` module provides a way for the database and schema that is the
|
||||
backend for the Bibles plugin
|
||||
The :mod:`upgrade` module provides a way for the database and schema that is the backend for the Bibles plugin.
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
@ -29,8 +29,8 @@
|
||||
|
||||
class VerseReferenceList(object):
|
||||
"""
|
||||
The VerseReferenceList class encapsulates a list of verse references, but
|
||||
maintains the order in which they were added.
|
||||
The VerseReferenceList class encapsulates a list of verse references, but maintains the order in which they were
|
||||
added.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -257,6 +257,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
# We must have at least one slide.
|
||||
if self.slide_list_view.count() == 0:
|
||||
critical_error_message_box(message=translate('CustomPlugin.EditCustomForm',
|
||||
'You need to add at least one slide'))
|
||||
'You need to add at least one slide.'))
|
||||
return False
|
||||
return True
|
||||
|
@ -35,7 +35,7 @@ from sqlalchemy import Column, Table, types
|
||||
from sqlalchemy.orm import mapper
|
||||
|
||||
from openlp.core.lib.db import BaseModel, init_db
|
||||
from openlp.core.utils import locale_compare
|
||||
from openlp.core.utils import get_locale_key
|
||||
|
||||
class CustomSlide(BaseModel):
|
||||
"""
|
||||
@ -44,11 +44,10 @@ class CustomSlide(BaseModel):
|
||||
# By default sort the customs by its title considering language specific
|
||||
# characters.
|
||||
def __lt__(self, other):
|
||||
r = locale_compare(self.title, other.title)
|
||||
return True if r < 0 else False
|
||||
return get_locale_key(self.title) < get_locale_key(other.title)
|
||||
|
||||
def __eq__(self, other):
|
||||
return 0 == locale_compare(self.title, other.title)
|
||||
return get_locale_key(self.title) == get_locale_key(other.title)
|
||||
|
||||
|
||||
def init_schema(url):
|
||||
|
@ -40,6 +40,7 @@ from openlp.plugins.custom.lib.db import CustomSlide
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CustomSearch(object):
|
||||
"""
|
||||
An enumeration for custom search methods.
|
||||
@ -63,14 +64,14 @@ class CustomMediaItem(MediaManagerItem):
|
||||
self.has_search = True
|
||||
# Holds information about whether the edit is remotely triggered and
|
||||
# which Custom is required.
|
||||
self.remoteCustom = -1
|
||||
self.remote_custom = -1
|
||||
self.manager = plugin.manager
|
||||
|
||||
def add_end_header_bar(self):
|
||||
self.toolbar.addSeparator()
|
||||
self.add_search_to_toolbar()
|
||||
# Signals and slots
|
||||
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'cleared()'), self.onClearTextButtonClick)
|
||||
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'cleared()'), self.on_clear_text_button_click)
|
||||
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL(u'searchTypeChanged(int)'),
|
||||
self.on_search_text_button_clicked)
|
||||
Registry().register_function(u'custom_load_list', self.load_list)
|
||||
@ -119,14 +120,13 @@ class CustomMediaItem(MediaManagerItem):
|
||||
def on_new_click(self):
|
||||
self.edit_custom_form.load_custom(0)
|
||||
self.edit_custom_form.exec_()
|
||||
self.onClearTextButtonClick()
|
||||
self.on_clear_text_button_click()
|
||||
self.on_selection_change()
|
||||
|
||||
def onRemoteEdit(self, custom_id, preview=False):
|
||||
def on_remote_edit(self, custom_id, preview=False):
|
||||
"""
|
||||
Called by ServiceManager or SlideController by event passing
|
||||
the custom Id in the payload along with an indicator to say which
|
||||
type of display is required.
|
||||
Called by ServiceManager or SlideController by event passing the custom Id in the payload along with an
|
||||
indicator to say which type of display is required.
|
||||
"""
|
||||
custom_id = int(custom_id)
|
||||
valid = self.manager.get_object(CustomSlide, custom_id)
|
||||
@ -134,12 +134,12 @@ class CustomMediaItem(MediaManagerItem):
|
||||
self.edit_custom_form.load_custom(custom_id, preview)
|
||||
if self.edit_custom_form.exec_() == QtGui.QDialog.Accepted:
|
||||
self.remote_triggered = True
|
||||
self.remoteCustom = custom_id
|
||||
self.remote_custom = custom_id
|
||||
self.auto_select_id = -1
|
||||
self.on_search_text_button_clicked()
|
||||
item = self.build_service_item(remote=True)
|
||||
self.remote_triggered = None
|
||||
self.remoteCustom = 1
|
||||
self.remote_custom = 1
|
||||
if item:
|
||||
return item
|
||||
return None
|
||||
@ -172,13 +172,12 @@ class CustomMediaItem(MediaManagerItem):
|
||||
return
|
||||
row_list = [item.row() for item in self.list_view.selectedIndexes()]
|
||||
row_list.sort(reverse=True)
|
||||
id_list = [(item.data(QtCore.Qt.UserRole))
|
||||
for item in self.list_view.selectedIndexes()]
|
||||
id_list = [(item.data(QtCore.Qt.UserRole)) for item in self.list_view.selectedIndexes()]
|
||||
for id in id_list:
|
||||
self.plugin.manager.delete_object(CustomSlide, id)
|
||||
self.on_search_text_button_clicked()
|
||||
|
||||
def onFocus(self):
|
||||
def on_focus(self):
|
||||
self.search_text_edit.setFocus()
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xmlVersion=False,
|
||||
@ -186,20 +185,20 @@ class CustomMediaItem(MediaManagerItem):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
"""
|
||||
item_id = self._get_id_of_item_to_generate(item, self.remoteCustom)
|
||||
item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
service_item.add_capability(ItemCapabilities.CanSoftBreak)
|
||||
service_item.add_capability(ItemCapabilities.OnLoadUpdate)
|
||||
customSlide = self.plugin.manager.get_object(CustomSlide, item_id)
|
||||
title = customSlide.title
|
||||
credit = customSlide.credits
|
||||
custom_slide = self.plugin.manager.get_object(CustomSlide, item_id)
|
||||
title = custom_slide.title
|
||||
credit = custom_slide.credits
|
||||
service_item.edit_id = item_id
|
||||
theme = customSlide.theme_name
|
||||
theme = custom_slide.theme_name
|
||||
if theme:
|
||||
service_item.theme = theme
|
||||
custom_xml = CustomXMLParser(customSlide.text)
|
||||
custom_xml = CustomXMLParser(custom_slide.text)
|
||||
verse_list = custom_xml.get_verses()
|
||||
raw_slides = [verse[1] for verse in verse_list]
|
||||
service_item.title = title
|
||||
@ -216,7 +215,6 @@ class CustomMediaItem(MediaManagerItem):
|
||||
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
|
||||
# Reload the list considering the new search type.
|
||||
search_keywords = self.search_text_edit.displayText()
|
||||
search_results = []
|
||||
search_type = self.search_text_edit.current_search_type()
|
||||
if search_type == CustomSearch.Titles:
|
||||
log.debug(u'Titles Search')
|
||||
@ -234,15 +232,14 @@ class CustomMediaItem(MediaManagerItem):
|
||||
|
||||
def on_search_text_edit_changed(self, text):
|
||||
"""
|
||||
If search as type enabled invoke the search on each key press.
|
||||
If the Title is being searched do not start until 2 characters
|
||||
have been entered.
|
||||
If search as type enabled invoke the search on each key press. If the Title is being searched do not start until
|
||||
2 characters have been entered.
|
||||
"""
|
||||
search_length = 2
|
||||
if len(text) > search_length:
|
||||
self.on_search_text_button_clicked()
|
||||
elif not text:
|
||||
self.onClearTextButtonClick()
|
||||
self.on_clear_text_button_click()
|
||||
|
||||
def service_load(self, item):
|
||||
"""
|
||||
@ -255,7 +252,8 @@ class CustomMediaItem(MediaManagerItem):
|
||||
and_(CustomSlide.title == item.title, CustomSlide.theme_name == item.theme,
|
||||
CustomSlide.credits == item.raw_footer[0][len(item.title) + 1:]))
|
||||
if custom:
|
||||
self.service_manager.service_item_update(custom.id, item.unique_identifier)
|
||||
item.edit_id = custom.id
|
||||
return item
|
||||
else:
|
||||
if self.add_custom_from_service:
|
||||
self.create_from_service_item(item)
|
||||
@ -284,10 +282,8 @@ class CustomMediaItem(MediaManagerItem):
|
||||
custom.text = unicode(custom_xml.extract_xml(), u'utf-8')
|
||||
self.plugin.manager.save_object(custom)
|
||||
self.on_search_text_button_clicked()
|
||||
if item.name.lower() == u'custom':
|
||||
Registry().execute(u'service_item_update', u'%s:%s:%s' % (custom.id, item.unique_identifier, False))
|
||||
|
||||
def onClearTextButtonClick(self):
|
||||
def on_clear_text_button_click(self):
|
||||
"""
|
||||
Clear the search text.
|
||||
"""
|
||||
|
@ -27,6 +27,6 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`images` module provides the Images plugin. The Images plugin
|
||||
provides the facility to display images from OpenLP.
|
||||
The :mod:`images` module provides the Images plugin. The Images plugin provides the facility to display images from
|
||||
OpenLP.
|
||||
"""
|
||||
|
@ -27,20 +27,16 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Forms in OpenLP are made up of two classes. One class holds all the graphical
|
||||
elements, like buttons and lists, and the other class holds all the functional
|
||||
code, like slots and loading and saving.
|
||||
Forms in OpenLP are made up of two classes. One class holds all the graphical elements, like buttons and lists, and the
|
||||
other class holds all the functional code, like slots and loading and saving.
|
||||
|
||||
The first class, commonly known as the **Dialog** class, is typically named
|
||||
``Ui_<name>Dialog``. It is a slightly modified version of the class that the
|
||||
``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
|
||||
converting most strings from "" to u'' and using OpenLP's ``translate()``
|
||||
function for translating strings.
|
||||
The first class, commonly known as the **Dialog** class, is typically named ``Ui_<name>Dialog``. It is a slightly
|
||||
modified version of the class that the ``pyuic4`` command produces from Qt4's .ui file. Typical modifications will be
|
||||
converting most strings from "" to u'' and using OpenLP's ``translate()`` function for translating strings.
|
||||
|
||||
The second class, commonly known as the **Form** class, is typically named
|
||||
``<name>Form``. This class is the one which is instantiated and used. It uses
|
||||
dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class
|
||||
mentioned above, like so::
|
||||
The second class, commonly known as the **Form** class, is typically named ``<name>Form``. This class is the one which
|
||||
is instantiated and used. It uses dual inheritance to inherit from (usually) QtGui.QDialog and the Ui class mentioned
|
||||
above, like so::
|
||||
|
||||
class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
|
||||
|
||||
@ -48,9 +44,8 @@ mentioned above, like so::
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
|
||||
them separate from the functionality, so that it is easier to recreate the GUI
|
||||
from the .ui files later if necessary.
|
||||
This allows OpenLP to use ``self.object`` for all the GUI elements while keeping them separate from the functionality,
|
||||
so that it is easier to recreate the GUI from the .ui files later if necessary.
|
||||
"""
|
||||
|
||||
from addgroupform import AddGroupForm
|
||||
|
@ -47,16 +47,16 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog):
|
||||
|
||||
def exec_(self, clear=True, show_top_level_group=False, selected_group=None):
|
||||
"""
|
||||
Show the form
|
||||
Show the form.
|
||||
|
||||
``clear``
|
||||
Set to False if the text input box should not be cleared when showing the dialog (default: True)
|
||||
Set to False if the text input box should not be cleared when showing the dialog (default: True).
|
||||
|
||||
``show_top_level_group``
|
||||
Set to True when "-- Top level group --" should be showed as first item (default: False)
|
||||
Set to True when "-- Top level group --" should be showed as first item (default: False).
|
||||
|
||||
``selected_group``
|
||||
The ID of the group that should be selected by default when showing the dialog
|
||||
The ID of the group that should be selected by default when showing the dialog.
|
||||
"""
|
||||
if clear:
|
||||
self.name_edit.clear()
|
||||
@ -72,7 +72,7 @@ class AddGroupForm(QtGui.QDialog, Ui_AddGroupDialog):
|
||||
|
||||
def accept(self):
|
||||
"""
|
||||
Override the accept() method from QDialog to make sure something is entered in the text input box
|
||||
Override the accept() method from QDialog to make sure something is entered in the text input box.
|
||||
"""
|
||||
if not self.name_edit.text():
|
||||
critical_error_message_box(message=translate('ImagePlugin.AddGroupForm',
|
||||
|
@ -48,10 +48,11 @@ class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog):
|
||||
Show the form
|
||||
|
||||
``selected_group``
|
||||
The ID of the group that should be selected by default when showing the dialog
|
||||
The ID of the group that should be selected by default when showing the dialog.
|
||||
"""
|
||||
self.new_group_edit.clear()
|
||||
if selected_group is not None:
|
||||
for i in range(self.group_combobox.count()):
|
||||
if self.group_combobox.itemData(i) == selected_group:
|
||||
self.group_combobox.setCurrentIndex(i)
|
||||
for index in range(self.group_combobox.count()):
|
||||
if self.group_combobox.itemData(index) == selected_group:
|
||||
self.group_combobox.setCurrentIndex(index)
|
||||
return QtGui.QDialog.exec_(self)
|
||||
|
@ -70,10 +70,10 @@ class ImagePlugin(Plugin):
|
||||
|
||||
def app_startup(self):
|
||||
"""
|
||||
Perform tasks on application startup
|
||||
Perform tasks on application startup.
|
||||
"""
|
||||
Plugin.app_startup(self)
|
||||
# Convert old settings-based image list to the database
|
||||
# Convert old settings-based image list to the database.
|
||||
files_from_config = Settings().get_files_from_config(self)
|
||||
if files_from_config:
|
||||
log.debug(u'Importing images list from old config: %s' % files_from_config)
|
||||
@ -93,7 +93,7 @@ class ImagePlugin(Plugin):
|
||||
|
||||
def set_plugin_text_strings(self):
|
||||
"""
|
||||
Called to define all translatable texts of the plugin
|
||||
Called to define all translatable texts of the plugin.
|
||||
"""
|
||||
## Name PluginList ##
|
||||
self.text_strings[StringContent.Name] = {
|
||||
@ -117,8 +117,8 @@ class ImagePlugin(Plugin):
|
||||
|
||||
def config_update(self):
|
||||
"""
|
||||
Triggered by saving and changing the image border. Sets the images in image manager to require updates.
|
||||
Actual update is triggered by the last part of saving the config.
|
||||
Triggered by saving and changing the image border. Sets the images in image manager to require updates. Actual
|
||||
update is triggered by the last part of saving the config.
|
||||
"""
|
||||
log.info(u'Images config_update')
|
||||
background = QtGui.QColor(Settings().value(self.settings_section + u'/background color'))
|
||||
|
@ -27,7 +27,7 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`db` module provides the database and schema that is the backend for the Images plugin
|
||||
The :mod:`db` module provides the database and schema that is the backend for the Images plugin.
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Table, types
|
||||
@ -38,14 +38,14 @@ from openlp.core.lib.db import BaseModel, init_db
|
||||
|
||||
class ImageGroups(BaseModel):
|
||||
"""
|
||||
ImageGroups model
|
||||
ImageGroups model.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ImageFilenames(BaseModel):
|
||||
"""
|
||||
ImageFilenames model
|
||||
ImageFilenames model.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -32,7 +32,6 @@ from PyQt4 import QtGui
|
||||
from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate
|
||||
|
||||
|
||||
|
||||
class ImageTab(SettingsTab):
|
||||
"""
|
||||
ImageTab is the images settings tab in the settings dialog.
|
||||
|
@ -36,10 +36,11 @@ from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, Servic
|
||||
StringContent, TreeWidgetWithDnD, UiStrings, build_icon, check_directory_exists, check_item_selected, \
|
||||
create_thumb, translate, validate_thumb
|
||||
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
|
||||
from openlp.core.utils import AppLocation, delete_file, locale_compare, get_images_filter
|
||||
from openlp.core.utils import AppLocation, delete_file, get_locale_key, get_images_filter
|
||||
from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm
|
||||
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -60,24 +61,23 @@ class ImageMediaItem(MediaManagerItem):
|
||||
self.fill_groups_combobox(self.choose_group_form.group_combobox)
|
||||
self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
|
||||
Registry().register_function(u'live_theme_changed', self.live_theme_changed)
|
||||
# Allow DnD from the desktop
|
||||
# Allow DnD from the desktop.
|
||||
self.list_view.activateDnD()
|
||||
|
||||
def retranslateUi(self):
|
||||
self.on_new_prompt = translate('ImagePlugin.MediaItem',
|
||||
'Select Image(s)')
|
||||
self.on_new_prompt = translate('ImagePlugin.MediaItem', 'Select Image(s)')
|
||||
file_formats = get_images_filter()
|
||||
self.on_new_file_masks = u'%s;;%s (*.*) (*)' % (file_formats, UiStrings().AllFiles)
|
||||
self.addGroupAction.setText(UiStrings().AddGroup)
|
||||
self.addGroupAction.setToolTip(UiStrings().AddGroup)
|
||||
self.replaceAction.setText(UiStrings().ReplaceBG)
|
||||
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG)
|
||||
self.resetAction.setText(UiStrings().ResetBG)
|
||||
self.resetAction.setToolTip(UiStrings().ResetLiveBG)
|
||||
self.replace_action.setText(UiStrings().ReplaceBG)
|
||||
self.replace_action.setToolTip(UiStrings().ReplaceLiveBG)
|
||||
self.reset_action.setText(UiStrings().ResetBG)
|
||||
self.reset_action.setToolTip(UiStrings().ResetLiveBG)
|
||||
|
||||
def required_icons(self):
|
||||
"""
|
||||
Set which icons the media manager tab should show
|
||||
Set which icons the media manager tab should show.
|
||||
"""
|
||||
MediaManagerItem.required_icons(self)
|
||||
self.has_file_icon = True
|
||||
@ -94,13 +94,13 @@ class ImageMediaItem(MediaManagerItem):
|
||||
self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settings_section), u'thumbnails')
|
||||
check_directory_exists(self.servicePath)
|
||||
# Load images from the database
|
||||
self.loadFullList(
|
||||
self.load_full_list(
|
||||
self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename), initial_load=True)
|
||||
|
||||
def add_list_view_to_toolbar(self):
|
||||
"""
|
||||
Creates the main widget for listing items the media item is tracking.
|
||||
This method overloads MediaManagerItem.add_list_view_to_toolbar
|
||||
Creates the main widget for listing items the media item is tracking. This method overloads
|
||||
MediaManagerItem.add_list_view_to_toolbar.
|
||||
"""
|
||||
# Add the List widget
|
||||
self.list_view = TreeWidgetWithDnD(self, self.plugin.name)
|
||||
@ -155,44 +155,41 @@ class ImageMediaItem(MediaManagerItem):
|
||||
self.list_view.doubleClicked.connect(self.on_double_clicked)
|
||||
self.list_view.itemSelectionChanged.connect(self.on_selection_change)
|
||||
self.list_view.customContextMenuRequested.connect(self.context_menu)
|
||||
self.list_view.addAction(self.replaceAction)
|
||||
self.list_view.addAction(self.replace_action)
|
||||
|
||||
def add_custom_context_actions(self):
|
||||
"""
|
||||
Add custom actions to the context menu
|
||||
Add custom actions to the context menu.
|
||||
"""
|
||||
create_widget_action(self.list_view, separator=True)
|
||||
create_widget_action(self.list_view,
|
||||
text=UiStrings().AddGroup,
|
||||
icon=u':/images/image_new_group.png',
|
||||
triggers=self.onAddGroupClick)
|
||||
text=UiStrings().AddGroup, icon=u':/images/image_new_group.png', triggers=self.on_add_group_click)
|
||||
create_widget_action(self.list_view,
|
||||
text=self.plugin.get_string(StringContent.Load)[u'tooltip'],
|
||||
icon=u':/general/general_open.png',
|
||||
triggers=self.on_file_click)
|
||||
icon=u':/general/general_open.png', triggers=self.on_file_click)
|
||||
|
||||
def add_start_header_bar(self):
|
||||
"""
|
||||
Add custom buttons to the start of the toolbar
|
||||
Add custom buttons to the start of the toolbar.
|
||||
"""
|
||||
self.addGroupAction = self.toolbar.add_toolbar_action(u'addGroupAction',
|
||||
icon=u':/images/image_new_group.png', triggers=self.onAddGroupClick)
|
||||
icon=u':/images/image_new_group.png', triggers=self.on_add_group_click)
|
||||
|
||||
def add_end_header_bar(self):
|
||||
"""
|
||||
Add custom buttons to the end of the toolbar
|
||||
"""
|
||||
self.replaceAction = self.toolbar.add_toolbar_action(u'replaceAction',
|
||||
icon=u':/slides/slide_blank.png', triggers=self.onReplaceClick)
|
||||
self.resetAction = self.toolbar.add_toolbar_action(u'resetAction',
|
||||
icon=u':/system/system_close.png', visible=False, triggers=self.onResetClick)
|
||||
self.replace_action = self.toolbar.add_toolbar_action(u'replace_action',
|
||||
icon=u':/slides/slide_blank.png', triggers=self.on_replace_click)
|
||||
self.reset_action = self.toolbar.add_toolbar_action(u'reset_action',
|
||||
icon=u':/system/system_close.png', visible=False, triggers=self.on_reset_click)
|
||||
|
||||
def recursively_delete_group(self, image_group):
|
||||
"""
|
||||
Recursively deletes a group and all groups and images in it
|
||||
Recursively deletes a group and all groups and images in it.
|
||||
|
||||
``image_group``
|
||||
The ImageGroups instance of the group that will be deleted
|
||||
The ImageGroups instance of the group that will be deleted.
|
||||
"""
|
||||
images = self.manager.get_all_objects(ImageFilenames, ImageFilenames.group_id == image_group.id)
|
||||
for image in images:
|
||||
@ -205,7 +202,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
|
||||
def on_delete_click(self):
|
||||
"""
|
||||
Remove an image item from the list
|
||||
Remove an image item from the list.
|
||||
"""
|
||||
# Turn off auto preview triggers.
|
||||
self.list_view.blockSignals(True)
|
||||
@ -246,16 +243,16 @@ class ImageMediaItem(MediaManagerItem):
|
||||
|
||||
def add_sub_groups(self, group_list, parent_group_id):
|
||||
"""
|
||||
Recursively add subgroups to the given parent group in a QTreeWidget
|
||||
Recursively add subgroups to the given parent group in a QTreeWidget.
|
||||
|
||||
``group_list``
|
||||
The List object that contains all QTreeWidgetItems
|
||||
The List object that contains all QTreeWidgetItems.
|
||||
|
||||
``parent_group_id``
|
||||
The ID of the group that will be added recursively
|
||||
The ID of the group that will be added recursively.
|
||||
"""
|
||||
image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parent_group_id)
|
||||
image_groups.sort(cmp=locale_compare, key=lambda group_object: group_object.group_name)
|
||||
image_groups.sort(key=lambda group_object: get_locale_key(group_object.group_name))
|
||||
folder_icon = build_icon(u':/images/image_group.png')
|
||||
for image_group in image_groups:
|
||||
group = QtGui.QTreeWidgetItem()
|
||||
@ -271,35 +268,35 @@ class ImageMediaItem(MediaManagerItem):
|
||||
|
||||
def fill_groups_combobox(self, combobox, parent_group_id=0, prefix=''):
|
||||
"""
|
||||
Recursively add groups to the combobox in the 'Add group' dialog
|
||||
Recursively add groups to the combobox in the 'Add group' dialog.
|
||||
|
||||
``combobox``
|
||||
The QComboBox to add the options to
|
||||
The QComboBox to add the options to.
|
||||
|
||||
``parent_group_id``
|
||||
The ID of the group that will be added
|
||||
The ID of the group that will be added.
|
||||
|
||||
``prefix``
|
||||
A string containing the prefix that will be added in front of the groupname for each level of the tree
|
||||
A string containing the prefix that will be added in front of the groupname for each level of the tree.
|
||||
"""
|
||||
if parent_group_id == 0:
|
||||
combobox.clear()
|
||||
combobox.top_level_group_added = False
|
||||
image_groups = self.manager.get_all_objects(ImageGroups, ImageGroups.parent_id == parent_group_id)
|
||||
image_groups.sort(cmp=locale_compare, key=lambda group_object: group_object.group_name)
|
||||
image_groups.sort(key=lambda group_object: get_locale_key(group_object.group_name))
|
||||
for image_group in image_groups:
|
||||
combobox.addItem(prefix + image_group.group_name, image_group.id)
|
||||
self.fill_groups_combobox(combobox, image_group.id, prefix + ' ')
|
||||
|
||||
def expand_group(self, group_id, root_item=None):
|
||||
"""
|
||||
Expand groups in the widget recursively
|
||||
Expand groups in the widget recursively.
|
||||
|
||||
``group_id``
|
||||
The ID of the group that will be expanded
|
||||
The ID of the group that will be expanded.
|
||||
|
||||
``root_item``
|
||||
This option is only used for recursion purposes
|
||||
This option is only used for recursion purposes.
|
||||
"""
|
||||
return_value = False
|
||||
if root_item is None:
|
||||
@ -314,31 +311,31 @@ class ImageMediaItem(MediaManagerItem):
|
||||
return True
|
||||
return return_value
|
||||
|
||||
def loadFullList(self, images, initial_load=False, open_group=None):
|
||||
def load_full_list(self, images, initial_load=False, open_group=None):
|
||||
"""
|
||||
Replace the list of images and groups in the interface.
|
||||
|
||||
``images``
|
||||
A List of ImageFilenames objects that will be used to reload the mediamanager list
|
||||
A List of ImageFilenames objects that will be used to reload the mediamanager list.
|
||||
|
||||
``initial_load``
|
||||
When set to False, the busy cursor and progressbar will be shown while loading images
|
||||
When set to False, the busy cursor and progressbar will be shown while loading images.
|
||||
|
||||
``open_group``
|
||||
ImageGroups object of the group that must be expanded after reloading the list in the interface
|
||||
ImageGroups object of the group that must be expanded after reloading the list in the interface.
|
||||
"""
|
||||
if not initial_load:
|
||||
self.application.set_busy_cursor()
|
||||
self.main_window.display_progress_bar(len(images))
|
||||
self.list_view.clear()
|
||||
# Load the list of groups and add them to the treeView
|
||||
# Load the list of groups and add them to the treeView.
|
||||
group_items = {}
|
||||
self.add_sub_groups(group_items, parent_group_id=0)
|
||||
if open_group is not None:
|
||||
self.expand_group(open_group.id)
|
||||
# Sort the images by its filename considering language specific
|
||||
# Sort the images by its filename considering language specific.
|
||||
# characters.
|
||||
images.sort(cmp=locale_compare, key=lambda image_object: os.path.split(unicode(image_object.filename))[1])
|
||||
images.sort(key=lambda image_object: get_locale_key(os.path.split(unicode(image_object.filename))[1]))
|
||||
for imageFile in images:
|
||||
log.debug(u'Loading image: %s', imageFile.filename)
|
||||
filename = os.path.split(imageFile.filename)[1]
|
||||
@ -394,6 +391,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
``initial_load``
|
||||
When set to False, the busy cursor and progressbar will be shown while loading images
|
||||
"""
|
||||
parent_group = None
|
||||
if target_group is None:
|
||||
# Find out if a group must be pre-selected
|
||||
preselect_group = None
|
||||
@ -439,6 +437,8 @@ class ImageMediaItem(MediaManagerItem):
|
||||
parent_group.parent_id = 0
|
||||
parent_group.group_name = self.choose_group_form.new_group_edit.text()
|
||||
self.manager.save_object(parent_group)
|
||||
self.fill_groups_combobox(self.choose_group_form.group_combobox)
|
||||
self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
|
||||
else:
|
||||
parent_group = target_group.data(0, QtCore.Qt.UserRole)
|
||||
if isinstance(parent_group, ImageFilenames):
|
||||
@ -455,7 +455,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
self.main_window.display_progress_bar(len(images))
|
||||
# Save the new images in the database
|
||||
self.save_new_images_list(images, group_id=parent_group.id, reload_list=False)
|
||||
self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename),
|
||||
self.load_full_list(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename),
|
||||
initial_load=initial_load, open_group=parent_group)
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
@ -473,7 +473,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
This boolean is set to True when the list in the interface should be reloaded after saving the new images
|
||||
"""
|
||||
for filename in images_list:
|
||||
if type(filename) is not str and type(filename) is not unicode:
|
||||
if not isinstance(filename, basestring):
|
||||
continue
|
||||
log.debug(u'Adding new image: %s', filename)
|
||||
imageFile = ImageFilenames()
|
||||
@ -482,7 +482,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
self.manager.save_object(imageFile)
|
||||
self.main_window.increment_progress_bar()
|
||||
if reload_list and images_list:
|
||||
self.loadFullList(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename))
|
||||
self.load_full_list(self.manager.get_all_objects(ImageFilenames, order_by_ref=ImageFilenames.filename))
|
||||
|
||||
def dnd_move_internal(self, target):
|
||||
"""
|
||||
@ -525,12 +525,12 @@ class ImageMediaItem(MediaManagerItem):
|
||||
group_items.append(item)
|
||||
if isinstance(item.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||
image_items.append(item)
|
||||
group_items.sort(cmp=locale_compare, key=lambda item: item.text(0))
|
||||
group_items.sort(key=lambda item: get_locale_key(item.text(0)))
|
||||
target_group.addChildren(group_items)
|
||||
image_items.sort(cmp=locale_compare, key=lambda item: item.text(0))
|
||||
image_items.sort(key=lambda item: get_locale_key(item.text(0)))
|
||||
target_group.addChildren(image_items)
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xmlVersion=False,
|
||||
def generate_slide_data(self, service_item, item=None, xml_version=False,
|
||||
remote=False, context=ServiceItemContext.Service):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
@ -554,28 +554,25 @@ class ImageMediaItem(MediaManagerItem):
|
||||
service_item.add_capability(ItemCapabilities.CanEditTitle)
|
||||
# force a nonexistent theme
|
||||
service_item.theme = -1
|
||||
missing_items = []
|
||||
missing_items_filenames = []
|
||||
images_filenames = []
|
||||
# Expand groups to images
|
||||
for bitem in items:
|
||||
if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None:
|
||||
for index in range(0, bitem.childCount()):
|
||||
if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||
items.append(bitem.child(index))
|
||||
items.remove(bitem)
|
||||
images_filenames.append(bitem.child(index).data(0, QtCore.Qt.UserRole).filename)
|
||||
elif isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||
images_filenames.append(bitem.data(0, QtCore.Qt.UserRole).filename)
|
||||
# Don't try to display empty groups
|
||||
if not items:
|
||||
if not images_filenames:
|
||||
return False
|
||||
# Find missing files
|
||||
for bitem in items:
|
||||
filename = bitem.data(0, QtCore.Qt.UserRole).filename
|
||||
for filename in images_filenames:
|
||||
if not os.path.exists(filename):
|
||||
missing_items.append(bitem)
|
||||
missing_items_filenames.append(filename)
|
||||
for item in missing_items:
|
||||
items.remove(item)
|
||||
# We cannot continue, as all images do not exist.
|
||||
if not items:
|
||||
if not images_filenames:
|
||||
if not remote:
|
||||
critical_error_message_box(
|
||||
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
||||
@ -583,15 +580,14 @@ class ImageMediaItem(MediaManagerItem):
|
||||
u'\n'.join(missing_items_filenames))
|
||||
return False
|
||||
# We have missing as well as existing images. We ask what to do.
|
||||
elif missing_items and QtGui.QMessageBox.question(self,
|
||||
elif missing_items_filenames and QtGui.QMessageBox.question(self,
|
||||
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
||||
translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: %s\n'
|
||||
'Do you want to add the other images anyway?') % u'\n'.join(missing_items_filenames),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
return False
|
||||
# Continue with the existing images.
|
||||
for bitem in items:
|
||||
filename = bitem.data(0, QtCore.Qt.UserRole).filename
|
||||
for filename in images_filenames:
|
||||
name = os.path.split(filename)[1]
|
||||
service_item.add_from_image(filename, name, background)
|
||||
return True
|
||||
@ -609,7 +605,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
else:
|
||||
return False
|
||||
|
||||
def onAddGroupClick(self):
|
||||
def on_add_group_click(self):
|
||||
"""
|
||||
Called to add a new group
|
||||
"""
|
||||
@ -630,7 +626,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
group_name=self.add_group_form.name_edit.text())
|
||||
if not self.check_group_exists(new_group):
|
||||
if self.manager.save_object(new_group):
|
||||
self.loadFullList(self.manager.get_all_objects(ImageFilenames,
|
||||
self.load_full_list(self.manager.get_all_objects(ImageFilenames,
|
||||
order_by_ref=ImageFilenames.filename))
|
||||
self.expand_group(new_group.id)
|
||||
self.fill_groups_combobox(self.choose_group_form.group_combobox)
|
||||
@ -639,23 +635,22 @@ class ImageMediaItem(MediaManagerItem):
|
||||
critical_error_message_box(
|
||||
message=translate('ImagePlugin.AddGroupForm', 'Could not add the new group.'))
|
||||
else:
|
||||
critical_error_message_box(
|
||||
message=translate('ImagePlugin.AddGroupForm', 'This group already exists.'))
|
||||
critical_error_message_box(message=translate('ImagePlugin.AddGroupForm', 'This group already exists.'))
|
||||
|
||||
def onResetClick(self):
|
||||
def on_reset_click(self):
|
||||
"""
|
||||
Called to reset the Live background with the image selected,
|
||||
Called to reset the Live background with the image selected.
|
||||
"""
|
||||
self.resetAction.setVisible(False)
|
||||
self.reset_action.setVisible(False)
|
||||
self.live_controller.display.reset_image()
|
||||
|
||||
def live_theme_changed(self):
|
||||
"""
|
||||
Triggered by the change of theme in the slide controller
|
||||
Triggered by the change of theme in the slide controller.
|
||||
"""
|
||||
self.resetAction.setVisible(False)
|
||||
self.reset_action.setVisible(False)
|
||||
|
||||
def onReplaceClick(self):
|
||||
def on_replace_click(self):
|
||||
"""
|
||||
Called to replace Live backgound with the image selected.
|
||||
"""
|
||||
@ -664,12 +659,12 @@ class ImageMediaItem(MediaManagerItem):
|
||||
background = QtGui.QColor(Settings().value(self.settings_section + u'/background color'))
|
||||
bitem = self.list_view.selectedItems()[0]
|
||||
if not isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||
# Only continue when an image is selected
|
||||
# Only continue when an image is selected.
|
||||
return
|
||||
filename = bitem.data(0, QtCore.Qt.UserRole).filename
|
||||
if os.path.exists(filename):
|
||||
if self.live_controller.display.direct_image(filename, background):
|
||||
self.resetAction.setVisible(True)
|
||||
self.reset_action.setVisible(True)
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
translate('ImagePlugin.MediaItem', 'There was no display item to amend.'))
|
||||
|
@ -27,8 +27,7 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`media` module provides the Media plugin which allows OpenLP to
|
||||
display videos. The media supported depends not only on the Python support
|
||||
but also extensively on the codecs installed on the underlying operating system
|
||||
being picked up and usable by Python.
|
||||
The :mod:`media` module provides the Media plugin which allows OpenLP to display videos. The media supported depends not
|
||||
only on the Python support but also extensively on the codecs installed on the underlying operating system being picked
|
||||
up and usable by Python.
|
||||
"""
|
||||
|
@ -37,15 +37,18 @@ from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, Regist
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
|
||||
from openlp.core.ui import DisplayController, Display, DisplayControllerType
|
||||
from openlp.core.ui.media import get_media_players, set_media_players
|
||||
from openlp.core.utils import AppLocation, locale_compare
|
||||
from openlp.core.utils import AppLocation, get_locale_key
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CLAPPERBOARD = u':/media/slidecontroller_multimedia.png'
|
||||
VIDEO = build_icon(QtGui.QImage(u':/media/media_video.png'))
|
||||
AUDIO = build_icon(QtGui.QImage(u':/media/media_audio.png'))
|
||||
DVDICON = build_icon(QtGui.QImage(u':/media/media_video.png'))
|
||||
ERROR = build_icon(QtGui.QImage(u':/general/general_delete.png'))
|
||||
VIDEO_ICON = build_icon(u':/media/media_video.png')
|
||||
AUDIO_ICON = build_icon(u':/media/media_audio.png')
|
||||
DVD_ICON = build_icon(u':/media/media_video.png')
|
||||
ERROR_ICON = build_icon(u':/general/general_delete.png')
|
||||
|
||||
|
||||
class MediaMediaItem(MediaManagerItem):
|
||||
"""
|
||||
@ -79,12 +82,12 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def retranslateUi(self):
|
||||
self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media')
|
||||
self.replaceAction.setText(UiStrings().ReplaceBG)
|
||||
self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG)
|
||||
self.resetAction.setText(UiStrings().ResetBG)
|
||||
self.resetAction.setToolTip(UiStrings().ResetLiveBG)
|
||||
self.replace_action.setText(UiStrings().ReplaceBG)
|
||||
self.replace_action.setToolTip(UiStrings().ReplaceLiveBG)
|
||||
self.reset_action.setText(UiStrings().ResetBG)
|
||||
self.reset_action.setToolTip(UiStrings().ResetLiveBG)
|
||||
self.automatic = UiStrings().Automatic
|
||||
self.displayTypeLabel.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
|
||||
self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
|
||||
self.rebuild_players()
|
||||
|
||||
def required_icons(self):
|
||||
@ -98,27 +101,28 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def add_list_view_to_toolbar(self):
|
||||
MediaManagerItem.add_list_view_to_toolbar(self)
|
||||
self.list_view.addAction(self.replaceAction)
|
||||
self.list_view.addAction(self.replace_action)
|
||||
|
||||
def add_end_header_bar(self):
|
||||
# Replace backgrounds do not work at present so remove functionality.
|
||||
self.replaceAction = self.toolbar.add_toolbar_action(u'replaceAction', icon=u':/slides/slide_blank.png',
|
||||
self.replace_action = self.toolbar.add_toolbar_action(u'replace_action', icon=u':/slides/slide_blank.png',
|
||||
triggers=self.onReplaceClick)
|
||||
self.resetAction = self.toolbar.add_toolbar_action(u'resetAction', icon=u':/system/system_close.png',
|
||||
self.reset_action = self.toolbar.add_toolbar_action(u'reset_action', icon=u':/system/system_close.png',
|
||||
visible=False, triggers=self.onResetClick)
|
||||
self.mediaWidget = QtGui.QWidget(self)
|
||||
self.mediaWidget.setObjectName(u'mediaWidget')
|
||||
self.displayLayout = QtGui.QFormLayout(self.mediaWidget)
|
||||
self.displayLayout.setMargin(self.displayLayout.spacing())
|
||||
self.displayLayout.setObjectName(u'displayLayout')
|
||||
self.displayTypeLabel = QtGui.QLabel(self.mediaWidget)
|
||||
self.displayTypeLabel.setObjectName(u'displayTypeLabel')
|
||||
self.displayTypeComboBox = create_horizontal_adjusting_combo_box(self.mediaWidget, u'displayTypeComboBox')
|
||||
self.displayTypeLabel.setBuddy(self.displayTypeComboBox)
|
||||
self.displayLayout.addRow(self.displayTypeLabel, self.displayTypeComboBox)
|
||||
# Add the Media widget to the page layout
|
||||
self.page_layout.addWidget(self.mediaWidget)
|
||||
self.displayTypeComboBox.currentIndexChanged.connect(self.overridePlayerChanged)
|
||||
self.media_widget = QtGui.QWidget(self)
|
||||
self.media_widget.setObjectName(u'media_widget')
|
||||
self.display_layout = QtGui.QFormLayout(self.media_widget)
|
||||
self.display_layout.setMargin(self.display_layout.spacing())
|
||||
self.display_layout.setObjectName(u'display_layout')
|
||||
self.display_type_label = QtGui.QLabel(self.media_widget)
|
||||
self.display_type_label.setObjectName(u'display_type_label')
|
||||
self.display_type_combo_box = create_horizontal_adjusting_combo_box(
|
||||
self.media_widget, u'display_type_combo_box')
|
||||
self.display_type_label.setBuddy(self.display_type_combo_box)
|
||||
self.display_layout.addRow(self.display_type_label, self.display_type_combo_box)
|
||||
# Add the Media widget to the page layout.
|
||||
self.page_layout.addWidget(self.media_widget)
|
||||
self.display_type_combo_box.currentIndexChanged.connect(self.overridePlayerChanged)
|
||||
|
||||
def overridePlayerChanged(self, index):
|
||||
player = get_media_players()[0]
|
||||
@ -132,13 +136,13 @@ class MediaMediaItem(MediaManagerItem):
|
||||
Called to reset the Live background with the media selected,
|
||||
"""
|
||||
self.media_controller.media_reset(self.live_controller)
|
||||
self.resetAction.setVisible(False)
|
||||
self.reset_action.setVisible(False)
|
||||
|
||||
def video_background_replaced(self):
|
||||
"""
|
||||
Triggered by main display on change of serviceitem.
|
||||
"""
|
||||
self.resetAction.setVisible(False)
|
||||
self.reset_action.setVisible(False)
|
||||
|
||||
def onReplaceClick(self):
|
||||
"""
|
||||
@ -151,11 +155,11 @@ class MediaMediaItem(MediaManagerItem):
|
||||
if os.path.exists(filename):
|
||||
service_item = ServiceItem()
|
||||
service_item.title = u'webkit'
|
||||
service_item.shortname = service_item.title
|
||||
service_item.processor = u'webkit'
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.add_from_command(path, name,CLAPPERBOARD)
|
||||
if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True):
|
||||
self.resetAction.setVisible(True)
|
||||
self.reset_action.setVisible(True)
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
translate('MediaPlugin.MediaItem', 'There was no display item to amend.'))
|
||||
@ -164,7 +168,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was a problem replacing your background, the media file "%s" no longer exists.') % filename)
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xmlVersion=False, remote=False,
|
||||
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
|
||||
context=ServiceItemContext.Live):
|
||||
"""
|
||||
Generate the slide data. Needs to be implemented by the plugin.
|
||||
@ -181,9 +185,9 @@ class MediaMediaItem(MediaManagerItem):
|
||||
translate('MediaPlugin.MediaItem', 'Missing Media File'),
|
||||
translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename)
|
||||
return False
|
||||
service_item.title = self.displayTypeComboBox.currentText()
|
||||
service_item.shortname = service_item.title
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.title = name
|
||||
service_item.processor = self.display_type_combo_box.currentText()
|
||||
service_item.add_from_command(path, name, CLAPPERBOARD)
|
||||
# Only get start and end times if going to a service
|
||||
if context == ServiceItemContext.Service:
|
||||
@ -192,7 +196,6 @@ class MediaMediaItem(MediaManagerItem):
|
||||
return False
|
||||
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
|
||||
service_item.add_capability(ItemCapabilities.RequiresMedia)
|
||||
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
|
||||
if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked:
|
||||
service_item.will_auto_start = True
|
||||
# force a non-existent theme
|
||||
@ -209,8 +212,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def rebuild_players(self):
|
||||
"""
|
||||
Rebuild the tab in the media manager when changes are made in
|
||||
the settings
|
||||
Rebuild the tab in the media manager when changes are made in the settings.
|
||||
"""
|
||||
self.populateDisplayTypes()
|
||||
self.on_new_file_masks = translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)') % (
|
||||
@ -222,29 +224,27 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def populateDisplayTypes(self):
|
||||
"""
|
||||
Load the combobox with the enabled media players,
|
||||
allowing user to select a specific player if settings allow
|
||||
Load the combobox with the enabled media players, allowing user to select a specific player if settings allow.
|
||||
"""
|
||||
# block signals to avoid unnecessary overridePlayerChanged Signals
|
||||
# while combo box creation
|
||||
self.displayTypeComboBox.blockSignals(True)
|
||||
self.displayTypeComboBox.clear()
|
||||
# block signals to avoid unnecessary overridePlayerChanged Signals while combo box creation
|
||||
self.display_type_combo_box.blockSignals(True)
|
||||
self.display_type_combo_box.clear()
|
||||
usedPlayers, overridePlayer = get_media_players()
|
||||
media_players = self.media_controller.media_players
|
||||
currentIndex = 0
|
||||
for player in usedPlayers:
|
||||
# load the drop down selection
|
||||
self.displayTypeComboBox.addItem(media_players[player].original_name)
|
||||
self.display_type_combo_box.addItem(media_players[player].original_name)
|
||||
if overridePlayer == player:
|
||||
currentIndex = len(self.displayTypeComboBox)
|
||||
if self.displayTypeComboBox.count() > 1:
|
||||
self.displayTypeComboBox.insertItem(0, self.automatic)
|
||||
self.displayTypeComboBox.setCurrentIndex(currentIndex)
|
||||
currentIndex = len(self.display_type_combo_box)
|
||||
if self.display_type_combo_box.count() > 1:
|
||||
self.display_type_combo_box.insertItem(0, self.automatic)
|
||||
self.display_type_combo_box.setCurrentIndex(currentIndex)
|
||||
if overridePlayer:
|
||||
self.mediaWidget.show()
|
||||
self.media_widget.show()
|
||||
else:
|
||||
self.mediaWidget.hide()
|
||||
self.displayTypeComboBox.blockSignals(False)
|
||||
self.media_widget.hide()
|
||||
self.display_type_combo_box.blockSignals(False)
|
||||
|
||||
def on_delete_click(self):
|
||||
"""
|
||||
@ -259,42 +259,41 @@ class MediaMediaItem(MediaManagerItem):
|
||||
Settings().setValue(self.settings_section + u'/media files', self.get_file_list())
|
||||
|
||||
def load_list(self, media, target_group=None):
|
||||
# Sort the media by its filename considering language specific
|
||||
# characters.
|
||||
media.sort(cmp=locale_compare, key=lambda filename: os.path.split(unicode(filename))[1])
|
||||
# Sort the media by its filename considering language specific characters.
|
||||
media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1]))
|
||||
for track in media:
|
||||
track_info = QtCore.QFileInfo(track)
|
||||
if not os.path.exists(track):
|
||||
filename = os.path.split(unicode(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setIcon(ERROR)
|
||||
item_name.setIcon(ERROR_ICON)
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
elif track_info.isFile():
|
||||
filename = os.path.split(unicode(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
if u'*.%s' % (filename.split(u'.')[-1].lower()) in self.media_controller.audio_extensions_list:
|
||||
item_name.setIcon(AUDIO)
|
||||
item_name.setIcon(AUDIO_ICON)
|
||||
else:
|
||||
item_name.setIcon(VIDEO)
|
||||
item_name.setIcon(VIDEO_ICON)
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
else:
|
||||
filename = os.path.split(unicode(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setIcon(build_icon(DVDICON))
|
||||
item_name.setIcon(build_icon(DVD_ICON))
|
||||
item_name.setData(QtCore.Qt.UserRole, track)
|
||||
item_name.setToolTip(track)
|
||||
self.list_view.addItem(item_name)
|
||||
|
||||
def getList(self, type=MediaType.Audio):
|
||||
def get_list(self, type=MediaType.Audio):
|
||||
media = Settings().value(self.settings_section + u'/media files')
|
||||
media.sort(cmp=locale_compare, key=lambda filename: os.path.split(unicode(filename))[1])
|
||||
ext = []
|
||||
media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1]))
|
||||
extension = []
|
||||
if type == MediaType.Audio:
|
||||
ext = self.media_controller.audio_extensions_list
|
||||
extension = self.media_controller.audio_extensions_list
|
||||
else:
|
||||
ext = self.media_controller.video_extensions_list
|
||||
ext = map(lambda x: x[1:], ext)
|
||||
media = filter(lambda x: os.path.splitext(x)[1] in ext, media)
|
||||
extension = self.media_controller.video_extensions_list
|
||||
extension = map(lambda x: x[1:], extension)
|
||||
media = filter(lambda x: os.path.splitext(x)[1] in extension, media)
|
||||
return media
|
||||
|
||||
def search(self, string, showError):
|
||||
|
@ -34,8 +34,10 @@ from PyQt4 import QtCore
|
||||
from openlp.core.lib import Plugin, Registry, StringContent, Settings, build_icon, translate
|
||||
from openlp.plugins.media.lib import MediaMediaItem, MediaTab
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Some settings starting with "media" are in core, because they are needed for core functionality.
|
||||
__default_settings__ = {
|
||||
u'media/media auto start': QtCore.Qt.Unchecked,
|
||||
@ -54,7 +56,7 @@ class MediaPlugin(Plugin):
|
||||
# passed with drag and drop messages
|
||||
self.dnd_id = u'Media'
|
||||
|
||||
def create_settings_Tab(self, parent):
|
||||
def create_settings_tab(self, parent):
|
||||
"""
|
||||
Create the settings Tab
|
||||
"""
|
||||
@ -94,7 +96,7 @@ class MediaPlugin(Plugin):
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Time to tidy up on exit
|
||||
Time to tidy up on exit.
|
||||
"""
|
||||
log.info(u'Media Finalising')
|
||||
self.media_controller.finalise()
|
||||
@ -102,19 +104,19 @@ class MediaPlugin(Plugin):
|
||||
|
||||
def get_display_css(self):
|
||||
"""
|
||||
Add css style sheets to htmlbuilder
|
||||
Add css style sheets to htmlbuilder.
|
||||
"""
|
||||
return self.media_controller.get_media_display_css()
|
||||
|
||||
def get_display_javascript(self):
|
||||
"""
|
||||
Add javascript functions to htmlbuilder
|
||||
Add javascript functions to htmlbuilder.
|
||||
"""
|
||||
return self.media_controller.get_media_display_javascript()
|
||||
|
||||
def get_display_html(self):
|
||||
"""
|
||||
Add html code to htmlbuilder
|
||||
Add html code to htmlbuilder.
|
||||
"""
|
||||
return self.media_controller.get_media_display_html()
|
||||
|
||||
|
@ -27,6 +27,6 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`presentations` module provides the Presentations plugin which allows
|
||||
OpenLP to show presentations from most popular presentation packages.
|
||||
The :mod:`presentations` module provides the Presentations plugin which allows OpenLP to show presentations from most
|
||||
popular presentation packages.
|
||||
"""
|
||||
|
@ -62,13 +62,14 @@ from openlp.core.lib import ScreenList
|
||||
from openlp.core.utils import delete_file, get_uno_command, get_uno_instance
|
||||
from presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ImpressController(PresentationController):
|
||||
"""
|
||||
Class to control interactions with Impress presentations.
|
||||
It creates the runtime environment, loads and closes the presentation as
|
||||
well as triggering the correct activities based on the users input
|
||||
Class to control interactions with Impress presentations. It creates the runtime environment, loads and closes the
|
||||
presentation as well as triggering the correct activities based on the users input.
|
||||
"""
|
||||
log.info(u'ImpressController loaded')
|
||||
|
||||
@ -79,14 +80,14 @@ class ImpressController(PresentationController):
|
||||
log.debug(u'Initialising')
|
||||
PresentationController.__init__(self, plugin, u'Impress', ImpressDocument)
|
||||
self.supports = [u'odp']
|
||||
self.alsosupports = [u'ppt', u'pps', u'pptx', u'ppsx']
|
||||
self.also_supports = [u'ppt', u'pps', u'pptx', u'ppsx']
|
||||
self.process = None
|
||||
self.desktop = None
|
||||
self.manager = None
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
Impress is able to run on this machine
|
||||
Impress is able to run on this machine.
|
||||
"""
|
||||
log.debug(u'check_available')
|
||||
if os.name == u'nt':
|
||||
@ -96,9 +97,8 @@ class ImpressController(PresentationController):
|
||||
|
||||
def start_process(self):
|
||||
"""
|
||||
Loads a running version of OpenOffice in the background.
|
||||
It is not displayed to the user but is available to the UNO interface
|
||||
when required.
|
||||
Loads a running version of OpenOffice in the background. It is not displayed to the user but is available to the
|
||||
UNO interface when required.
|
||||
"""
|
||||
log.debug(u'start process Openoffice')
|
||||
if os.name == u'nt':
|
||||
@ -113,8 +113,7 @@ class ImpressController(PresentationController):
|
||||
|
||||
def get_uno_desktop(self):
|
||||
"""
|
||||
On non-Windows platforms, use Uno. Get the OpenOffice desktop
|
||||
which will be used to manage impress
|
||||
On non-Windows platforms, use Uno. Get the OpenOffice desktop which will be used to manage impress.
|
||||
"""
|
||||
log.debug(u'get UNO Desktop Openoffice')
|
||||
uno_instance = None
|
||||
@ -132,8 +131,7 @@ class ImpressController(PresentationController):
|
||||
loop += 1
|
||||
try:
|
||||
self.manager = uno_instance.ServiceManager
|
||||
log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext'
|
||||
u' - Desktop')
|
||||
log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
|
||||
desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
|
||||
return desktop
|
||||
except:
|
||||
@ -142,8 +140,7 @@ class ImpressController(PresentationController):
|
||||
|
||||
def get_com_desktop(self):
|
||||
"""
|
||||
On Windows platforms, use COM. Return the desktop object which
|
||||
will be used to manage Impress
|
||||
On Windows platforms, use COM. Return the desktop object which will be used to manage Impress.
|
||||
"""
|
||||
log.debug(u'get COM Desktop OpenOffice')
|
||||
if not self.manager:
|
||||
@ -157,7 +154,7 @@ class ImpressController(PresentationController):
|
||||
|
||||
def get_com_servicemanager(self):
|
||||
"""
|
||||
Return the OOo service manager for windows
|
||||
Return the OOo service manager for windows.
|
||||
"""
|
||||
log.debug(u'get_com_servicemanager openoffice')
|
||||
try:
|
||||
@ -168,7 +165,7 @@ class ImpressController(PresentationController):
|
||||
|
||||
def kill(self):
|
||||
"""
|
||||
Called at system exit to clean up any running presentations
|
||||
Called at system exit to clean up any running presentations.
|
||||
"""
|
||||
log.debug(u'Kill OpenOffice')
|
||||
while self.docs:
|
||||
@ -203,12 +200,12 @@ class ImpressController(PresentationController):
|
||||
|
||||
class ImpressDocument(PresentationDocument):
|
||||
"""
|
||||
Class which holds information and controls a single presentation
|
||||
Class which holds information and controls a single presentation.
|
||||
"""
|
||||
|
||||
def __init__(self, controller, presentation):
|
||||
"""
|
||||
Constructor, store information about the file and initialise
|
||||
Constructor, store information about the file and initialise.
|
||||
"""
|
||||
log.debug(u'Init Presentation OpenOffice')
|
||||
PresentationDocument.__init__(self, controller, presentation)
|
||||
@ -218,11 +215,9 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def load_presentation(self):
|
||||
"""
|
||||
Called when a presentation is added to the SlideController.
|
||||
It builds the environment, starts communcations with the background
|
||||
OpenOffice task started earlier. If OpenOffice is not present is is
|
||||
started. Once the environment is available the presentation is loaded
|
||||
and started.
|
||||
Called when a presentation is added to the SlideController. It builds the environment, starts communcations with
|
||||
the background OpenOffice task started earlier. If OpenOffice is not present is is started. Once the environment
|
||||
is available the presentation is loaded and started.
|
||||
"""
|
||||
log.debug(u'Load Presentation OpenOffice')
|
||||
if os.name == u'nt':
|
||||
@ -239,13 +234,12 @@ class ImpressDocument(PresentationDocument):
|
||||
self.desktop = desktop
|
||||
properties = []
|
||||
if os.name != u'nt':
|
||||
# Recent versions of Impress on Windows won't start the presentation
|
||||
# if it starts as minimized. It seems OK on Linux though.
|
||||
# Recent versions of Impress on Windows won't start the presentation if it starts as minimized. It seems OK
|
||||
# on Linux though.
|
||||
properties.append(self.create_property(u'Minimized', True))
|
||||
properties = tuple(properties)
|
||||
try:
|
||||
self.document = desktop.loadComponentFromURL(url, u'_blank',
|
||||
0, properties)
|
||||
self.document = desktop.loadComponentFromURL(url, u'_blank', 0, properties)
|
||||
except:
|
||||
log.warn(u'Failed to load presentation %s' % url)
|
||||
return False
|
||||
@ -262,33 +256,33 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def create_thumbnails(self):
|
||||
"""
|
||||
Create thumbnail images for presentation
|
||||
Create thumbnail images for presentation.
|
||||
"""
|
||||
log.debug(u'create thumbnails OpenOffice')
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
if os.name == u'nt':
|
||||
thumbdirurl = u'file:///' + self.get_temp_folder().replace(u'\\', u'/') \
|
||||
thumb_dir_url = u'file:///' + self.get_temp_folder().replace(u'\\', u'/') \
|
||||
.replace(u':', u'|').replace(u' ', u'%20')
|
||||
else:
|
||||
thumbdirurl = uno.systemPathToFileUrl(self.get_temp_folder())
|
||||
props = []
|
||||
props.append(self.create_property(u'FilterName', u'impress_png_Export'))
|
||||
props = tuple(props)
|
||||
thumb_dir_url = uno.systemPathToFileUrl(self.get_temp_folder())
|
||||
properties = []
|
||||
properties.append(self.create_property(u'FilterName', u'impress_png_Export'))
|
||||
properties = tuple(properties)
|
||||
doc = self.document
|
||||
pages = doc.getDrawPages()
|
||||
if not pages:
|
||||
return
|
||||
if not os.path.isdir(self.get_temp_folder()):
|
||||
os.makedirs(self.get_temp_folder())
|
||||
for idx in range(pages.getCount()):
|
||||
page = pages.getByIndex(idx)
|
||||
for index in range(pages.getCount()):
|
||||
page = pages.getByIndex(index)
|
||||
doc.getCurrentController().setCurrentPage(page)
|
||||
urlpath = u'%s/%s.png' % (thumbdirurl, unicode(idx + 1))
|
||||
path = os.path.join(self.get_temp_folder(), unicode(idx + 1) + u'.png')
|
||||
url_path = u'%s/%s.png' % (thumb_dir_url, unicode(index + 1))
|
||||
path = os.path.join(self.get_temp_folder(), unicode(index + 1) + u'.png')
|
||||
try:
|
||||
doc.storeToURL(urlpath, props)
|
||||
self.convert_thumbnail(path, idx + 1)
|
||||
doc.storeToURL(url_path, properties)
|
||||
self.convert_thumbnail(path, index + 1)
|
||||
delete_file(path)
|
||||
except ErrorCodeIOException, exception:
|
||||
log.exception(u'ERROR! ErrorCodeIOException %d' % exception.ErrCode)
|
||||
@ -297,23 +291,21 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def create_property(self, name, value):
|
||||
"""
|
||||
Create an OOo style property object which are passed into some
|
||||
Uno methods
|
||||
Create an OOo style property object which are passed into some Uno methods.
|
||||
"""
|
||||
log.debug(u'create property OpenOffice')
|
||||
if os.name == u'nt':
|
||||
prop = self.controller.manager.Bridge_GetStruct(u'com.sun.star.beans.PropertyValue')
|
||||
property_object = self.controller.manager.Bridge_GetStruct(u'com.sun.star.beans.PropertyValue')
|
||||
else:
|
||||
prop = PropertyValue()
|
||||
prop.Name = name
|
||||
prop.Value = value
|
||||
return prop
|
||||
property_object = PropertyValue()
|
||||
property_object.Name = name
|
||||
property_object.Value = value
|
||||
return property_object
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
Close presentation and clean up objects
|
||||
Triggered by new object being added to SlideController or OpenLP
|
||||
being shutdown
|
||||
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
|
||||
shutdown.
|
||||
"""
|
||||
log.debug(u'close Presentation OpenOffice')
|
||||
if self.document:
|
||||
@ -329,7 +321,7 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def is_loaded(self):
|
||||
"""
|
||||
Returns true if a presentation is loaded
|
||||
Returns true if a presentation is loaded.
|
||||
"""
|
||||
log.debug(u'is loaded OpenOffice')
|
||||
if self.presentation is None or self.document is None:
|
||||
@ -346,7 +338,7 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns true if a presentation is active and running
|
||||
Returns true if a presentation is active and running.
|
||||
"""
|
||||
log.debug(u'is active OpenOffice')
|
||||
if not self.is_loaded():
|
||||
@ -355,21 +347,21 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def unblank_screen(self):
|
||||
"""
|
||||
Unblanks the screen
|
||||
Unblanks the screen.
|
||||
"""
|
||||
log.debug(u'unblank screen OpenOffice')
|
||||
return self.control.resume()
|
||||
|
||||
def blank_screen(self):
|
||||
"""
|
||||
Blanks the screen
|
||||
Blanks the screen.
|
||||
"""
|
||||
log.debug(u'blank screen OpenOffice')
|
||||
self.control.blankScreen(0)
|
||||
|
||||
def is_blank(self):
|
||||
"""
|
||||
Returns true if screen is blank
|
||||
Returns true if screen is blank.
|
||||
"""
|
||||
log.debug(u'is blank OpenOffice')
|
||||
if self.control and self.control.isRunning():
|
||||
@ -379,7 +371,7 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def stop_presentation(self):
|
||||
"""
|
||||
Stop the presentation, remove from screen
|
||||
Stop the presentation, remove from screen.
|
||||
"""
|
||||
log.debug(u'stop presentation OpenOffice')
|
||||
# deactivate should hide the screen according to docs, but doesn't
|
||||
@ -389,18 +381,17 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def start_presentation(self):
|
||||
"""
|
||||
Start the presentation from the beginning
|
||||
Start the presentation from the beginning.
|
||||
"""
|
||||
log.debug(u'start presentation OpenOffice')
|
||||
if self.control is None or not self.control.isRunning():
|
||||
self.presentation.start()
|
||||
self.control = self.presentation.getController()
|
||||
# start() returns before the Component is ready.
|
||||
# Try for 15 seconds
|
||||
i = 1
|
||||
while not self.control and i < 150:
|
||||
# start() returns before the Component is ready. Try for 15 seconds.
|
||||
sleep_count = 1
|
||||
while not self.control and sleep_count < 150:
|
||||
time.sleep(0.1)
|
||||
i += 1
|
||||
sleep_count += 1
|
||||
self.control = self.presentation.getController()
|
||||
else:
|
||||
self.control.activate()
|
||||
@ -408,25 +399,25 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def get_slide_number(self):
|
||||
"""
|
||||
Return the current slide number on the screen, from 1
|
||||
Return the current slide number on the screen, from 1.
|
||||
"""
|
||||
return self.control.getCurrentSlideIndex() + 1
|
||||
|
||||
def get_slide_count(self):
|
||||
"""
|
||||
Return the total number of slides
|
||||
Return the total number of slides.
|
||||
"""
|
||||
return self.document.getDrawPages().getCount()
|
||||
|
||||
def goto_slide(self, slideno):
|
||||
"""
|
||||
Go to a specific slide (from 1)
|
||||
Go to a specific slide (from 1).
|
||||
"""
|
||||
self.control.gotoSlideIndex(slideno-1)
|
||||
|
||||
def next_step(self):
|
||||
"""
|
||||
Triggers the next effect of slide on the running presentation
|
||||
Triggers the next effect of slide on the running presentation.
|
||||
"""
|
||||
is_paused = self.control.isPaused()
|
||||
self.control.gotoNextEffect()
|
||||
@ -436,7 +427,7 @@ class ImpressDocument(PresentationDocument):
|
||||
|
||||
def previous_step(self):
|
||||
"""
|
||||
Triggers the previous slide on the running presentation
|
||||
Triggers the previous slide on the running presentation.
|
||||
"""
|
||||
self.control.gotoPreviousSlide()
|
||||
|
||||
@ -470,8 +461,8 @@ class ImpressDocument(PresentationDocument):
|
||||
page = pages.getByIndex(slide_no - 1)
|
||||
if notes:
|
||||
page = page.getNotesPage()
|
||||
for idx in range(page.getCount()):
|
||||
shape = page.getByIndex(idx)
|
||||
for index in range(page.getCount()):
|
||||
shape = page.getByIndex(index)
|
||||
if shape.supportsService("com.sun.star.drawing.Text"):
|
||||
text += shape.getString() + '\n'
|
||||
return text
|
||||
|
@ -35,17 +35,20 @@ from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.lib import MediaManagerItem, Registry, ItemCapabilities, ServiceItemContext, Settings, UiStrings, \
|
||||
build_icon, check_item_selected, create_thumb, translate, validate_thumb
|
||||
from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
|
||||
from openlp.core.utils import locale_compare
|
||||
from openlp.core.utils import get_locale_key
|
||||
from openlp.plugins.presentations.lib import MessageListener
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
ERROR = QtGui.QImage(u':/general/general_delete.png')
|
||||
|
||||
ERROR_IMAGE = QtGui.QImage(u':/general/general_delete.png')
|
||||
|
||||
|
||||
class PresentationMediaItem(MediaManagerItem):
|
||||
"""
|
||||
This is the Presentation media manager item for Presentation Items.
|
||||
It can present files using Openoffice and Powerpoint
|
||||
This is the Presentation media manager item for Presentation Items. It can present files using Openoffice and
|
||||
Powerpoint
|
||||
"""
|
||||
log.info(u'Presentations Media Item loaded')
|
||||
|
||||
@ -71,25 +74,25 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
"""
|
||||
self.on_new_prompt = translate('PresentationPlugin.MediaItem', 'Select Presentation(s)')
|
||||
self.Automatic = translate('PresentationPlugin.MediaItem', 'Automatic')
|
||||
self.displayTypeLabel.setText(translate('PresentationPlugin.MediaItem', 'Present using:'))
|
||||
self.display_type_label.setText(translate('PresentationPlugin.MediaItem', 'Present using:'))
|
||||
|
||||
def build_file_mask_string(self):
|
||||
"""
|
||||
Build the list of file extensions to be used in the Open file dialog
|
||||
Build the list of file extensions to be used in the Open file dialog.
|
||||
"""
|
||||
fileType = u''
|
||||
file_type_list = u''
|
||||
for controller in self.controllers:
|
||||
if self.controllers[controller].enabled():
|
||||
types = self.controllers[controller].supports + self.controllers[controller].alsosupports
|
||||
for type in types:
|
||||
if fileType.find(type) == -1:
|
||||
fileType += u'*.%s ' % type
|
||||
self.service_manager.supported_suffixes(type)
|
||||
self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % fileType
|
||||
file_types = self.controllers[controller].supports + self.controllers[controller].also_supports
|
||||
for file_type in file_types:
|
||||
if file_type.find(file_type) == -1:
|
||||
file_type_list += u'*.%s ' % file_type
|
||||
self.service_manager.supported_suffixes(file_type)
|
||||
self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % file_type_list
|
||||
|
||||
def required_icons(self):
|
||||
"""
|
||||
Set which icons the media manager tab should show
|
||||
Set which icons the media manager tab should show.
|
||||
"""
|
||||
MediaManagerItem.required_icons(self)
|
||||
self.has_file_icon = True
|
||||
@ -98,21 +101,21 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
|
||||
def add_end_header_bar(self):
|
||||
"""
|
||||
Display custom media manager items for presentations
|
||||
Display custom media manager items for presentations.
|
||||
"""
|
||||
self.presentationWidget = QtGui.QWidget(self)
|
||||
self.presentationWidget.setObjectName(u'presentationWidget')
|
||||
self.displayLayout = QtGui.QFormLayout(self.presentationWidget)
|
||||
self.displayLayout.setMargin(self.displayLayout.spacing())
|
||||
self.displayLayout.setObjectName(u'displayLayout')
|
||||
self.displayTypeLabel = QtGui.QLabel(self.presentationWidget)
|
||||
self.displayTypeLabel.setObjectName(u'displayTypeLabel')
|
||||
self.displayTypeComboBox = create_horizontal_adjusting_combo_box(self.presentationWidget,
|
||||
u'displayTypeComboBox')
|
||||
self.displayTypeLabel.setBuddy(self.displayTypeComboBox)
|
||||
self.displayLayout.addRow(self.displayTypeLabel, self.displayTypeComboBox)
|
||||
# Add the Presentation widget to the page layout
|
||||
self.page_layout.addWidget(self.presentationWidget)
|
||||
self.presentation_widget = QtGui.QWidget(self)
|
||||
self.presentation_widget.setObjectName(u'presentation_widget')
|
||||
self.display_layout = QtGui.QFormLayout(self.presentation_widget)
|
||||
self.display_layout.setMargin(self.display_layout.spacing())
|
||||
self.display_layout.setObjectName(u'display_layout')
|
||||
self.display_type_label = QtGui.QLabel(self.presentation_widget)
|
||||
self.display_type_label.setObjectName(u'display_type_label')
|
||||
self.display_type_combo_box = create_horizontal_adjusting_combo_box(self.presentation_widget,
|
||||
u'display_type_combo_box')
|
||||
self.display_type_label.setBuddy(self.display_type_combo_box)
|
||||
self.display_layout.addRow(self.display_type_label, self.display_type_combo_box)
|
||||
# Add the Presentation widget to the page layout.
|
||||
self.page_layout.addWidget(self.presentation_widget)
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
@ -120,56 +123,54 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
"""
|
||||
self.list_view.setIconSize(QtCore.QSize(88, 50))
|
||||
files = Settings().value(self.settings_section + u'/presentations files')
|
||||
self.load_list(files, initialLoad=True)
|
||||
self.load_list(files, initial_load=True)
|
||||
self.populate_display_types()
|
||||
|
||||
def populate_display_types(self):
|
||||
"""
|
||||
Load the combobox with the enabled presentation controllers,
|
||||
allowing user to select a specific app if settings allow
|
||||
Load the combobox with the enabled presentation controllers, allowing user to select a specific app if settings
|
||||
allow.
|
||||
"""
|
||||
self.displayTypeComboBox.clear()
|
||||
self.display_type_combo_box.clear()
|
||||
for item in self.controllers:
|
||||
# load the drop down selection
|
||||
if self.controllers[item].enabled():
|
||||
self.displayTypeComboBox.addItem(item)
|
||||
if self.displayTypeComboBox.count() > 1:
|
||||
self.displayTypeComboBox.insertItem(0, self.Automatic)
|
||||
self.displayTypeComboBox.setCurrentIndex(0)
|
||||
self.display_type_combo_box.addItem(item)
|
||||
if self.display_type_combo_box.count() > 1:
|
||||
self.display_type_combo_box.insertItem(0, self.Automatic)
|
||||
self.display_type_combo_box.setCurrentIndex(0)
|
||||
if Settings().value(self.settings_section + u'/override app') == QtCore.Qt.Checked:
|
||||
self.presentationWidget.show()
|
||||
self.presentation_widget.show()
|
||||
else:
|
||||
self.presentationWidget.hide()
|
||||
self.presentation_widget.hide()
|
||||
|
||||
def load_list(self, files, target_group=None, initialLoad=False):
|
||||
def load_list(self, files, target_group=None, initial_load=False):
|
||||
"""
|
||||
Add presentations into the media manager
|
||||
This is called both on initial load of the plugin to populate with
|
||||
existing files, and when the user adds new files via the media manager
|
||||
Add presentations into the media manager. This is called both on initial load of the plugin to populate with
|
||||
existing files, and when the user adds new files via the media manager.
|
||||
"""
|
||||
currlist = self.get_file_list()
|
||||
titles = [os.path.split(file)[1] for file in currlist]
|
||||
current_list = self.get_file_list()
|
||||
titles = [os.path.split(file)[1] for file in current_list]
|
||||
self.application.set_busy_cursor()
|
||||
if not initialLoad:
|
||||
if not initial_load:
|
||||
self.main_window.display_progress_bar(len(files))
|
||||
# Sort the presentations by its filename considering language specific characters.
|
||||
files.sort(cmp=locale_compare,
|
||||
key=lambda filename: os.path.split(unicode(filename))[1])
|
||||
files.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1]))
|
||||
for file in files:
|
||||
if not initialLoad:
|
||||
if not initial_load:
|
||||
self.main_window.increment_progress_bar()
|
||||
if currlist.count(file) > 0:
|
||||
if current_list.count(file) > 0:
|
||||
continue
|
||||
filename = os.path.split(unicode(file))[1]
|
||||
if not os.path.exists(file):
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setIcon(build_icon(ERROR))
|
||||
item_name.setIcon(build_icon(ERROR_IMAGE))
|
||||
item_name.setData(QtCore.Qt.UserRole, file)
|
||||
item_name.setToolTip(file)
|
||||
self.list_view.addItem(item_name)
|
||||
else:
|
||||
if titles.count(filename) > 0:
|
||||
if not initialLoad:
|
||||
if not initial_load:
|
||||
critical_error_message_box(translate('PresentationPlugin.MediaItem', 'File Exists'),
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'A presentation with that filename already exists.')
|
||||
@ -181,7 +182,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
doc = controller.add_document(unicode(file))
|
||||
thumb = os.path.join(doc.get_thumbnail_folder(), u'icon.png')
|
||||
preview = doc.get_thumbnail_path(1, True)
|
||||
if not preview and not initialLoad:
|
||||
if not preview and not initial_load:
|
||||
doc.load_presentation()
|
||||
preview = doc.get_thumbnail_path(1, True)
|
||||
doc.close_presentation()
|
||||
@ -193,7 +194,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
else:
|
||||
icon = create_thumb(preview, thumb)
|
||||
else:
|
||||
if initialLoad:
|
||||
if initial_load:
|
||||
icon = build_icon(u':/general/general_delete.png')
|
||||
else:
|
||||
critical_error_message_box(UiStrings().UnsupportedFile,
|
||||
@ -204,13 +205,13 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
item_name.setIcon(icon)
|
||||
item_name.setToolTip(file)
|
||||
self.list_view.addItem(item_name)
|
||||
if not initialLoad:
|
||||
if not initial_load:
|
||||
self.main_window.finished_progress_bar()
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def on_delete_click(self):
|
||||
"""
|
||||
Remove a presentation item from the list
|
||||
Remove a presentation item from the list.
|
||||
"""
|
||||
if check_item_selected(self.list_view, UiStrings().SelectDelete):
|
||||
items = self.list_view.selectedIndexes()
|
||||
@ -231,12 +232,11 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
self.list_view.takeItem(row)
|
||||
Settings().setValue(self.settings_section + u'/presentations files', self.get_file_list())
|
||||
|
||||
def generate_slide_data(self, service_item, item=None, xmlVersion=False,
|
||||
def generate_slide_data(self, service_item, item=None, xml_version=False,
|
||||
remote=False, context=ServiceItemContext.Service):
|
||||
"""
|
||||
Load the relevant information for displaying the presentation
|
||||
in the slidecontroller. In the case of powerpoints, an image
|
||||
for each slide
|
||||
Load the relevant information for displaying the presentation in the slidecontroller. In the case of
|
||||
powerpoints, an image for each slide.
|
||||
"""
|
||||
if item:
|
||||
items = [item]
|
||||
@ -244,22 +244,20 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
items = self.list_view.selectedItems()
|
||||
if len(items) > 1:
|
||||
return False
|
||||
service_item.title = self.displayTypeComboBox.currentText()
|
||||
service_item.shortname = self.displayTypeComboBox.currentText()
|
||||
service_item.processor = self.display_type_combo_box.currentText()
|
||||
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
|
||||
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
|
||||
shortname = service_item.shortname
|
||||
if not shortname:
|
||||
if not self.display_type_combo_box.currentText():
|
||||
return False
|
||||
for bitem in items:
|
||||
filename = bitem.data(QtCore.Qt.UserRole)
|
||||
if os.path.exists(filename):
|
||||
if shortname == self.Automatic:
|
||||
service_item.shortname = self.findControllerByType(filename)
|
||||
if not service_item.shortname:
|
||||
return False
|
||||
controller = self.controllers[service_item.shortname]
|
||||
(path, name) = os.path.split(filename)
|
||||
service_item.title = name
|
||||
if os.path.exists(filename):
|
||||
if service_item.processor == self.Automatic:
|
||||
service_item.processor = self.findControllerByType(filename)
|
||||
if not service_item.processor:
|
||||
return False
|
||||
controller = self.controllers[service_item.processor]
|
||||
doc = controller.add_document(filename)
|
||||
if doc.get_thumbnail_path(1, True) is None:
|
||||
doc.load_presentation()
|
||||
@ -288,26 +286,24 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
|
||||
def findControllerByType(self, filename):
|
||||
"""
|
||||
Determine the default application controller to use for the selected
|
||||
file type. This is used if "Automatic" is set as the preferred
|
||||
controller. Find the first (alphabetic) enabled controller which
|
||||
"supports" the extension. If none found, then look for a controller
|
||||
which "also supports" it instead.
|
||||
Determine the default application controller to use for the selected file type. This is used if "Automatic" is
|
||||
set as the preferred controller. Find the first (alphabetic) enabled controller which "supports" the extension.
|
||||
If none found, then look for a controller which "also supports" it instead.
|
||||
"""
|
||||
filetype = os.path.splitext(filename)[1][1:]
|
||||
if not filetype:
|
||||
file_type = os.path.splitext(filename)[1][1:]
|
||||
if not file_type:
|
||||
return None
|
||||
for controller in self.controllers:
|
||||
if self.controllers[controller].enabled():
|
||||
if filetype in self.controllers[controller].supports:
|
||||
if file_type in self.controllers[controller].supports:
|
||||
return controller
|
||||
for controller in self.controllers:
|
||||
if self.controllers[controller].enabled():
|
||||
if filetype in self.controllers[controller].alsosupports:
|
||||
if file_type in self.controllers[controller].also_supports:
|
||||
return controller
|
||||
return None
|
||||
|
||||
def search(self, string, showError):
|
||||
def search(self, string, show_error):
|
||||
files = Settings().value(self.settings_section + u'/presentations files')
|
||||
results = []
|
||||
string = string.lower()
|
||||
|
@ -36,10 +36,11 @@ from openlp.core.ui import HideMode
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Controller(object):
|
||||
"""
|
||||
This is the Presentation listener who acts on events from the slide
|
||||
controller and passes the messages on the the correct presentation handlers
|
||||
This is the Presentation listener who acts on events from the slide controller and passes the messages on the the
|
||||
correct presentation handlers.
|
||||
"""
|
||||
log.info(u'Controller loaded')
|
||||
|
||||
@ -54,9 +55,8 @@ class Controller(object):
|
||||
|
||||
def add_handler(self, controller, file, hide_mode, slide_no):
|
||||
"""
|
||||
Add a handler, which is an instance of a presentation and
|
||||
slidecontroller combination. If the slidecontroller has a display
|
||||
then load the presentation.
|
||||
Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller
|
||||
has a display then load the presentation.
|
||||
"""
|
||||
log.debug(u'Live = %s, add_handler %s' % (self.is_live, file))
|
||||
self.controller = controller
|
||||
@ -86,8 +86,7 @@ class Controller(object):
|
||||
|
||||
def activate(self):
|
||||
"""
|
||||
Active the presentation, and show it on the screen.
|
||||
Use the last slide number.
|
||||
Active the presentation, and show it on the screen. Use the last slide number.
|
||||
"""
|
||||
log.debug(u'Live = %s, activate' % self.is_live)
|
||||
if not self.doc:
|
||||
@ -130,7 +129,7 @@ class Controller(object):
|
||||
|
||||
def first(self):
|
||||
"""
|
||||
Based on the handler passed at startup triggers the first slide
|
||||
Based on the handler passed at startup triggers the first slide.
|
||||
"""
|
||||
log.debug(u'Live = %s, first' % self.is_live)
|
||||
if not self.doc:
|
||||
@ -148,7 +147,7 @@ class Controller(object):
|
||||
|
||||
def last(self):
|
||||
"""
|
||||
Based on the handler passed at startup triggers the last slide
|
||||
Based on the handler passed at startup triggers the last slide.
|
||||
"""
|
||||
log.debug(u'Live = %s, last' % self.is_live)
|
||||
if not self.doc:
|
||||
@ -166,7 +165,7 @@ class Controller(object):
|
||||
|
||||
def next(self):
|
||||
"""
|
||||
Based on the handler passed at startup triggers the next slide event
|
||||
Based on the handler passed at startup triggers the next slide event.
|
||||
"""
|
||||
log.debug(u'Live = %s, next' % self.is_live)
|
||||
if not self.doc:
|
||||
@ -182,9 +181,8 @@ class Controller(object):
|
||||
return
|
||||
if not self.activate():
|
||||
return
|
||||
# The "End of slideshow" screen is after the last slide
|
||||
# Note, we can't just stop on the last slide, since it may
|
||||
# contain animations that need to be stepped through.
|
||||
# The "End of slideshow" screen is after the last slide. Note, we can't just stop on the last slide, since it
|
||||
# may contain animations that need to be stepped through.
|
||||
if self.doc.slidenumber > self.doc.get_slide_count():
|
||||
return
|
||||
self.doc.next_step()
|
||||
@ -192,7 +190,7 @@ class Controller(object):
|
||||
|
||||
def previous(self):
|
||||
"""
|
||||
Based on the handler passed at startup triggers the previous slide event
|
||||
Based on the handler passed at startup triggers the previous slide event.
|
||||
"""
|
||||
log.debug(u'Live = %s, previous' % self.is_live)
|
||||
if not self.doc:
|
||||
@ -213,7 +211,7 @@ class Controller(object):
|
||||
|
||||
def shutdown(self):
|
||||
"""
|
||||
Based on the handler passed at startup triggers slide show to shut down
|
||||
Based on the handler passed at startup triggers slide show to shut down.
|
||||
"""
|
||||
log.debug(u'Live = %s, shutdown' % self.is_live)
|
||||
if not self.doc:
|
||||
@ -223,7 +221,7 @@ class Controller(object):
|
||||
|
||||
def blank(self, hide_mode):
|
||||
"""
|
||||
Instruct the controller to blank the presentation
|
||||
Instruct the controller to blank the presentation.
|
||||
"""
|
||||
log.debug(u'Live = %s, blank' % self.is_live)
|
||||
self.hide_mode = hide_mode
|
||||
@ -244,7 +242,7 @@ class Controller(object):
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Instruct the controller to stop and hide the presentation
|
||||
Instruct the controller to stop and hide the presentation.
|
||||
"""
|
||||
log.debug(u'Live = %s, stop' % self.is_live)
|
||||
self.hide_mode = HideMode.Screen
|
||||
@ -260,7 +258,7 @@ class Controller(object):
|
||||
|
||||
def unblank(self):
|
||||
"""
|
||||
Instruct the controller to unblank the presentation
|
||||
Instruct the controller to unblank the presentation.
|
||||
"""
|
||||
log.debug(u'Live = %s, unblank' % self.is_live)
|
||||
self.hide_mode = None
|
||||
@ -283,8 +281,8 @@ class Controller(object):
|
||||
|
||||
class MessageListener(object):
|
||||
"""
|
||||
This is the Presentation listener who acts on events from the slide
|
||||
controller and passes the messages on the the correct presentation handlers
|
||||
This is the Presentation listener who acts on events from the slide controller and passes the messages on the the
|
||||
correct presentation handlers
|
||||
"""
|
||||
log.info(u'Message Listener loaded')
|
||||
|
||||
@ -310,15 +308,14 @@ class MessageListener(object):
|
||||
|
||||
def startup(self, message):
|
||||
"""
|
||||
Start of new presentation
|
||||
Save the handler as any new presentations start here
|
||||
Start of new presentation. Save the handler as any new presentations start here
|
||||
"""
|
||||
log.debug(u'Startup called with message %s' % message)
|
||||
is_live = message[1]
|
||||
item = message[0]
|
||||
log.debug(u'Startup called with message %s' % message)
|
||||
hide_mode = message[2]
|
||||
file = item.get_frame_path()
|
||||
self.handler = item.title
|
||||
self.handler = item.processor
|
||||
if self.handler == self.media_item.Automatic:
|
||||
self.handler = self.media_item.findControllerByType(file)
|
||||
if not self.handler:
|
||||
@ -331,7 +328,7 @@ class MessageListener(object):
|
||||
|
||||
def slide(self, message):
|
||||
"""
|
||||
React to the message to move to a specific slide
|
||||
React to the message to move to a specific slide.
|
||||
"""
|
||||
is_live = message[1]
|
||||
slide = message[2]
|
||||
@ -342,7 +339,7 @@ class MessageListener(object):
|
||||
|
||||
def first(self, message):
|
||||
"""
|
||||
React to the message to move to the first slide
|
||||
React to the message to move to the first slide.
|
||||
"""
|
||||
is_live = message[1]
|
||||
if is_live:
|
||||
@ -352,7 +349,7 @@ class MessageListener(object):
|
||||
|
||||
def last(self, message):
|
||||
"""
|
||||
React to the message to move to the last slide
|
||||
React to the message to move to the last slide.
|
||||
"""
|
||||
is_live = message[1]
|
||||
if is_live:
|
||||
@ -362,7 +359,7 @@ class MessageListener(object):
|
||||
|
||||
def next(self, message):
|
||||
"""
|
||||
React to the message to move to the next animation/slide
|
||||
React to the message to move to the next animation/slide.
|
||||
"""
|
||||
is_live = message[1]
|
||||
if is_live:
|
||||
@ -372,7 +369,7 @@ class MessageListener(object):
|
||||
|
||||
def previous(self, message):
|
||||
"""
|
||||
React to the message to move to the previous animation/slide
|
||||
React to the message to move to the previous animation/slide.
|
||||
"""
|
||||
is_live = message[1]
|
||||
if is_live:
|
||||
@ -382,8 +379,7 @@ class MessageListener(object):
|
||||
|
||||
def shutdown(self, message):
|
||||
"""
|
||||
React to message to shutdown the presentation. I.e. end the show
|
||||
and close the file
|
||||
React to message to shutdown the presentation. I.e. end the show and close the file.
|
||||
"""
|
||||
is_live = message[1]
|
||||
if is_live:
|
||||
@ -393,7 +389,7 @@ class MessageListener(object):
|
||||
|
||||
def hide(self, message):
|
||||
"""
|
||||
React to the message to show the desktop
|
||||
React to the message to show the desktop.
|
||||
"""
|
||||
is_live = message[1]
|
||||
if is_live:
|
||||
@ -401,7 +397,7 @@ class MessageListener(object):
|
||||
|
||||
def blank(self, message):
|
||||
"""
|
||||
React to the message to blank the display
|
||||
React to the message to blank the display.
|
||||
"""
|
||||
is_live = message[1]
|
||||
hide_mode = message[2]
|
||||
@ -410,7 +406,7 @@ class MessageListener(object):
|
||||
|
||||
def unblank(self, message):
|
||||
"""
|
||||
React to the message to unblank the display
|
||||
React to the message to unblank the display.
|
||||
"""
|
||||
is_live = message[1]
|
||||
if is_live:
|
||||
@ -418,9 +414,7 @@ class MessageListener(object):
|
||||
|
||||
def timeout(self):
|
||||
"""
|
||||
The presentation may be timed or might be controlled by the
|
||||
application directly, rather than through OpenLP. Poll occasionally
|
||||
to check which slide is currently displayed so the slidecontroller
|
||||
view can be updated
|
||||
The presentation may be timed or might be controlled by the application directly, rather than through OpenLP.
|
||||
Poll occasionally to check which slide is currently displayed so the slidecontroller view can be updated.
|
||||
"""
|
||||
self.live_handler.poll()
|
||||
|
@ -26,7 +26,10 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
This modul is for controlling powerpiont. PPT API documentation:
|
||||
`http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
|
||||
@ -39,16 +42,14 @@ if os.name == u'nt':
|
||||
from openlp.core.lib import ScreenList
|
||||
from presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# PPT API documentation:
|
||||
# http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx
|
||||
|
||||
class PowerpointController(PresentationController):
|
||||
"""
|
||||
Class to control interactions with PowerPoint Presentations
|
||||
It creates the runtime Environment , Loads the and Closes the Presentation
|
||||
As well as triggering the correct activities based on the users input
|
||||
Class to control interactions with PowerPoint Presentations. It creates the runtime Environment , Loads the and
|
||||
Closes the Presentation. As well as triggering the correct activities based on the users input.
|
||||
"""
|
||||
log.info(u'PowerpointController loaded')
|
||||
|
||||
@ -63,7 +64,7 @@ class PowerpointController(PresentationController):
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
PowerPoint is able to run on this machine
|
||||
PowerPoint is able to run on this machine.
|
||||
"""
|
||||
log.debug(u'check_available')
|
||||
if os.name == u'nt':
|
||||
@ -77,7 +78,7 @@ class PowerpointController(PresentationController):
|
||||
if os.name == u'nt':
|
||||
def start_process(self):
|
||||
"""
|
||||
Loads PowerPoint process
|
||||
Loads PowerPoint process.
|
||||
"""
|
||||
log.debug(u'start_process')
|
||||
if not self.process:
|
||||
@ -87,7 +88,7 @@ class PowerpointController(PresentationController):
|
||||
|
||||
def kill(self):
|
||||
"""
|
||||
Called at system exit to clean up any running presentations
|
||||
Called at system exit to clean up any running presentations.
|
||||
"""
|
||||
log.debug(u'Kill powerpoint')
|
||||
while self.docs:
|
||||
@ -105,12 +106,12 @@ class PowerpointController(PresentationController):
|
||||
|
||||
class PowerpointDocument(PresentationDocument):
|
||||
"""
|
||||
Class which holds information and controls a single presentation
|
||||
Class which holds information and controls a single presentation.
|
||||
"""
|
||||
|
||||
def __init__(self, controller, presentation):
|
||||
"""
|
||||
Constructor, store information about the file and initialise
|
||||
Constructor, store information about the file and initialise.
|
||||
"""
|
||||
log.debug(u'Init Presentation Powerpoint')
|
||||
PresentationDocument.__init__(self, controller, presentation)
|
||||
@ -118,8 +119,8 @@ class PowerpointDocument(PresentationDocument):
|
||||
|
||||
def load_presentation(self):
|
||||
"""
|
||||
Called when a presentation is added to the SlideController.
|
||||
Opens the PowerPoint file using the process created earlier.
|
||||
Called when a presentation is added to the SlideController. Opens the PowerPoint file using the process created
|
||||
earlier.
|
||||
"""
|
||||
log.debug(u'load_presentation')
|
||||
if not self.controller.process or not self.controller.process.Visible:
|
||||
@ -142,20 +143,19 @@ class PowerpointDocument(PresentationDocument):
|
||||
self.presentation.Slides[n].Copy()
|
||||
thumbnail = QApplication.clipboard.image()
|
||||
|
||||
However, for the moment, we want a physical file since it makes life
|
||||
easier elsewhere.
|
||||
However, for the moment, we want a physical file since it makes life easier elsewhere.
|
||||
"""
|
||||
log.debug(u'create_thumbnails')
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
for num in range(self.presentation.Slides.Count):
|
||||
self.presentation.Slides(num + 1).Export(os.path.join(
|
||||
self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240)
|
||||
self.presentation.Slides(num + 1).Export(
|
||||
os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240)
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
Close presentation and clean up objects. This is triggered by a new
|
||||
object being added to SlideController or OpenLP being shut down.
|
||||
Close presentation and clean up objects. This is triggered by a new object being added to SlideController or
|
||||
OpenLP being shut down.
|
||||
"""
|
||||
log.debug(u'ClosePresentation')
|
||||
if self.presentation:
|
||||
@ -182,7 +182,6 @@ class PowerpointDocument(PresentationDocument):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns ``True`` if a presentation is currently active.
|
||||
@ -253,15 +252,14 @@ class PowerpointDocument(PresentationDocument):
|
||||
dpi = win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88)
|
||||
except win32ui.error:
|
||||
dpi = 96
|
||||
rect = ScreenList().current[u'size']
|
||||
size = ScreenList().current[u'size']
|
||||
ppt_window = self.presentation.SlideShowSettings.Run()
|
||||
if not ppt_window:
|
||||
return
|
||||
ppt_window.Top = rect.y() * 72 / dpi
|
||||
ppt_window.Height = rect.height() * 72 / dpi
|
||||
ppt_window.Left = rect.x() * 72 / dpi
|
||||
ppt_window.Width = rect.width() * 72 / dpi
|
||||
|
||||
ppt_window.Top = size.y() * 72 / dpi
|
||||
ppt_window.Height = size.height() * 72 / dpi
|
||||
ppt_window.Left = size.x() * 72 / dpi
|
||||
ppt_window.Width = size.width() * 72 / dpi
|
||||
|
||||
def get_slide_number(self):
|
||||
"""
|
||||
@ -318,6 +316,7 @@ class PowerpointDocument(PresentationDocument):
|
||||
"""
|
||||
return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes)
|
||||
|
||||
|
||||
def _get_text_from_shapes(shapes):
|
||||
"""
|
||||
Returns any text extracted from the shapes on a presentation slide.
|
||||
@ -326,8 +325,8 @@ def _get_text_from_shapes(shapes):
|
||||
A set of shapes to search for text.
|
||||
"""
|
||||
text = ''
|
||||
for idx in range(shapes.Count):
|
||||
shape = shapes(idx + 1)
|
||||
for index in range(shapes.Count):
|
||||
shape = shapes(index + 1)
|
||||
if shape.HasTextFrame:
|
||||
text += shape.TextFrame.TextRange.Text + '\n'
|
||||
return text
|
||||
|
@ -37,13 +37,14 @@ if os.name == u'nt':
|
||||
from openlp.core.lib import ScreenList
|
||||
from presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PptviewController(PresentationController):
|
||||
"""
|
||||
Class to control interactions with PowerPoint Viewer Presentations
|
||||
It creates the runtime Environment , Loads the and Closes the Presentation
|
||||
As well as triggering the correct activities based on the users input
|
||||
Class to control interactions with PowerPoint Viewer Presentations. It creates the runtime Environment , Loads the
|
||||
and Closes the Presentation. As well as triggering the correct activities based on the users input
|
||||
"""
|
||||
log.info(u'PPTViewController loaded')
|
||||
|
||||
@ -58,7 +59,7 @@ class PptviewController(PresentationController):
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
PPT Viewer is able to run on this machine
|
||||
PPT Viewer is able to run on this machine.
|
||||
"""
|
||||
log.debug(u'check_available')
|
||||
if os.name != u'nt':
|
||||
@ -68,7 +69,7 @@ class PptviewController(PresentationController):
|
||||
if os.name == u'nt':
|
||||
def check_installed(self):
|
||||
"""
|
||||
Check the viewer is installed
|
||||
Check the viewer is installed.
|
||||
"""
|
||||
log.debug(u'Check installed')
|
||||
try:
|
||||
@ -79,14 +80,14 @@ class PptviewController(PresentationController):
|
||||
|
||||
def start_process(self):
|
||||
"""
|
||||
Loads the PPTVIEWLIB library
|
||||
Loads the PPTVIEWLIB library.
|
||||
"""
|
||||
if self.process:
|
||||
return
|
||||
log.debug(u'start PPTView')
|
||||
dllpath = os.path.join(self.plugin_manager.base_path, u'presentations', u'lib', u'pptviewlib',
|
||||
u'pptviewlib.dll')
|
||||
self.process = cdll.LoadLibrary(dllpath)
|
||||
dll_path = os.path.join(
|
||||
self.plugin_manager.base_path, u'presentations', u'lib', u'pptviewlib', u'pptviewlib.dll')
|
||||
self.process = cdll.LoadLibrary(dll_path)
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
self.process.SetDebug(1)
|
||||
|
||||
@ -101,33 +102,32 @@ class PptviewController(PresentationController):
|
||||
|
||||
class PptviewDocument(PresentationDocument):
|
||||
"""
|
||||
Class which holds information and controls a single presentation
|
||||
Class which holds information and controls a single presentation.
|
||||
"""
|
||||
def __init__(self, controller, presentation):
|
||||
"""
|
||||
Constructor, store information about the file and initialise
|
||||
Constructor, store information about the file and initialise.
|
||||
"""
|
||||
log.debug(u'Init Presentation PowerPoint')
|
||||
PresentationDocument.__init__(self, controller, presentation)
|
||||
self.presentation = None
|
||||
self.pptid = None
|
||||
self.ppt_id = None
|
||||
self.blanked = False
|
||||
self.hidden = False
|
||||
|
||||
def load_presentation(self):
|
||||
"""
|
||||
Called when a presentation is added to the SlideController.
|
||||
It builds the environment, starts communication with the background
|
||||
PptView task started earlier.
|
||||
Called when a presentation is added to the SlideController. It builds the environment, starts communication with
|
||||
the background PptView task started earlier.
|
||||
"""
|
||||
log.debug(u'LoadPresentation')
|
||||
rect = ScreenList().current[u'size']
|
||||
rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom())
|
||||
size = ScreenList().current[u'size']
|
||||
rect = RECT(size.x(), size.y(), size.right(), size.bottom())
|
||||
filepath = str(self.filepath.replace(u'/', u'\\'))
|
||||
if not os.path.isdir(self.get_temp_folder()):
|
||||
os.makedirs(self.get_temp_folder())
|
||||
self.pptid = self.controller.process.OpenPPT(filepath, None, rect, str(self.get_temp_folder()) + '\\slide')
|
||||
if self.pptid >= 0:
|
||||
self.ppt_id = self.controller.process.OpenPPT(filepath, None, rect, str(self.get_temp_folder()) + '\\slide')
|
||||
if self.ppt_id >= 0:
|
||||
self.create_thumbnails()
|
||||
self.stop_presentation()
|
||||
return True
|
||||
@ -136,8 +136,7 @@ class PptviewDocument(PresentationDocument):
|
||||
|
||||
def create_thumbnails(self):
|
||||
"""
|
||||
PPTviewLib creates large BMP's, but we want small PNG's for consistency.
|
||||
Convert them here.
|
||||
PPTviewLib creates large BMP's, but we want small PNG's for consistency. Convert them here.
|
||||
"""
|
||||
log.debug(u'create_thumbnails')
|
||||
if self.check_thumbnails():
|
||||
@ -149,21 +148,20 @@ class PptviewDocument(PresentationDocument):
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
Close presentation and clean up objects
|
||||
Triggered by new object being added to SlideController orOpenLP
|
||||
being shut down
|
||||
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
|
||||
shut down.
|
||||
"""
|
||||
log.debug(u'ClosePresentation')
|
||||
if self.controller.process:
|
||||
self.controller.process.ClosePPT(self.pptid)
|
||||
self.pptid = -1
|
||||
self.controller.process.ClosePPT(self.ppt_id)
|
||||
self.ppt_id = -1
|
||||
self.controller.remove_doc(self)
|
||||
|
||||
def is_loaded(self):
|
||||
"""
|
||||
Returns true if a presentation is loaded
|
||||
Returns true if a presentation is loaded.
|
||||
"""
|
||||
if self.pptid < 0:
|
||||
if self.ppt_id < 0:
|
||||
return False
|
||||
if self.get_slide_count() < 0:
|
||||
return False
|
||||
@ -171,74 +169,74 @@ class PptviewDocument(PresentationDocument):
|
||||
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns true if a presentation is currently active
|
||||
Returns true if a presentation is currently active.
|
||||
"""
|
||||
return self.is_loaded() and not self.hidden
|
||||
|
||||
def blank_screen(self):
|
||||
"""
|
||||
Blanks the screen
|
||||
Blanks the screen.
|
||||
"""
|
||||
self.controller.process.Blank(self.pptid)
|
||||
self.controller.process.Blank(self.ppt_id)
|
||||
self.blanked = True
|
||||
|
||||
def unblank_screen(self):
|
||||
"""
|
||||
Unblanks (restores) the presentation
|
||||
Unblanks (restores) the presentation.
|
||||
"""
|
||||
self.controller.process.Unblank(self.pptid)
|
||||
self.controller.process.Unblank(self.ppt_id)
|
||||
self.blanked = False
|
||||
|
||||
def is_blank(self):
|
||||
"""
|
||||
Returns true if screen is blank
|
||||
Returns true if screen is blank.
|
||||
"""
|
||||
log.debug(u'is blank OpenOffice')
|
||||
return self.blanked
|
||||
|
||||
def stop_presentation(self):
|
||||
"""
|
||||
Stops the current presentation and hides the output
|
||||
Stops the current presentation and hides the output.
|
||||
"""
|
||||
self.hidden = True
|
||||
self.controller.process.Stop(self.pptid)
|
||||
self.controller.process.Stop(self.ppt_id)
|
||||
|
||||
def start_presentation(self):
|
||||
"""
|
||||
Starts a presentation from the beginning
|
||||
Starts a presentation from the beginning.
|
||||
"""
|
||||
if self.hidden:
|
||||
self.hidden = False
|
||||
self.controller.process.Resume(self.pptid)
|
||||
self.controller.process.Resume(self.ppt_id)
|
||||
else:
|
||||
self.controller.process.RestartShow(self.pptid)
|
||||
self.controller.process.RestartShow(self.ppt_id)
|
||||
|
||||
def get_slide_number(self):
|
||||
"""
|
||||
Returns the current slide number
|
||||
Returns the current slide number.
|
||||
"""
|
||||
return self.controller.process.GetCurrentSlide(self.pptid)
|
||||
return self.controller.process.GetCurrentSlide(self.ppt_id)
|
||||
|
||||
def get_slide_count(self):
|
||||
"""
|
||||
Returns total number of slides
|
||||
Returns total number of slides.
|
||||
"""
|
||||
return self.controller.process.GetSlideCount(self.pptid)
|
||||
return self.controller.process.GetSlideCount(self.ppt_id)
|
||||
|
||||
def goto_slide(self, slideno):
|
||||
"""
|
||||
Moves to a specific slide in the presentation
|
||||
Moves to a specific slide in the presentation.
|
||||
"""
|
||||
self.controller.process.GotoSlide(self.pptid, slideno)
|
||||
self.controller.process.GotoSlide(self.ppt_id, slideno)
|
||||
|
||||
def next_step(self):
|
||||
"""
|
||||
Triggers the next effect of slide on the running presentation
|
||||
Triggers the next effect of slide on the running presentation.
|
||||
"""
|
||||
self.controller.process.NextStep(self.pptid)
|
||||
self.controller.process.NextStep(self.ppt_id)
|
||||
|
||||
def previous_step(self):
|
||||
"""
|
||||
Triggers the previous slide on the running presentation
|
||||
Triggers the previous slide on the running presentation.
|
||||
"""
|
||||
self.controller.process.PrevStep(self.pptid)
|
||||
self.controller.process.PrevStep(self.ppt_id)
|
||||
|
@ -38,11 +38,11 @@ from openlp.core.utils import AppLocation
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PresentationDocument(object):
|
||||
"""
|
||||
Base class for presentation documents to inherit from.
|
||||
Loads and closes the presentation as well as triggering the correct
|
||||
activities based on the users input
|
||||
Base class for presentation documents to inherit from. Loads and closes the presentation as well as triggering the
|
||||
correct activities based on the users input
|
||||
|
||||
**Hook Functions**
|
||||
|
||||
@ -131,20 +131,17 @@ class PresentationDocument(object):
|
||||
"""
|
||||
The location where thumbnail images will be stored
|
||||
"""
|
||||
return os.path.join(
|
||||
self.controller.thumbnail_folder, self.get_file_name())
|
||||
return os.path.join(self.controller.thumbnail_folder, self.get_file_name())
|
||||
|
||||
def get_temp_folder(self):
|
||||
"""
|
||||
The location where thumbnail images will be stored
|
||||
"""
|
||||
return os.path.join(
|
||||
self.controller.temp_folder, self.get_file_name())
|
||||
return os.path.join(self.controller.temp_folder, self.get_file_name())
|
||||
|
||||
def check_thumbnails(self):
|
||||
"""
|
||||
Returns ``True`` if the thumbnail images exist and are more recent than
|
||||
the powerpoint file.
|
||||
Returns ``True`` if the thumbnail images exist and are more recent than the powerpoint file.
|
||||
"""
|
||||
lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
|
||||
if not (lastimage and os.path.isfile(lastimage)):
|
||||
@ -153,8 +150,7 @@ class PresentationDocument(object):
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
Close presentation and clean up objects
|
||||
Triggered by new object being added to SlideController
|
||||
Close presentation and clean up objects. Triggered by new object being added to SlideController
|
||||
"""
|
||||
self.controller.close_presentation()
|
||||
|
||||
@ -223,8 +219,8 @@ class PresentationDocument(object):
|
||||
|
||||
def next_step(self):
|
||||
"""
|
||||
Triggers the next effect of slide on the running presentation
|
||||
This might be the next animation on the current slide, or the next slide
|
||||
Triggers the next effect of slide on the running presentation. This might be the next animation on the current
|
||||
slide, or the next slide
|
||||
"""
|
||||
pass
|
||||
|
||||
@ -236,8 +232,7 @@ class PresentationDocument(object):
|
||||
|
||||
def convert_thumbnail(self, file, idx):
|
||||
"""
|
||||
Convert the slide image the application made to a standard 320x240
|
||||
.png image.
|
||||
Convert the slide image the application made to a standard 320x240 .png image.
|
||||
"""
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
@ -297,17 +292,14 @@ class PresentationDocument(object):
|
||||
|
||||
class PresentationController(object):
|
||||
"""
|
||||
This class is used to control interactions with presentation applications
|
||||
by creating a runtime environment. This is a base class for presentation
|
||||
controllers to inherit from.
|
||||
This class is used to control interactions with presentation applications by creating a runtime environment. This is
|
||||
a base class for presentation controllers to inherit from.
|
||||
|
||||
To create a new controller, take a copy of this file and name it so it ends
|
||||
with ``controller.py``, i.e. ``foobarcontroller.py``. Make sure it inherits
|
||||
:class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`,
|
||||
and then fill in the blanks. If possible try to make sure it loads on all
|
||||
platforms, usually by using :mod:``os.name`` checks, although
|
||||
``__init__``, ``check_available`` and ``presentation_deleted`` should
|
||||
always be implemented.
|
||||
To create a new controller, take a copy of this file and name it so it ends with ``controller.py``, i.e.
|
||||
``foobarcontroller.py``. Make sure it inherits
|
||||
:class:`~openlp.plugins.presentations.lib.presentationcontroller.PresentationController`, and then fill in the
|
||||
blanks. If possible try to make sure it loads on all platforms, usually by using :mod:``os.name`` checks, although
|
||||
``__init__``, ``check_available`` and ``presentation_deleted`` should always be implemented.
|
||||
|
||||
See :class:`~openlp.plugins.presentations.lib.impresscontroller.ImpressController`,
|
||||
:class:`~openlp.plugins.presentations.lib.powerpointcontroller.PowerpointController` or
|
||||
@ -317,36 +309,34 @@ class PresentationController(object):
|
||||
**Basic Attributes**
|
||||
|
||||
``name``
|
||||
The name that appears in the options and the media manager
|
||||
The name that appears in the options and the media manager.
|
||||
|
||||
``enabled``
|
||||
The controller is enabled
|
||||
The controller is enabled.
|
||||
|
||||
``available``
|
||||
The controller is available on this machine. Set by init via
|
||||
call to check_available
|
||||
The controller is available on this machine. Set by init via call to check_available.
|
||||
|
||||
``plugin``
|
||||
The presentationplugin object
|
||||
The presentationplugin object.
|
||||
|
||||
``supports``
|
||||
The primary native file types this application supports
|
||||
The primary native file types this application supports.
|
||||
|
||||
``alsosupports``
|
||||
Other file types the application can import, although not necessarily
|
||||
the first choice due to potential incompatibilities
|
||||
``also_supports``
|
||||
Other file types the application can import, although not necessarily the first choice due to potential
|
||||
incompatibilities.
|
||||
|
||||
**Hook Functions**
|
||||
|
||||
``kill()``
|
||||
Called at system exit to clean up any running presentations
|
||||
Called at system exit to clean up any running presentations.
|
||||
|
||||
``check_available()``
|
||||
Returns True if presentation application is installed/can run on this
|
||||
machine
|
||||
Returns True if presentation application is installed/can run on this machine.
|
||||
|
||||
``presentation_deleted()``
|
||||
Deletes presentation specific files, e.g. thumbnails
|
||||
Deletes presentation specific files, e.g. thumbnails.
|
||||
|
||||
"""
|
||||
log.info(u'PresentationController loaded')
|
||||
@ -354,9 +344,8 @@ class PresentationController(object):
|
||||
def __init__(self, plugin=None, name=u'PresentationController',
|
||||
document_class=PresentationDocument):
|
||||
"""
|
||||
This is the constructor for the presentationcontroller object. This
|
||||
provides an easy way for descendent plugins to populate common data.
|
||||
This method *must* be overridden, like so::
|
||||
This is the constructor for the presentationcontroller object. This provides an easy way for descendent plugins
|
||||
to populate common data. This method *must* be overridden, like so::
|
||||
|
||||
class MyPresentationController(PresentationController):
|
||||
def __init__(self, plugin):
|
||||
@ -370,7 +359,7 @@ class PresentationController(object):
|
||||
Name of the application, to appear in the application
|
||||
"""
|
||||
self.supports = []
|
||||
self.alsosupports = []
|
||||
self.also_supports = []
|
||||
self.docs = []
|
||||
self.plugin = plugin
|
||||
self.name = name
|
||||
@ -399,28 +388,26 @@ class PresentationController(object):
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
Presentation app is able to run on this machine
|
||||
Presentation app is able to run on this machine.
|
||||
"""
|
||||
return False
|
||||
|
||||
def start_process(self):
|
||||
"""
|
||||
Loads a running version of the presentation application in the
|
||||
background.
|
||||
Loads a running version of the presentation application in the background.
|
||||
"""
|
||||
pass
|
||||
|
||||
def kill(self):
|
||||
"""
|
||||
Called at system exit to clean up any running presentations and
|
||||
close the application
|
||||
Called at system exit to clean up any running presentations and close the application.
|
||||
"""
|
||||
log.debug(u'Kill')
|
||||
self.close_presentation()
|
||||
|
||||
def add_document(self, name):
|
||||
"""
|
||||
Called when a new presentation document is opened
|
||||
Called when a new presentation document is opened.
|
||||
"""
|
||||
document = self.document_class(self, name)
|
||||
self.docs.append(document)
|
||||
@ -428,7 +415,7 @@ class PresentationController(object):
|
||||
|
||||
def remove_doc(self, doc=None):
|
||||
"""
|
||||
Called to remove an open document from the collection
|
||||
Called to remove an open document from the collection.
|
||||
"""
|
||||
log.debug(u'remove_doc Presentation')
|
||||
if doc is None:
|
||||
|
@ -91,8 +91,7 @@ class PresentationTab(SettingsTab):
|
||||
if checkbox.isEnabled():
|
||||
checkbox.setText(controller.name)
|
||||
else:
|
||||
checkbox.setText(
|
||||
translate('PresentationPlugin.PresentationTab', '%s (unavailable)') % controller.name)
|
||||
checkbox.setText(translate('PresentationPlugin.PresentationTab', '%s (unavailable)') % controller.name)
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
@ -106,8 +105,8 @@ class PresentationTab(SettingsTab):
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save the settings. If the tab hasn't been made visible to the user then there is nothing to do,
|
||||
so exit. This removes the need to start presentation applications unnecessarily.
|
||||
Save the settings. If the tab hasn't been made visible to the user then there is nothing to do, so exit. This
|
||||
removes the need to start presentation applications unnecessarily.
|
||||
"""
|
||||
if not self.activated:
|
||||
return
|
||||
|
@ -27,8 +27,8 @@
|
||||
# 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:`presentationplugin` module provides the ability for OpenLP to display presentations from a variety of document
|
||||
formats.
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
@ -39,8 +39,10 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate
|
||||
from openlp.core.utils import AppLocation
|
||||
from openlp.plugins.presentations.lib import PresentationController, PresentationMediaItem, PresentationTab
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
__default_settings__ = {
|
||||
u'presentations/override app': QtCore.Qt.Unchecked,
|
||||
u'presentations/Impress': QtCore.Qt.Checked,
|
||||
@ -52,9 +54,8 @@ __default_settings__ = {
|
||||
|
||||
class PresentationPlugin(Plugin):
|
||||
"""
|
||||
This plugin allowed a Presentation to be opened, controlled and displayed
|
||||
on the output display. The plugin controls third party applications such
|
||||
as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer
|
||||
This plugin allowed a Presentation to be opened, controlled and displayed on the output display. The plugin controls
|
||||
third party applications such as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer.
|
||||
"""
|
||||
log = logging.getLogger(u'PresentationPlugin')
|
||||
|
||||
@ -69,18 +70,16 @@ class PresentationPlugin(Plugin):
|
||||
self.icon_path = u':/plugins/plugin_presentations.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
||||
def create_settings_Tab(self, parent):
|
||||
def create_settings_tab(self, parent):
|
||||
"""
|
||||
Create the settings Tab
|
||||
Create the settings Tab.
|
||||
"""
|
||||
visible_name = self.get_string(StringContent.VisibleName)
|
||||
self.settings_tab = PresentationTab(parent, self.name, visible_name[u'title'], self.controllers,
|
||||
self.icon_path)
|
||||
self.settings_tab = PresentationTab(parent, self.name, visible_name[u'title'], self.controllers, self.icon_path)
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Initialise the plugin. Determine which controllers are enabled
|
||||
are start their processes.
|
||||
Initialise the plugin. Determine which controllers are enabled are start their processes.
|
||||
"""
|
||||
log.info(u'Presentations Initialising')
|
||||
Plugin.initialise(self)
|
||||
@ -95,8 +94,8 @@ class PresentationPlugin(Plugin):
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Finalise the plugin. Ask all the enabled presentation applications
|
||||
to close down their applications and release resources.
|
||||
Finalise the plugin. Ask all the enabled presentation applications to close down their applications and release
|
||||
resources.
|
||||
"""
|
||||
log.info(u'Plugin Finalise')
|
||||
# Ask each controller to tidy up.
|
||||
@ -108,26 +107,23 @@ class PresentationPlugin(Plugin):
|
||||
|
||||
def create_media_manager_item(self):
|
||||
"""
|
||||
Create the Media Manager List
|
||||
Create the Media Manager List.
|
||||
"""
|
||||
self.media_item = PresentationMediaItem(
|
||||
self.main_window.media_dock_manager.media_dock, self, self.icon, self.controllers)
|
||||
|
||||
def register_controllers(self, controller):
|
||||
"""
|
||||
Register each presentation controller (Impress, PPT etc) and store for later use
|
||||
Register each presentation controller (Impress, PPT etc) and store for later use.
|
||||
"""
|
||||
self.controllers[controller.name] = controller
|
||||
|
||||
def check_pre_conditions(self):
|
||||
"""
|
||||
Check to see if we have any presentation software available
|
||||
If Not do not install the plugin.
|
||||
Check to see if we have any presentation software available. If not do not install the plugin.
|
||||
"""
|
||||
log.debug(u'check_pre_conditions')
|
||||
controller_dir = os.path.join(
|
||||
AppLocation.get_directory(AppLocation.PluginsDir),
|
||||
u'presentations', u'lib')
|
||||
controller_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'presentations', u'lib')
|
||||
for filename in os.listdir(controller_dir):
|
||||
if filename.endswith(u'controller.py') and not filename == 'presentationcontroller.py':
|
||||
path = os.path.join(controller_dir, filename)
|
||||
@ -146,7 +142,7 @@ class PresentationPlugin(Plugin):
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
Return information about this plugin
|
||||
Return information about this plugin.
|
||||
"""
|
||||
about_text = translate('PresentationPlugin', '<strong>Presentation '
|
||||
'Plugin</strong><br />The presentation plugin provides the '
|
||||
@ -157,7 +153,7 @@ class PresentationPlugin(Plugin):
|
||||
|
||||
def set_plugin_text_strings(self):
|
||||
"""
|
||||
Called to define all translatable texts of the plugin
|
||||
Called to define all translatable texts of the plugin.
|
||||
"""
|
||||
## Name PluginList ##
|
||||
self.text_strings[StringContent.Name] = {
|
||||
|
39
openlp/plugins/remotes/html/live.css
Normal file
39
openlp/plugins/remotes/html/live.css
Normal file
@ -0,0 +1,39 @@
|
||||
/******************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2013 Raoul Snyman *
|
||||
* Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan *
|
||||
* Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, *
|
||||
* Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. *
|
||||
* Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, *
|
||||
* Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, *
|
||||
* Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, *
|
||||
* Frode Woldsund, Martin Zibricky *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* 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 *
|
||||
******************************************************************************/
|
||||
body {
|
||||
background-color: black;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.size {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
41
openlp/plugins/remotes/html/live.html
Normal file
41
openlp/plugins/remotes/html/live.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>${live_title}</title>
|
||||
<link rel="stylesheet" href="/files/live.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/files/images/favicon.ico">
|
||||
<script type="text/javascript" src="/files/jquery.js"></script>
|
||||
<script type="text/javascript" src="/files/live.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="image" class="size"/>
|
||||
</body>
|
||||
</html>
|
52
openlp/plugins/remotes/html/live.js
Normal file
52
openlp/plugins/remotes/html/live.js
Normal file
@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2013 Raoul Snyman *
|
||||
* Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan *
|
||||
* Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, *
|
||||
* Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. *
|
||||
* Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, *
|
||||
* Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, *
|
||||
* Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, *
|
||||
* Frode Woldsund, Martin Zibricky *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* 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 *
|
||||
******************************************************************************/
|
||||
window.OpenLP = {
|
||||
loadSlide: function (event) {
|
||||
$.getJSON(
|
||||
"/live/image",
|
||||
function (data, status) {
|
||||
var img = document.getElementById('image');
|
||||
img.src = data.results.slide_image;
|
||||
img.style.display = 'block';
|
||||
}
|
||||
);
|
||||
},
|
||||
pollServer: function () {
|
||||
$.getJSON(
|
||||
"/live/poll",
|
||||
function (data, status) {
|
||||
if (OpenLP.slideCount != data.results.slide_count) {
|
||||
OpenLP.slideCount = data.results.slide_count;
|
||||
OpenLP.loadSlide();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
$.ajaxSetup({ cache: false });
|
||||
setInterval("OpenLP.pollServer();", 500);
|
||||
OpenLP.pollServer();
|
||||
|
@ -147,7 +147,7 @@ window.OpenLP = {
|
||||
},
|
||||
pollServer: function () {
|
||||
$.getJSON(
|
||||
"/api/poll",
|
||||
"/stage/poll",
|
||||
function (data, status) {
|
||||
var prevItem = OpenLP.currentItem;
|
||||
OpenLP.currentSlide = data.results.slide;
|
||||
|
@ -26,7 +26,7 @@
|
||||
window.OpenLP = {
|
||||
loadService: function (event) {
|
||||
$.getJSON(
|
||||
"/api/service/list",
|
||||
"/stage/service/list",
|
||||
function (data, status) {
|
||||
OpenLP.nextSong = "";
|
||||
$("#notes").html("");
|
||||
@ -46,7 +46,7 @@ window.OpenLP = {
|
||||
},
|
||||
loadSlides: function (event) {
|
||||
$.getJSON(
|
||||
"/api/controller/live/text",
|
||||
"/stage/controller/live/text",
|
||||
function (data, status) {
|
||||
OpenLP.currentSlides = data.results.slides;
|
||||
OpenLP.currentSlide = 0;
|
||||
@ -137,7 +137,7 @@ window.OpenLP = {
|
||||
},
|
||||
pollServer: function () {
|
||||
$.getJSON(
|
||||
"/api/poll",
|
||||
"/stage/poll",
|
||||
function (data, status) {
|
||||
OpenLP.updateClock(data);
|
||||
if (OpenLP.currentItem != data.results.item ||
|
||||
|
@ -43,7 +43,7 @@ the remotes.
|
||||
``/files/{filename}``
|
||||
Serve a static file.
|
||||
|
||||
``/api/poll``
|
||||
``/stage/api/poll``
|
||||
Poll to see if there are any changes. Returns a JSON-encoded dict of
|
||||
any changes that occurred::
|
||||
|
||||
@ -119,122 +119,218 @@ import os
|
||||
import re
|
||||
import urllib
|
||||
import urlparse
|
||||
import cherrypy
|
||||
|
||||
from PyQt4 import QtCore, QtNetwork
|
||||
from mako.template import Template
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Registry, Settings, PluginStatus, StringContent
|
||||
|
||||
from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte
|
||||
from openlp.core.utils import AppLocation, translate
|
||||
|
||||
from cherrypy._cpcompat import sha, ntob
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HttpResponse(object):
|
||||
def make_sha_hash(password):
|
||||
"""
|
||||
A simple object to encapsulate a pseudo-http response.
|
||||
Create an encrypted password for the given password.
|
||||
"""
|
||||
code = '200 OK'
|
||||
content = ''
|
||||
headers = {
|
||||
'Content-Type': 'text/html; charset="utf-8"\r\n'
|
||||
}
|
||||
log.debug("make_sha_hash")
|
||||
return sha(ntob(password)).hexdigest()
|
||||
|
||||
def __init__(self, content='', headers=None, code=None):
|
||||
if headers is None:
|
||||
headers = {}
|
||||
self.content = content
|
||||
for key, value in headers.iteritems():
|
||||
self.headers[key] = value
|
||||
if code:
|
||||
self.code = code
|
||||
|
||||
def fetch_password(username):
|
||||
"""
|
||||
Fetch the password for a provided user.
|
||||
"""
|
||||
log.debug("Fetch Password")
|
||||
if username != Settings().value(u'remotes/user id'):
|
||||
return None
|
||||
return make_sha_hash(Settings().value(u'remotes/password'))
|
||||
|
||||
|
||||
class HttpServer(object):
|
||||
"""
|
||||
Ability to control OpenLP via a web browser.
|
||||
This class controls the Cherrypy server and configuration.
|
||||
"""
|
||||
def __init__(self, plugin):
|
||||
_cp_config = {
|
||||
'tools.sessions.on': True,
|
||||
'tools.auth.on': True
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialise the http server, and start the server.
|
||||
"""
|
||||
log.debug(u'Initialise httpserver')
|
||||
self.plugin = plugin
|
||||
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
|
||||
self.connections = []
|
||||
self.start_tcp()
|
||||
self.settings_section = u'remotes'
|
||||
self.router = HttpRouter()
|
||||
|
||||
def start_tcp(self):
|
||||
def start_server(self):
|
||||
"""
|
||||
Start the http server, use the port in the settings default to 4316.
|
||||
Listen out for slide and song changes so they can be broadcast to
|
||||
clients. Listen out for socket connections.
|
||||
Start the http server based on configuration.
|
||||
"""
|
||||
log.debug(u'Start TCP server')
|
||||
port = Settings().value(self.plugin.settings_section + u'/port')
|
||||
address = Settings().value(self.plugin.settings_section + u'/ip address')
|
||||
self.server = QtNetwork.QTcpServer()
|
||||
self.server.listen(QtNetwork.QHostAddress(address), port)
|
||||
self.server.newConnection.connect(self.new_connection)
|
||||
log.debug(u'TCP listening on port %d' % port)
|
||||
log.debug(u'Start CherryPy server')
|
||||
# Define to security levels and inject the router code
|
||||
self.root = self.Public()
|
||||
self.root.files = self.Files()
|
||||
self.root.stage = self.Stage()
|
||||
self.root.live = self.Live()
|
||||
self.root.router = self.router
|
||||
self.root.files.router = self.router
|
||||
self.root.stage.router = self.router
|
||||
self.root.live.router = self.router
|
||||
cherrypy.tree.mount(self.root, '/', config=self.define_config())
|
||||
# Turn off the flood of access messages cause by poll
|
||||
cherrypy.log.access_log.propagate = False
|
||||
cherrypy.engine.start()
|
||||
|
||||
def new_connection(self):
|
||||
def define_config(self):
|
||||
"""
|
||||
A new http connection has been made. Create a client object to handle
|
||||
communication.
|
||||
Define the configuration of the server.
|
||||
"""
|
||||
log.debug(u'new http connection')
|
||||
socket = self.server.nextPendingConnection()
|
||||
if socket:
|
||||
self.connections.append(HttpConnection(self, socket))
|
||||
if Settings().value(self.settings_section + u'/https enabled'):
|
||||
port = Settings().value(self.settings_section + u'/https port')
|
||||
address = Settings().value(self.settings_section + u'/ip address')
|
||||
local_data = AppLocation.get_directory(AppLocation.DataDir)
|
||||
cherrypy.config.update({u'server.socket_host': str(address),
|
||||
u'server.socket_port': port,
|
||||
u'server.ssl_certificate': os.path.join(local_data, u'remotes', u'openlp.crt'),
|
||||
u'server.ssl_private_key': os.path.join(local_data, u'remotes', u'openlp.key')})
|
||||
else:
|
||||
port = Settings().value(self.settings_section + u'/port')
|
||||
address = Settings().value(self.settings_section + u'/ip address')
|
||||
cherrypy.config.update({u'server.socket_host': str(address)})
|
||||
cherrypy.config.update({u'server.socket_port': port})
|
||||
cherrypy.config.update({u'environment': u'embedded'})
|
||||
cherrypy.config.update({u'engine.autoreload_on': False})
|
||||
directory_config = {u'/': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': Settings().value(u'remotes/authentication enabled'),
|
||||
u'tools.basic_auth.realm': u'OpenLP Remote Login',
|
||||
u'tools.basic_auth.users': fetch_password,
|
||||
u'tools.basic_auth.encrypt': make_sha_hash},
|
||||
u'/files': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False},
|
||||
u'/stage': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False},
|
||||
u'/live': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False}}
|
||||
return directory_config
|
||||
|
||||
def close_connection(self, connection):
|
||||
class Public(object):
|
||||
"""
|
||||
The connection has been closed. Clean up
|
||||
Main access class with may have security enabled on it.
|
||||
"""
|
||||
log.debug(u'close http connection')
|
||||
if connection in self.connections:
|
||||
self.connections.remove(connection)
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
self.router.request_data = None
|
||||
if isinstance(kwargs, dict):
|
||||
self.router.request_data = kwargs.get(u'data', None)
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
class Files(object):
|
||||
"""
|
||||
Provides access to files and has no security available. These are read only accesses
|
||||
"""
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
class Stage(object):
|
||||
"""
|
||||
Stage view is read only so security is not relevant and would reduce it's usability
|
||||
"""
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
class Live(object):
|
||||
"""
|
||||
Live view is read only so security is not relevant and would reduce it's usability
|
||||
"""
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close down the http server.
|
||||
"""
|
||||
log.debug(u'close http server')
|
||||
self.server.close()
|
||||
cherrypy.engine.exit()
|
||||
|
||||
|
||||
class HttpConnection(object):
|
||||
class HttpRouter(object):
|
||||
"""
|
||||
A single connection, this handles communication between the server
|
||||
and the client.
|
||||
This code is called by the HttpServer upon a request and it processes it based on the routing table.
|
||||
"""
|
||||
def __init__(self, parent, socket):
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialise the http connection. Listen out for socket signals.
|
||||
Initialise the router
|
||||
"""
|
||||
log.debug(u'Initialise HttpConnection: %s' % socket.peerAddress())
|
||||
self.socket = socket
|
||||
self.parent = parent
|
||||
self.routes = [
|
||||
(u'^/$', self.serve_file),
|
||||
(u'^/(stage)$', self.serve_file),
|
||||
(u'^/(live)$', self.serve_file),
|
||||
(r'^/files/(.*)$', self.serve_file),
|
||||
(r'^/api/poll$', self.poll),
|
||||
(r'^/stage/poll$', self.poll),
|
||||
(r'^/live/poll$', self.live_poll),
|
||||
(r'^/live/image$', self.live_image),
|
||||
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/stage/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/api/service/(.*)$', self.service),
|
||||
(r'^/stage/service/(.*)$', self.service),
|
||||
(r'^/api/display/(hide|show|blank|theme|desktop)$', self.display),
|
||||
(r'^/api/alert$', self.alert),
|
||||
(r'^/api/plugin/(search)$', self.pluginInfo),
|
||||
(r'^/api/plugin/(search)$', self.plugin_info),
|
||||
(r'^/api/(.*)/search$', self.search),
|
||||
(r'^/api/(.*)/live$', self.go_live),
|
||||
(r'^/api/(.*)/add$', self.add_to_service)
|
||||
]
|
||||
self.socket.readyRead.connect(self.ready_read)
|
||||
self.socket.disconnected.connect(self.disconnected)
|
||||
self.translate()
|
||||
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
|
||||
|
||||
def process_http_request(self, url_path, *args):
|
||||
"""
|
||||
Common function to process HTTP requests
|
||||
|
||||
``url_path``
|
||||
The requested URL.
|
||||
|
||||
``*args``
|
||||
Any passed data.
|
||||
"""
|
||||
response = None
|
||||
for route, func in self.routes:
|
||||
match = re.match(route, url_path)
|
||||
if match:
|
||||
log.debug('Route "%s" matched "%s"', route, url_path)
|
||||
args = []
|
||||
for param in match.groups():
|
||||
args.append(param)
|
||||
response = func(*args)
|
||||
break
|
||||
if response:
|
||||
return response
|
||||
else:
|
||||
log.debug('Path not found %s', url_path)
|
||||
return self._http_not_found()
|
||||
|
||||
def _get_service_items(self):
|
||||
"""
|
||||
Read the service item in use and return the data as a json object
|
||||
"""
|
||||
service_items = []
|
||||
if self.live_controller.service_item:
|
||||
current_unique_identifier = self.live_controller.service_item.unique_identifier
|
||||
@ -258,6 +354,7 @@ class HttpConnection(object):
|
||||
self.template_vars = {
|
||||
'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Remote'),
|
||||
'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Stage View'),
|
||||
'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 Live View'),
|
||||
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
|
||||
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
|
||||
'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
|
||||
@ -281,57 +378,24 @@ class HttpConnection(object):
|
||||
'slides': translate('RemotePlugin.Mobile', 'Slides')
|
||||
}
|
||||
|
||||
def ready_read(self):
|
||||
"""
|
||||
Data has been sent from the client. Respond to it
|
||||
"""
|
||||
log.debug(u'ready to read socket')
|
||||
if self.socket.canReadLine():
|
||||
data = str(self.socket.readLine())
|
||||
try:
|
||||
log.debug(u'received: ' + data)
|
||||
except UnicodeDecodeError:
|
||||
# Malicious request containing non-ASCII characters.
|
||||
self.close()
|
||||
return
|
||||
words = data.split(' ')
|
||||
response = None
|
||||
if words[0] == u'GET':
|
||||
url = urlparse.urlparse(words[1])
|
||||
self.url_params = urlparse.parse_qs(url.query)
|
||||
# Loop through the routes we set up earlier and execute them
|
||||
for route, func in self.routes:
|
||||
match = re.match(route, url.path)
|
||||
if match:
|
||||
log.debug('Route "%s" matched "%s"', route, url.path)
|
||||
args = []
|
||||
for param in match.groups():
|
||||
args.append(param)
|
||||
response = func(*args)
|
||||
break
|
||||
if response:
|
||||
self.send_response(response)
|
||||
else:
|
||||
self.send_response(HttpResponse(code='404 Not Found'))
|
||||
self.close()
|
||||
|
||||
def serve_file(self, filename=None):
|
||||
"""
|
||||
Send a file to the socket. For now, just a subset of file types
|
||||
and must be top level inside the html folder.
|
||||
Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder.
|
||||
If subfolders requested return 404, easier for security for the present.
|
||||
|
||||
Ultimately for i18n, this could first look for xx/file.html before
|
||||
falling back to file.html... where xx is the language, e.g. 'en'
|
||||
Ultimately for i18n, this could first look for xx/file.html before falling back to file.html.
|
||||
where xx is the language, e.g. 'en'
|
||||
"""
|
||||
log.debug(u'serve file request %s' % filename)
|
||||
if not filename:
|
||||
filename = u'index.html'
|
||||
elif filename == u'stage':
|
||||
filename = u'stage.html'
|
||||
path = os.path.normpath(os.path.join(self.parent.html_dir, filename))
|
||||
if not path.startswith(self.parent.html_dir):
|
||||
return HttpResponse(code=u'404 Not Found')
|
||||
elif filename == u'live':
|
||||
filename = u'live.html'
|
||||
path = os.path.normpath(os.path.join(self.html_dir, filename))
|
||||
if not path.startswith(self.html_dir):
|
||||
return self._http_not_found()
|
||||
ext = os.path.splitext(filename)[1]
|
||||
html = None
|
||||
if ext == u'.html':
|
||||
@ -360,11 +424,12 @@ class HttpConnection(object):
|
||||
content = file_handle.read()
|
||||
except IOError:
|
||||
log.exception(u'Failed to open %s' % path)
|
||||
return HttpResponse(code=u'404 Not Found')
|
||||
return self._http_not_found()
|
||||
finally:
|
||||
if file_handle:
|
||||
file_handle.close()
|
||||
return HttpResponse(content, {u'Content-Type': mimetype})
|
||||
cherrypy.response.headers['Content-Type'] = mimetype
|
||||
return content
|
||||
|
||||
def poll(self):
|
||||
"""
|
||||
@ -379,18 +444,40 @@ class HttpConnection(object):
|
||||
u'theme': self.live_controller.theme_screen.isChecked(),
|
||||
u'display': self.live_controller.desktop_screen.isChecked()
|
||||
}
|
||||
return HttpResponse(json.dumps({u'results': result}), {u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': result})
|
||||
|
||||
def live_poll(self):
|
||||
"""
|
||||
Poll OpenLP to determine the current slide count.
|
||||
"""
|
||||
result = {
|
||||
u'slide_count': self.live_controller.slide_count
|
||||
}
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': result})
|
||||
|
||||
def live_image(self):
|
||||
"""
|
||||
Return the latest display image as a byte stream.
|
||||
"""
|
||||
result = {
|
||||
u'slide_image': u'data:image/png;base64,' + str(image_to_byte(self.live_controller.slide_image))
|
||||
}
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': result})
|
||||
|
||||
def display(self, action):
|
||||
"""
|
||||
Hide or show the display screen.
|
||||
This is a cross Thread call and UI is updated so Events need to be used.
|
||||
|
||||
``action``
|
||||
This is the action, either ``hide`` or ``show``.
|
||||
"""
|
||||
Registry().execute(u'slidecontroller_toggle_display', action)
|
||||
return HttpResponse(json.dumps({u'results': {u'success': True}}),
|
||||
{u'Content-Type': u'application/json'})
|
||||
self.live_controller.emit(QtCore.SIGNAL(u'slidecontroller_toggle_display'), action)
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'success': True}})
|
||||
|
||||
def alert(self):
|
||||
"""
|
||||
@ -399,16 +486,16 @@ class HttpConnection(object):
|
||||
plugin = self.plugin_manager.get_plugin_by_name("alerts")
|
||||
if plugin.status == PluginStatus.Active:
|
||||
try:
|
||||
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
|
||||
text = json.loads(self.request_data)[u'request'][u'text']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
text = urllib.unquote(text)
|
||||
Registry().execute(u'alerts_text', [text])
|
||||
self.alerts_manager.emit(QtCore.SIGNAL(u'alerts_text'), [text])
|
||||
success = True
|
||||
else:
|
||||
success = False
|
||||
return HttpResponse(json.dumps({u'results': {u'success': success}}),
|
||||
{u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'success': success}})
|
||||
|
||||
def controller(self, display_type, action):
|
||||
"""
|
||||
@ -444,44 +531,44 @@ class HttpConnection(object):
|
||||
if current_item:
|
||||
json_data[u'results'][u'item'] = self.live_controller.service_item.unique_identifier
|
||||
else:
|
||||
if self.url_params and self.url_params.get(u'data'):
|
||||
if self.request_data:
|
||||
try:
|
||||
data = json.loads(self.url_params[u'data'][0])
|
||||
data = json.loads(self.request_data)[u'request'][u'id']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
log.info(data)
|
||||
# This slot expects an int within a list.
|
||||
id = data[u'request'][u'id']
|
||||
Registry().execute(event, [id])
|
||||
self.live_controller.emit(QtCore.SIGNAL(event), [data])
|
||||
else:
|
||||
Registry().execute(event)
|
||||
self.live_controller.emit(QtCore.SIGNAL(event))
|
||||
json_data = {u'results': {u'success': True}}
|
||||
return HttpResponse(json.dumps(json_data), {u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps(json_data)
|
||||
|
||||
def service(self, action):
|
||||
"""
|
||||
Handles requests for service items
|
||||
Handles requests for service items in the service manager
|
||||
|
||||
``action``
|
||||
The action to perform.
|
||||
"""
|
||||
event = u'servicemanager_%s' % action
|
||||
if action == u'list':
|
||||
return HttpResponse(json.dumps({u'results': {u'items': self._get_service_items()}}),
|
||||
{u'Content-Type': u'application/json'})
|
||||
else:
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'items': self._get_service_items()}})
|
||||
event += u'_item'
|
||||
if self.url_params and self.url_params.get(u'data'):
|
||||
if self.request_data:
|
||||
try:
|
||||
data = json.loads(self.url_params[u'data'][0])
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
Registry().execute(event, data[u'request'][u'id'])
|
||||
data = json.loads(self.request_data)[u'request'][u'id']
|
||||
except KeyError:
|
||||
return self._http_bad_request()
|
||||
self.service_manager.emit(QtCore.SIGNAL(event), data)
|
||||
else:
|
||||
Registry().execute(event)
|
||||
return HttpResponse(json.dumps({u'results': {u'success': True}}), {u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'success': True}})
|
||||
|
||||
def pluginInfo(self, action):
|
||||
def plugin_info(self, action):
|
||||
"""
|
||||
Return plugin related information, based on the action.
|
||||
|
||||
@ -493,8 +580,9 @@ class HttpConnection(object):
|
||||
searches = []
|
||||
for plugin in self.plugin_manager.plugins:
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
searches.append([plugin.name, unicode(plugin.textStrings[StringContent.Name][u'plural'])])
|
||||
return HttpResponse(json.dumps({u'results': {u'items': searches}}), {u'Content-Type': u'application/json'})
|
||||
searches.append([plugin.name, unicode(plugin.text_strings[StringContent.Name][u'plural'])])
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'items': searches}})
|
||||
|
||||
def search(self, plugin_name):
|
||||
"""
|
||||
@ -504,69 +592,63 @@ class HttpConnection(object):
|
||||
The plugin name to search in.
|
||||
"""
|
||||
try:
|
||||
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
|
||||
text = json.loads(self.request_data)[u'request'][u'text']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
text = urllib.unquote(text)
|
||||
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
else:
|
||||
results = []
|
||||
return HttpResponse(json.dumps({u'results': {u'items': results}}), {u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'items': results}})
|
||||
|
||||
def go_live(self, plugin_name):
|
||||
"""
|
||||
Go live on an item of type ``plugin``.
|
||||
"""
|
||||
try:
|
||||
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
|
||||
id = json.loads(self.request_data)[u'request'][u'id']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.go_live(id, remote=True)
|
||||
return HttpResponse(code=u'200 OK')
|
||||
plugin.media_item.emit(QtCore.SIGNAL(u'%s_go_live' % plugin_name), [id, True])
|
||||
return self._http_success()
|
||||
|
||||
def add_to_service(self, plugin_name):
|
||||
"""
|
||||
Add item of type ``plugin_name`` to the end of the service.
|
||||
"""
|
||||
try:
|
||||
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
|
||||
id = json.loads(self.request_data)[u'request'][u'id']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.createItemFromId(id)
|
||||
plugin.media_item.add_to_service(item_id, remote=True)
|
||||
return HttpResponse(code=u'200 OK')
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.emit(QtCore.SIGNAL(u'%s_add_to_service' % plugin_name), [item_id, True])
|
||||
self._http_success()
|
||||
|
||||
def send_response(self, response):
|
||||
http = u'HTTP/1.1 %s\r\n' % response.code
|
||||
for header, value in response.headers.iteritems():
|
||||
http += '%s: %s\r\n' % (header, value)
|
||||
http += '\r\n'
|
||||
self.socket.write(http)
|
||||
self.socket.write(response.content)
|
||||
def _http_success(self):
|
||||
"""
|
||||
Set the HTTP success return code.
|
||||
"""
|
||||
cherrypy.response.status = 200
|
||||
|
||||
def disconnected(self):
|
||||
def _http_bad_request(self):
|
||||
"""
|
||||
The client has disconnected. Tidy up
|
||||
Set the HTTP bad response return code.
|
||||
"""
|
||||
log.debug(u'socket disconnected')
|
||||
self.close()
|
||||
cherrypy.response.status = 400
|
||||
|
||||
def close(self):
|
||||
def _http_not_found(self):
|
||||
"""
|
||||
The server has closed the connection. Tidy up
|
||||
Set the HTTP not found return code.
|
||||
"""
|
||||
if not self.socket:
|
||||
return
|
||||
log.debug(u'close socket')
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
self.parent.close_connection(self)
|
||||
cherrypy.response.status = 404
|
||||
cherrypy.response.body = ["<html><body>Sorry, an error occurred </body></html>"]
|
||||
|
||||
def _get_service_manager(self):
|
||||
"""
|
||||
@ -597,3 +679,13 @@ class HttpConnection(object):
|
||||
return self._plugin_manager
|
||||
|
||||
plugin_manager = property(_get_plugin_manager)
|
||||
|
||||
def _get_alerts_manager(self):
|
||||
"""
|
||||
Adds the alerts manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, u'_alerts_manager'):
|
||||
self._alerts_manager = Registry().get(u'alerts_manager')
|
||||
return self._alerts_manager
|
||||
|
||||
alerts_manager = property(_get_alerts_manager)
|
||||
|
@ -27,9 +27,12 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import os.path
|
||||
|
||||
from PyQt4 import QtCore, QtGui, QtNetwork
|
||||
|
||||
from openlp.core.lib import Registry, Settings, SettingsTab, translate
|
||||
from openlp.core.lib import Settings, SettingsTab, translate
|
||||
from openlp.core.utils import AppLocation
|
||||
|
||||
|
||||
ZERO_URL = u'0.0.0.0'
|
||||
@ -53,32 +56,96 @@ class RemoteTab(SettingsTab):
|
||||
self.address_label.setObjectName(u'address_label')
|
||||
self.address_edit = QtGui.QLineEdit(self.server_settings_group_box)
|
||||
self.address_edit.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
|
||||
self.address_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(
|
||||
u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), self))
|
||||
self.address_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'),
|
||||
self))
|
||||
self.address_edit.setObjectName(u'address_edit')
|
||||
self.server_settings_layout.addRow(self.address_label, self.address_edit)
|
||||
self.twelve_hour_check_box = QtGui.QCheckBox(self.server_settings_group_box)
|
||||
self.twelve_hour_check_box.setObjectName(u'twelve_hour_check_box')
|
||||
self.server_settings_layout.addRow(self.twelve_hour_check_box)
|
||||
self.port_label = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.left_layout.addWidget(self.server_settings_group_box)
|
||||
self.http_settings_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.http_settings_group_box.setObjectName(u'http_settings_group_box')
|
||||
self.http_setting_layout = QtGui.QFormLayout(self.http_settings_group_box)
|
||||
self.http_setting_layout.setObjectName(u'http_setting_layout')
|
||||
self.port_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.port_label.setObjectName(u'port_label')
|
||||
self.port_spin_box = QtGui.QSpinBox(self.server_settings_group_box)
|
||||
self.port_spin_box = QtGui.QSpinBox(self.http_settings_group_box)
|
||||
self.port_spin_box.setMaximum(32767)
|
||||
self.port_spin_box.setObjectName(u'port_spin_box')
|
||||
self.server_settings_layout.addRow(self.port_label, self.port_spin_box)
|
||||
self.remote_url_label = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.http_setting_layout.addRow(self.port_label, self.port_spin_box)
|
||||
self.remote_url_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.remote_url_label.setObjectName(u'remote_url_label')
|
||||
self.remote_url = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.remote_url = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.remote_url.setObjectName(u'remote_url')
|
||||
self.remote_url.setOpenExternalLinks(True)
|
||||
self.server_settings_layout.addRow(self.remote_url_label, self.remote_url)
|
||||
self.stage_url_label = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.http_setting_layout.addRow(self.remote_url_label, self.remote_url)
|
||||
self.stage_url_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.stage_url_label.setObjectName(u'stage_url_label')
|
||||
self.stage_url = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.stage_url = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.stage_url.setObjectName(u'stage_url')
|
||||
self.stage_url.setOpenExternalLinks(True)
|
||||
self.server_settings_layout.addRow(self.stage_url_label, self.stage_url)
|
||||
self.left_layout.addWidget(self.server_settings_group_box)
|
||||
self.http_setting_layout.addRow(self.stage_url_label, self.stage_url)
|
||||
self.live_url_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.live_url_label.setObjectName(u'live_url_label')
|
||||
self.live_url = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.live_url.setObjectName(u'live_url')
|
||||
self.live_url.setOpenExternalLinks(True)
|
||||
self.http_setting_layout.addRow(self.live_url_label, self.live_url)
|
||||
self.left_layout.addWidget(self.http_settings_group_box)
|
||||
self.https_settings_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.https_settings_group_box.setCheckable(True)
|
||||
self.https_settings_group_box.setChecked(False)
|
||||
self.https_settings_group_box.setObjectName(u'https_settings_group_box')
|
||||
self.https_settings_layout = QtGui.QFormLayout(self.https_settings_group_box)
|
||||
self.https_settings_layout.setObjectName(u'https_settings_layout')
|
||||
self.https_error_label = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.https_error_label.setVisible(False)
|
||||
self.https_error_label.setWordWrap(True)
|
||||
self.https_error_label.setObjectName(u'https_error_label')
|
||||
self.https_settings_layout.addRow(self.https_error_label)
|
||||
self.https_port_label = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.https_port_label.setObjectName(u'https_port_label')
|
||||
self.https_port_spin_box = QtGui.QSpinBox(self.https_settings_group_box)
|
||||
self.https_port_spin_box.setMaximum(32767)
|
||||
self.https_port_spin_box.setObjectName(u'https_port_spin_box')
|
||||
self.https_settings_layout.addRow(self.https_port_label, self.https_port_spin_box)
|
||||
self.remote_https_url = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.remote_https_url.setObjectName(u'remote_http_url')
|
||||
self.remote_https_url.setOpenExternalLinks(True)
|
||||
self.remote_https_url_label = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.remote_https_url_label.setObjectName(u'remote_http_url_label')
|
||||
self.https_settings_layout.addRow(self.remote_https_url_label, self.remote_https_url)
|
||||
self.stage_https_url_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.stage_https_url_label.setObjectName(u'stage_https_url_label')
|
||||
self.stage_https_url = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.stage_https_url.setObjectName(u'stage_https_url')
|
||||
self.stage_https_url.setOpenExternalLinks(True)
|
||||
self.https_settings_layout.addRow(self.stage_https_url_label, self.stage_https_url)
|
||||
self.live_https_url_label = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.live_https_url_label.setObjectName(u'live_url_label')
|
||||
self.live_https_url = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.live_https_url.setObjectName(u'live_https_url')
|
||||
self.live_https_url.setOpenExternalLinks(True)
|
||||
self.https_settings_layout.addRow(self.live_https_url_label, self.live_https_url)
|
||||
self.left_layout.addWidget(self.https_settings_group_box)
|
||||
self.user_login_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.user_login_group_box.setCheckable(True)
|
||||
self.user_login_group_box.setChecked(False)
|
||||
self.user_login_group_box.setObjectName(u'user_login_group_box')
|
||||
self.user_login_layout = QtGui.QFormLayout(self.user_login_group_box)
|
||||
self.user_login_layout.setObjectName(u'user_login_layout')
|
||||
self.user_id_label = QtGui.QLabel(self.user_login_group_box)
|
||||
self.user_id_label.setObjectName(u'user_id_label')
|
||||
self.user_id = QtGui.QLineEdit(self.user_login_group_box)
|
||||
self.user_id.setObjectName(u'user_id')
|
||||
self.user_login_layout.addRow(self.user_id_label, self.user_id)
|
||||
self.password_label = QtGui.QLabel(self.user_login_group_box)
|
||||
self.password_label.setObjectName(u'password_label')
|
||||
self.password = QtGui.QLineEdit(self.user_login_group_box)
|
||||
self.password.setObjectName(u'password')
|
||||
self.user_login_layout.addRow(self.password_label, self.password)
|
||||
self.left_layout.addWidget(self.user_login_group_box)
|
||||
self.android_app_group_box = QtGui.QGroupBox(self.right_column)
|
||||
self.android_app_group_box.setObjectName(u'android_app_group_box')
|
||||
self.right_layout.addWidget(self.android_app_group_box)
|
||||
@ -96,9 +163,11 @@ class RemoteTab(SettingsTab):
|
||||
self.qr_layout.addWidget(self.qr_description_label)
|
||||
self.left_layout.addStretch()
|
||||
self.right_layout.addStretch()
|
||||
self.twelve_hour_check_box.stateChanged.connect(self.onTwelveHourCheckBoxChanged)
|
||||
self.twelve_hour_check_box.stateChanged.connect(self.on_twelve_hour_check_box_changed)
|
||||
self.address_edit.textChanged.connect(self.set_urls)
|
||||
self.port_spin_box.valueChanged.connect(self.set_urls)
|
||||
self.https_port_spin_box.valueChanged.connect(self.set_urls)
|
||||
self.https_settings_group_box.clicked.connect(self.https_changed)
|
||||
|
||||
def retranslateUi(self):
|
||||
self.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings'))
|
||||
@ -106,14 +175,29 @@ class RemoteTab(SettingsTab):
|
||||
self.port_label.setText(translate('RemotePlugin.RemoteTab', 'Port number:'))
|
||||
self.remote_url_label.setText(translate('RemotePlugin.RemoteTab', 'Remote URL:'))
|
||||
self.stage_url_label.setText(translate('RemotePlugin.RemoteTab', 'Stage view URL:'))
|
||||
self.live_url_label.setText(translate('RemotePlugin.RemoteTab', 'Live view URL:'))
|
||||
self.twelve_hour_check_box.setText(translate('RemotePlugin.RemoteTab', 'Display stage time in 12h format'))
|
||||
self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App'))
|
||||
self.qr_description_label.setText(translate('RemotePlugin.RemoteTab',
|
||||
'Scan the QR code or click <a href="https://play.google.com/store/'
|
||||
'apps/details?id=org.openlp.android">download</a> to install the '
|
||||
'Android app from Google Play.'))
|
||||
self.https_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'HTTPS Server'))
|
||||
self.https_error_label.setText(translate('RemotePlugin.RemoteTab',
|
||||
'Could not find an SSL certificate. The HTTPS server will not be available unless an SSL certificate '
|
||||
'is found. Please see the manual for more information.'))
|
||||
self.https_port_label.setText(self.port_label.text())
|
||||
self.remote_https_url_label.setText(self.remote_url_label.text())
|
||||
self.stage_https_url_label.setText(self.stage_url_label.text())
|
||||
self.live_https_url_label.setText(self.live_url_label.text())
|
||||
self.user_login_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'User Authentication'))
|
||||
self.user_id_label.setText(translate('RemotePlugin.RemoteTab', 'User id:'))
|
||||
self.password_label.setText(translate('RemotePlugin.RemoteTab', 'Password:'))
|
||||
|
||||
def set_urls(self):
|
||||
"""
|
||||
Update the display based on the data input on the screen
|
||||
"""
|
||||
ip_address = u'localhost'
|
||||
if self.address_edit.text() == ZERO_URL:
|
||||
interfaces = QtNetwork.QNetworkInterface.allInterfaces()
|
||||
@ -129,31 +213,77 @@ class RemoteTab(SettingsTab):
|
||||
break
|
||||
else:
|
||||
ip_address = self.address_edit.text()
|
||||
url = u'http://%s:%s/' % (ip_address, self.port_spin_box.value())
|
||||
self.remote_url.setText(u'<a href="%s">%s</a>' % (url, url))
|
||||
url += u'stage'
|
||||
self.stage_url.setText(u'<a href="%s">%s</a>' % (url, url))
|
||||
http_url = u'http://%s:%s/' % (ip_address, self.port_spin_box.value())
|
||||
https_url = u'https://%s:%s/' % (ip_address, self.https_port_spin_box.value())
|
||||
self.remote_url.setText(u'<a href="%s">%s</a>' % (http_url, http_url))
|
||||
self.remote_https_url.setText(u'<a href="%s">%s</a>' % (https_url, https_url))
|
||||
http_url_temp = http_url + u'stage'
|
||||
https_url_temp = https_url + u'stage'
|
||||
self.stage_url.setText(u'<a href="%s">%s</a>' % (http_url_temp, http_url_temp))
|
||||
self.stage_https_url.setText(u'<a href="%s">%s</a>' % (https_url_temp, https_url_temp))
|
||||
http_url_temp = http_url + u'live'
|
||||
https_url_temp = https_url + u'live'
|
||||
self.live_url.setText(u'<a href="%s">%s</a>' % (http_url_temp, http_url_temp))
|
||||
self.live_https_url.setText(u'<a href="%s">%s</a>' % (https_url_temp, https_url_temp))
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the configuration and update the server configuration if necessary
|
||||
"""
|
||||
self.port_spin_box.setValue(Settings().value(self.settings_section + u'/port'))
|
||||
self.https_port_spin_box.setValue(Settings().value(self.settings_section + u'/https port'))
|
||||
self.address_edit.setText(Settings().value(self.settings_section + u'/ip address'))
|
||||
self.twelve_hour = Settings().value(self.settings_section + u'/twelve hour')
|
||||
self.twelve_hour_check_box.setChecked(self.twelve_hour)
|
||||
local_data = AppLocation.get_directory(AppLocation.DataDir)
|
||||
if not os.path.exists(os.path.join(local_data, u'remotes', u'openlp.crt')) or \
|
||||
not os.path.exists(os.path.join(local_data, u'remotes', u'openlp.key')):
|
||||
self.https_settings_group_box.setChecked(False)
|
||||
self.https_settings_group_box.setEnabled(False)
|
||||
self.https_error_label.setVisible(True)
|
||||
else:
|
||||
self.https_settings_group_box.setChecked(Settings().value(self.settings_section + u'/https enabled'))
|
||||
self.https_settings_group_box.setEnabled(True)
|
||||
self.https_error_label.setVisible(False)
|
||||
self.user_login_group_box.setChecked(Settings().value(self.settings_section + u'/authentication enabled'))
|
||||
self.user_id.setText(Settings().value(self.settings_section + u'/user id'))
|
||||
self.password.setText(Settings().value(self.settings_section + u'/password'))
|
||||
self.set_urls()
|
||||
self.https_changed()
|
||||
|
||||
def save(self):
|
||||
changed = False
|
||||
"""
|
||||
Save the configuration and update the server configuration if necessary
|
||||
"""
|
||||
if Settings().value(self.settings_section + u'/ip address') != self.address_edit.text() or \
|
||||
Settings().value(self.settings_section + u'/port') != self.port_spin_box.value():
|
||||
changed = True
|
||||
Settings().value(self.settings_section + u'/port') != self.port_spin_box.value() or \
|
||||
Settings().value(self.settings_section + u'/https port') != self.https_port_spin_box.value() or \
|
||||
Settings().value(self.settings_section + u'/https enabled') != \
|
||||
self.https_settings_group_box.isChecked() or \
|
||||
Settings().value(self.settings_section + u'/authentication enabled') != \
|
||||
self.user_login_group_box.isChecked():
|
||||
self.settings_form.register_post_process(u'remotes_config_updated')
|
||||
Settings().setValue(self.settings_section + u'/port', self.port_spin_box.value())
|
||||
Settings().setValue(self.settings_section + u'/https port', self.https_port_spin_box.value())
|
||||
Settings().setValue(self.settings_section + u'/https enabled', self.https_settings_group_box.isChecked())
|
||||
Settings().setValue(self.settings_section + u'/ip address', self.address_edit.text())
|
||||
Settings().setValue(self.settings_section + u'/twelve hour', self.twelve_hour)
|
||||
if changed:
|
||||
Registry().execute(u'remotes_config_updated')
|
||||
Settings().setValue(self.settings_section + u'/authentication enabled', self.user_login_group_box.isChecked())
|
||||
Settings().setValue(self.settings_section + u'/user id', self.user_id.text())
|
||||
Settings().setValue(self.settings_section + u'/password', self.password.text())
|
||||
|
||||
def onTwelveHourCheckBoxChanged(self, check_state):
|
||||
def on_twelve_hour_check_box_changed(self, check_state):
|
||||
"""
|
||||
Toggle the 12 hour check box.
|
||||
"""
|
||||
self.twelve_hour = False
|
||||
# we have a set value convert to True/False
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
self.twelve_hour = True
|
||||
|
||||
def https_changed(self):
|
||||
"""
|
||||
Invert the HTTP group box based on Https group settings
|
||||
"""
|
||||
self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked())
|
||||
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, translate, build_icon
|
||||
from openlp.plugins.remotes.lib import RemoteTab, HttpServer
|
||||
|
||||
@ -37,6 +39,11 @@ log = logging.getLogger(__name__)
|
||||
__default_settings__ = {
|
||||
u'remotes/twelve hour': True,
|
||||
u'remotes/port': 4316,
|
||||
u'remotes/https port': 4317,
|
||||
u'remotes/https enabled': False,
|
||||
u'remotes/user id': u'openlp',
|
||||
u'remotes/password': u'password',
|
||||
u'remotes/authentication enabled': False,
|
||||
u'remotes/ip address': u'0.0.0.0'
|
||||
}
|
||||
|
||||
@ -60,7 +67,8 @@ class RemotesPlugin(Plugin):
|
||||
"""
|
||||
log.debug(u'initialise')
|
||||
Plugin.initialise(self)
|
||||
self.server = HttpServer(self)
|
||||
self.server = HttpServer()
|
||||
self.server.start_server()
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
@ -70,6 +78,7 @@ class RemotesPlugin(Plugin):
|
||||
Plugin.finalise(self)
|
||||
if self.server:
|
||||
self.server.close()
|
||||
self.server = None
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
@ -99,5 +108,6 @@ class RemotesPlugin(Plugin):
|
||||
"""
|
||||
Called when Config is changed to restart the server on new address or port
|
||||
"""
|
||||
self.finalise()
|
||||
self.initialise()
|
||||
log.debug(u'remote config changed')
|
||||
self.main_window.information_message(translate('RemotePlugin', 'Configuration Change'),
|
||||
translate('RemotePlugin', 'OpenLP will need to be restarted for the Remote changes to become active.'))
|
||||
|
@ -52,3 +52,4 @@ This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
|
||||
them separate from the functionality, so that it is easier to recreate the GUI
|
||||
from the .ui files later if necessary.
|
||||
"""
|
||||
from editsongform import EditSongForm
|
||||
|
358
openlp/plugins/songs/forms/duplicatesongremovalform.py
Normal file
358
openlp/plugins/songs/forms/duplicatesongremovalform.py
Normal file
@ -0,0 +1,358 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The duplicate song removal logic for OpenLP.
|
||||
"""
|
||||
from __future__ import division
|
||||
import logging
|
||||
import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Registry, translate
|
||||
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.utils import AppLocation
|
||||
from openlp.plugins.songs.lib import delete_song
|
||||
from openlp.plugins.songs.lib.db import Song, MediaFile
|
||||
from openlp.plugins.songs.forms.songreviewwidget import SongReviewWidget
|
||||
from openlp.plugins.songs.lib.songcompare import songs_probably_equal
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class DuplicateSongRemovalForm(OpenLPWizard):
|
||||
"""
|
||||
This is the Duplicate Song Removal Wizard. It provides functionality to
|
||||
search for and remove duplicate songs in the database.
|
||||
"""
|
||||
log.info(u'DuplicateSongRemovalForm loaded')
|
||||
|
||||
def __init__(self, plugin):
|
||||
"""
|
||||
Instantiate the wizard, and run any extra setup we need to.
|
||||
|
||||
``parent``
|
||||
The QWidget-derived parent of the wizard.
|
||||
|
||||
``plugin``
|
||||
The songs plugin.
|
||||
"""
|
||||
self.duplicate_song_list = []
|
||||
self.review_current_count = 0
|
||||
self.review_total_count = 0
|
||||
# Used to interrupt ongoing searches when cancel is clicked.
|
||||
self.break_search = False
|
||||
OpenLPWizard.__init__(self, self.main_window, plugin, u'duplicateSongRemovalWizard',
|
||||
u':/wizards/wizard_duplicateremoval.bmp', False)
|
||||
self.setMinimumWidth(730)
|
||||
|
||||
def custom_signals(self):
|
||||
"""
|
||||
Song wizard specific signals.
|
||||
"""
|
||||
self.finish_button.clicked.connect(self.on_wizard_exit)
|
||||
self.cancel_button.clicked.connect(self.on_wizard_exit)
|
||||
|
||||
def add_custom_pages(self):
|
||||
"""
|
||||
Add song wizard specific pages.
|
||||
"""
|
||||
# Add custom pages.
|
||||
self.searching_page = QtGui.QWizardPage()
|
||||
self.searching_page.setObjectName(u'searching_page')
|
||||
self.searching_vertical_layout = QtGui.QVBoxLayout(self.searching_page)
|
||||
self.searching_vertical_layout.setObjectName(u'searching_vertical_layout')
|
||||
self.duplicate_search_progress_bar = QtGui.QProgressBar(self.searching_page)
|
||||
self.duplicate_search_progress_bar.setObjectName(u'duplicate_search_progress_bar')
|
||||
self.duplicate_search_progress_bar.setFormat(WizardStrings.PercentSymbolFormat)
|
||||
self.searching_vertical_layout.addWidget(self.duplicate_search_progress_bar)
|
||||
self.found_duplicates_edit = QtGui.QPlainTextEdit(self.searching_page)
|
||||
self.found_duplicates_edit.setUndoRedoEnabled(False)
|
||||
self.found_duplicates_edit.setReadOnly(True)
|
||||
self.found_duplicates_edit.setObjectName(u'found_duplicates_edit')
|
||||
self.searching_vertical_layout.addWidget(self.found_duplicates_edit)
|
||||
self.searching_page_id = self.addPage(self.searching_page)
|
||||
self.review_page = QtGui.QWizardPage()
|
||||
self.review_page.setObjectName(u'review_page')
|
||||
self.review_layout = QtGui.QVBoxLayout(self.review_page)
|
||||
self.review_layout.setObjectName(u'review_layout')
|
||||
self.review_scroll_area = QtGui.QScrollArea(self.review_page)
|
||||
self.review_scroll_area.setObjectName(u'review_scroll_area')
|
||||
self.review_scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
self.review_scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
|
||||
self.review_scroll_area.setWidgetResizable(True)
|
||||
self.review_scroll_area_widget = QtGui.QWidget(self.review_scroll_area)
|
||||
self.review_scroll_area_widget.setObjectName(u'review_scroll_area_widget')
|
||||
self.review_scroll_area_layout = QtGui.QHBoxLayout(self.review_scroll_area_widget)
|
||||
self.review_scroll_area_layout.setObjectName(u'review_scroll_area_layout')
|
||||
self.review_scroll_area_layout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize)
|
||||
self.review_scroll_area_layout.setMargin(0)
|
||||
self.review_scroll_area_layout.setSpacing(0)
|
||||
self.review_scroll_area.setWidget(self.review_scroll_area_widget)
|
||||
self.review_layout.addWidget(self.review_scroll_area)
|
||||
self.review_page_id = self.addPage(self.review_page)
|
||||
# Add a dummy page to the end, to prevent the finish button to appear and the next button do disappear on the
|
||||
#review page.
|
||||
self.dummy_page = QtGui.QWizardPage()
|
||||
self.dummy_page_id = self.addPage(self.dummy_page)
|
||||
|
||||
def retranslateUi(self):
|
||||
"""
|
||||
Song wizard localisation.
|
||||
"""
|
||||
self.setWindowTitle(translate(u'Wizard', u'Wizard'))
|
||||
self.title_label.setText(WizardStrings.HeaderStyle % translate(u'OpenLP.Ui',
|
||||
u'Welcome to the Duplicate Song Removal Wizard'))
|
||||
self.information_label.setText(translate("Wizard",
|
||||
u'This wizard will help you to remove duplicate songs from the song database. You will have a chance to '
|
||||
u'review every potential duplicate song before it is deleted. So no songs will be deleted without your '
|
||||
u'explicit approval.'))
|
||||
self.searching_page.setTitle(translate(u'Wizard', u'Searching for duplicate songs.'))
|
||||
self.searching_page.setSubTitle(translate(u'Wizard', u'Please wait while your songs database is analyzed.'))
|
||||
self.update_review_counter_text()
|
||||
self.review_page.setSubTitle(translate(u'Wizard',
|
||||
u'Here you can decide which songs to remove and which ones to keep.'))
|
||||
|
||||
def update_review_counter_text(self):
|
||||
"""
|
||||
Set the wizard review page header text.
|
||||
"""
|
||||
self.review_page.setTitle(translate(u'Wizard', u'Review duplicate songs (%s/%s)') % \
|
||||
(self.review_current_count, self.review_total_count))
|
||||
|
||||
def custom_page_changed(self, page_id):
|
||||
"""
|
||||
Called when changing the wizard page.
|
||||
|
||||
``page_id``
|
||||
ID of the page the wizard changed to.
|
||||
"""
|
||||
# Hide back button.
|
||||
self.button(QtGui.QWizard.BackButton).hide()
|
||||
if page_id == self.searching_page_id:
|
||||
self.application.set_busy_cursor()
|
||||
try:
|
||||
self.button(QtGui.QWizard.NextButton).hide()
|
||||
# Search duplicate songs.
|
||||
max_songs = self.plugin.manager.get_object_count(Song)
|
||||
if max_songs == 0 or max_songs == 1:
|
||||
self.duplicate_search_progress_bar.setMaximum(1)
|
||||
self.duplicate_search_progress_bar.setValue(1)
|
||||
self.notify_no_duplicates()
|
||||
return
|
||||
# With x songs we have x*(x - 1) / 2 comparisons.
|
||||
max_progress_count = max_songs * (max_songs - 1) // 2
|
||||
self.duplicate_search_progress_bar.setMaximum(max_progress_count)
|
||||
songs = self.plugin.manager.get_all_objects(Song)
|
||||
for outer_song_counter in range(max_songs - 1):
|
||||
for inner_song_counter in range(outer_song_counter + 1, max_songs):
|
||||
if songs_probably_equal(songs[outer_song_counter], songs[inner_song_counter]):
|
||||
duplicate_added = self.add_duplicates_to_song_list(songs[outer_song_counter],
|
||||
songs[inner_song_counter])
|
||||
if duplicate_added:
|
||||
self.found_duplicates_edit.appendPlainText(songs[outer_song_counter].title + " = " +
|
||||
songs[inner_song_counter].title)
|
||||
self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
|
||||
# The call to process_events() will keep the GUI responsive.
|
||||
self.application.process_events()
|
||||
if self.break_search:
|
||||
return
|
||||
self.review_total_count = len(self.duplicate_song_list)
|
||||
if self.review_total_count == 0:
|
||||
self.notify_no_duplicates()
|
||||
else:
|
||||
self.button(QtGui.QWizard.NextButton).show()
|
||||
finally:
|
||||
self.application.set_normal_cursor()
|
||||
elif page_id == self.review_page_id:
|
||||
self.process_current_duplicate_entry()
|
||||
|
||||
def notify_no_duplicates(self):
|
||||
"""
|
||||
Notifies the user, that there were no duplicates found in the database.
|
||||
"""
|
||||
self.button(QtGui.QWizard.FinishButton).show()
|
||||
self.button(QtGui.QWizard.FinishButton).setEnabled(True)
|
||||
self.button(QtGui.QWizard.NextButton).hide()
|
||||
self.button(QtGui.QWizard.CancelButton).hide()
|
||||
QtGui.QMessageBox.information(self, translate(u'Wizard', u'Information'),
|
||||
translate(u'Wizard', u'No duplicate songs have been found in the database.'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
|
||||
|
||||
def add_duplicates_to_song_list(self, search_song, duplicate_song):
|
||||
"""
|
||||
Inserts a song duplicate (two similar songs) to the duplicate song list.
|
||||
If one of the two songs is already part of the duplicate song list,
|
||||
don't add another duplicate group but add the other song to that group.
|
||||
Returns True if at least one of the songs was added, False if both were already
|
||||
member of a group.
|
||||
|
||||
``search_song``
|
||||
The song we searched the duplicate for.
|
||||
|
||||
``duplicate_song``
|
||||
The duplicate song.
|
||||
"""
|
||||
duplicate_group_found = False
|
||||
duplicate_added = False
|
||||
for duplicate_group in self.duplicate_song_list:
|
||||
# Skip the first song in the duplicate lists, since the first one has to be an earlier song.
|
||||
if search_song in duplicate_group and not duplicate_song in duplicate_group:
|
||||
duplicate_group.append(duplicate_song)
|
||||
duplicate_group_found = True
|
||||
duplicate_added = True
|
||||
break
|
||||
elif not search_song in duplicate_group and duplicate_song in duplicate_group:
|
||||
duplicate_group.append(search_song)
|
||||
duplicate_group_found = True
|
||||
duplicate_added = True
|
||||
break
|
||||
elif search_song in duplicate_group and duplicate_song in duplicate_group:
|
||||
duplicate_group_found = True
|
||||
duplicate_added = False
|
||||
break
|
||||
if not duplicate_group_found:
|
||||
self.duplicate_song_list.append([search_song, duplicate_song])
|
||||
duplicate_added = True
|
||||
return duplicate_added
|
||||
|
||||
def on_wizard_exit(self):
|
||||
"""
|
||||
Once the wizard is finished, refresh the song list,
|
||||
since we potentially removed songs from it.
|
||||
"""
|
||||
self.break_search = True
|
||||
self.plugin.media_item.on_search_text_button_clicked()
|
||||
|
||||
def setDefaults(self):
|
||||
"""
|
||||
Set default form values for the song import wizard.
|
||||
"""
|
||||
self.restart()
|
||||
self.duplicate_search_progress_bar.setValue(0)
|
||||
self.found_duplicates_edit.clear()
|
||||
|
||||
def validateCurrentPage(self):
|
||||
"""
|
||||
Controls whether we should switch to the next wizard page. This method loops
|
||||
on the review page as long as there are more song duplicates to review.
|
||||
"""
|
||||
if self.currentId() == self.review_page_id:
|
||||
# As long as it's not the last duplicate list entry we revisit the review page.
|
||||
if len(self.duplicate_song_list) == 1:
|
||||
return True
|
||||
else:
|
||||
self.proceed_to_next_review()
|
||||
return False
|
||||
return OpenLPWizard.validateCurrentPage(self)
|
||||
|
||||
def remove_button_clicked(self, song_review_widget):
|
||||
"""
|
||||
Removes a song from the database, removes the GUI element representing the
|
||||
song on the review page, and disable the remove button if only one duplicate
|
||||
is left.
|
||||
|
||||
``song_review_widget``
|
||||
The SongReviewWidget whose song we should delete.
|
||||
"""
|
||||
# Remove song from duplicate song list.
|
||||
self.duplicate_song_list[-1].remove(song_review_widget.song)
|
||||
# Remove song from the database.
|
||||
delete_song(song_review_widget.song.id, self.plugin)
|
||||
# Remove GUI elements for the song.
|
||||
self.review_scroll_area_layout.removeWidget(song_review_widget)
|
||||
song_review_widget.setParent(None)
|
||||
# Check if we only have one duplicate left:
|
||||
# 2 stretches + 1 SongReviewWidget = 3
|
||||
# The SongReviewWidget is then at position 1.
|
||||
if len(self.duplicate_song_list[-1]) == 1:
|
||||
self.review_scroll_area_layout.itemAt(1).widget().song_remove_button.setEnabled(False)
|
||||
|
||||
def proceed_to_next_review(self):
|
||||
"""
|
||||
Removes the previous review UI elements and calls process_current_duplicate_entry.
|
||||
"""
|
||||
# Remove last duplicate group.
|
||||
self.duplicate_song_list.pop()
|
||||
# Remove all previous elements.
|
||||
for i in reversed(range(self.review_scroll_area_layout.count())):
|
||||
item = self.review_scroll_area_layout.itemAt(i)
|
||||
if isinstance(item, QtGui.QWidgetItem):
|
||||
# The order is important here, if the .setParent(None) call is done
|
||||
# before the .removeItem() call, a segfault occurs.
|
||||
widget = item.widget()
|
||||
self.review_scroll_area_layout.removeItem(item)
|
||||
widget.setParent(None)
|
||||
else:
|
||||
self.review_scroll_area_layout.removeItem(item)
|
||||
# Process next set of duplicates.
|
||||
self.process_current_duplicate_entry()
|
||||
|
||||
def process_current_duplicate_entry(self):
|
||||
"""
|
||||
Update the review counter in the wizard header, add song widgets for
|
||||
the current duplicate group to review, if it's the last
|
||||
duplicate song group, hide the "next" button and show the "finish" button.
|
||||
"""
|
||||
# Update the counter.
|
||||
self.review_current_count = self.review_total_count - (len(self.duplicate_song_list) - 1)
|
||||
self.update_review_counter_text()
|
||||
# Add song elements to the UI.
|
||||
if len(self.duplicate_song_list) > 0:
|
||||
self.review_scroll_area_layout.addStretch(1)
|
||||
for duplicate in self.duplicate_song_list[-1]:
|
||||
song_review_widget = SongReviewWidget(self.review_page, duplicate)
|
||||
song_review_widget.song_remove_button_clicked.connect(self.remove_button_clicked)
|
||||
self.review_scroll_area_layout.addWidget(song_review_widget)
|
||||
self.review_scroll_area_layout.addStretch(1)
|
||||
# Change next button to finish button on last review.
|
||||
if len(self.duplicate_song_list) == 1:
|
||||
self.button(QtGui.QWizard.FinishButton).show()
|
||||
self.button(QtGui.QWizard.FinishButton).setEnabled(True)
|
||||
self.button(QtGui.QWizard.NextButton).hide()
|
||||
self.button(QtGui.QWizard.CancelButton).hide()
|
||||
|
||||
def _get_main_window(self):
|
||||
"""
|
||||
Adds the main window to the class dynamically.
|
||||
"""
|
||||
if not hasattr(self, u'_main_window'):
|
||||
self._main_window = Registry().get(u'main_window')
|
||||
return self._main_window
|
||||
|
||||
main_window = property(_get_main_window)
|
||||
|
||||
def _get_application(self):
|
||||
"""
|
||||
Adds the openlp to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, u'_application'):
|
||||
self._application = Registry().get(u'application')
|
||||
return self._application
|
||||
|
||||
application = property(_get_application)
|
@ -320,7 +320,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
for plugin in self.plugin_manager.plugins:
|
||||
if plugin.name == u'media' and plugin.status == PluginStatus.Active:
|
||||
self.from_media_button.setVisible(True)
|
||||
self.media_form.populateFiles(plugin.media_item.getList(MediaType.Audio))
|
||||
self.media_form.populateFiles(plugin.media_item.get_list(MediaType.Audio))
|
||||
break
|
||||
|
||||
def new_song(self):
|
||||
@ -714,7 +714,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
text = self.song_book_combo_box.currentText()
|
||||
if item == 0 and text:
|
||||
temp_song_book = text
|
||||
self.media_item.songMaintenanceForm.exec_(True)
|
||||
self.media_item.song_maintenance_form.exec_(True)
|
||||
self.load_authors()
|
||||
self.load_books()
|
||||
self.load_topics()
|
||||
|
@ -37,7 +37,6 @@ from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.lib import Registry, UiStrings, create_separated_list, build_icon, translate
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.plugins.songs.lib import natcmp
|
||||
from openlp.plugins.songs.lib.db import Song
|
||||
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
|
||||
|
||||
@ -222,7 +221,7 @@ class SongExportForm(OpenLPWizard):
|
||||
# Load the list of songs.
|
||||
self.application.set_busy_cursor()
|
||||
songs = self.plugin.manager.get_all_objects(Song)
|
||||
songs.sort(cmp=natcmp, key=lambda song: song.sort_key)
|
||||
songs.sort(key=lambda song: song.sort_key)
|
||||
for song in songs:
|
||||
# No need to export temporary songs.
|
||||
if song.temporary:
|
||||
@ -236,7 +235,7 @@ class SongExportForm(OpenLPWizard):
|
||||
self.availableListWidget.addItem(item)
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def preWizard(self):
|
||||
def pre_wizard(self):
|
||||
"""
|
||||
Perform pre export tasks.
|
||||
"""
|
||||
|
@ -325,7 +325,7 @@ class SongImportForm(OpenLPWizard):
|
||||
self.error_copy_to_button.setHidden(True)
|
||||
self.error_save_to_button.setHidden(True)
|
||||
|
||||
def preWizard(self):
|
||||
def pre_wizard(self):
|
||||
"""
|
||||
Perform pre import tasks
|
||||
"""
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user