Merge from head.

This commit is contained in:
Raoul Snyman 2010-03-12 17:42:47 +02:00
commit 04c1db2033
135 changed files with 10447 additions and 4458 deletions

View File

@ -10,3 +10,7 @@ openlp.org 2.0.e4*
documentation/build/html
documentation/build/doctrees
*.log*
dist
OpenLP.egg-info
build
resources/innosetup/Output

12
MANIFEST.in Normal file
View File

@ -0,0 +1,12 @@
recursive-include openlp *.py
recursive-include openlp *.sqlite
recursive-include openlp *.csv
recursive-include documentation *
recursive-include resources/forms *
recursive-include resources/i18n *
recursive-include resources/images *
recursive-include scripts *.py
include resources/*.desktop
include copyright.txt
include LICENSE
include openlp/.version

19
OpenLP.spec Normal file
View File

@ -0,0 +1,19 @@
# -*- mode: python -*-
a = Analysis([os.path.join(HOMEPATH,'support\\_mountzlib.py'), os.path.join(HOMEPATH,'support\\useUnicode.py'), 'openlp.pyw'],
pathex=['c:\\Documents and Settings\\raoul\\My Documents\\My Projects\\openlp\\pyinstaller'])
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
exclude_binaries=1,
name=os.path.join('build\\pyi.win32\\OpenLP', 'OpenLP.exe'),
debug=False,
strip=False,
upx=True,
console=False , icon='resources\\images\\OpenLP.ico')
coll = COLLECT( exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name=os.path.join('dist', 'OpenLP'))

View File

@ -28,16 +28,17 @@ import os
import sys
import logging
from logging.handlers import RotatingFileHandler
from logging import FileHandler
from optparse import OptionParser
from PyQt4 import QtCore, QtGui
log = logging.getLogger()
import openlp
from openlp.core.lib import Receiver, str_to_bool
from openlp.core.resources import qInitResources
from openlp.core.ui import MainWindow, SplashScreen
from openlp.core.utils import ConfigHelper
log = logging.getLogger()
from openlp.core.ui import MainWindow, SplashScreen, ScreenList
from openlp.core.utils import AppLocation, ConfigHelper
application_stylesheet = u"""
QMainWindow::separator
@ -47,9 +48,11 @@ QMainWindow::separator
QDockWidget::title
{
border: none;
/*background: palette(dark);*/
border: 1px solid palette(dark);
padding-left: 5px;
padding-top: 3px;
padding-top: 2px;
margin: 1px 0;
}
QToolBar
@ -65,16 +68,21 @@ class OpenLP(QtGui.QApplication):
The core application class. This class inherits from Qt's QApplication
class in order to provide the core of the application.
"""
global log
log.info(u'OpenLP Application Loaded')
def notify(self, obj, evt):
#TODO needed for presentation exceptions
return QtGui.QApplication.notify(self, obj, evt)
def run(self):
"""
Run the OpenLP application.
"""
#Load and store current Application Version
filepath = os.path.split(os.path.abspath(__file__))[0]
filepath = os.path.abspath(os.path.join(filepath, u'version.txt'))
filepath = AppLocation.get_directory(AppLocation.AppDir)
if not hasattr(sys, u'frozen'):
filepath = os.path.join(filepath, u'openlp')
filepath = os.path.join(filepath, u'.version')
fversion = None
try:
fversion = open(filepath, u'r')
@ -91,9 +99,9 @@ class OpenLP(QtGui.QApplication):
app_version[u'version'], app_version[u'build']))
except:
app_version = {
u'full': u'1.9.0-000',
u'full': u'1.9.0-bzr000',
u'version': u'1.9.0',
u'build': u'000'
u'build': u'bzr000'
}
finally:
if fversion:
@ -117,10 +125,10 @@ class OpenLP(QtGui.QApplication):
self.splash.show()
# make sure Qt really display the splash screen
self.processEvents()
screens = []
screens = ScreenList()
# Decide how many screens we have and their size
for screen in xrange(0, self.desktop().numScreens()):
screens.append({u'number': screen,
screens.add_screen({u'number': screen,
u'size': self.desktop().availableGeometry(screen),
u'primary': (self.desktop().primaryScreen() == screen)})
log.info(u'Screen %d found with resolution %s',
@ -131,7 +139,8 @@ class OpenLP(QtGui.QApplication):
if show_splash:
# now kill the splashscreen
self.splash.finish(self.mainWindow)
self.mainWindow.versionCheck()
self.mainWindow.repaint()
self.mainWindow.versionThread()
return self.exec_()
def main():
@ -143,7 +152,7 @@ def main():
usage = u'Usage: %prog [options] [qt-options]'
parser = OptionParser(usage=usage)
parser.add_option("-l", "--log-level", dest="loglevel",
default="info", metavar="LEVEL",
default="warning", metavar="LEVEL",
help="Set logging to LEVEL level. Valid values are "
"\"debug\", \"info\", \"warning\".")
parser.add_option("-p", "--portable", dest="portable",
@ -153,10 +162,13 @@ def main():
parser.add_option("-s", "--style", dest="style",
help="Set the Qt4 style (passed directly to Qt4).")
# Set up logging
filename = u'openlp.log'
logfile = RotatingFileHandler(filename, maxBytes=200000, backupCount=5)
log_path = AppLocation.get_directory(AppLocation.ConfigDir)
if not os.path.exists(log_path):
os.makedirs(log_path)
filename = os.path.join(log_path, u'openlp.log')
logfile = FileHandler(filename, u'w')
logfile.setFormatter(logging.Formatter(
u'%(asctime)s %(name)-15s %(levelname)-8s %(message)s'))
u'%(asctime)s %(name)-20s %(levelname)-8s %(message)s'))
log.addHandler(logfile)
logging.addLevelName(15, u'Timer')
# Parse command line options and deal with them.
@ -164,6 +176,7 @@ def main():
qt_args = []
if options.loglevel.lower() in ['d', 'debug']:
log.setLevel(logging.DEBUG)
print 'Logging to:', filename
elif options.loglevel.lower() in ['w', 'warning']:
log.setLevel(logging.WARNING)
else:
@ -182,4 +195,4 @@ if __name__ == u'__main__':
"""
Instantiate and run the application.
"""
main()
main()

1
openlp/.version Normal file
View File

@ -0,0 +1 @@
1.9.0-bzr722

View File

@ -136,6 +136,26 @@ def contextMenuSeparator(base):
action.setSeparator(True)
return action
def resize_image(image, width, height):
"""
Resize an image to fit on the current screen.
``image``
The image to resize.
"""
preview = QtGui.QImage(image)
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation)
realw = preview.width()
realh = preview.height()
# and move it to the centre of the preview space
newImage = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32_Premultiplied)
newImage.fill(QtCore.Qt.black)
painter = QtGui.QPainter(newImage)
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
return newImage
class ThemeLevel(object):
Global = 1
Service = 2
@ -160,6 +180,3 @@ from renderer import Renderer
from rendermanager import RenderManager
from mediamanageritem import MediaManagerItem
from baselistwithdnd import BaseListWithDnD
#__all__ = [ 'translate', 'get_text_file_string', 'str_to_bool',
# 'contextMenuAction', 'contextMenuSeparator', 'ServiceItem']

View File

@ -32,6 +32,7 @@ class BaseListWithDnD(QtGui.QListWidget):
def __init__(self, parent=None):
QtGui.QListWidget.__init__(self, parent)
self.parent = parent
# this must be set by the class which is inheriting
assert(self.PluginName)
@ -47,4 +48,5 @@ class BaseListWithDnD(QtGui.QListWidget):
mimeData = QtCore.QMimeData()
drag.setMimeData(mimeData)
mimeData.setText(self.PluginName)
dropAction = drag.start(QtCore.Qt.CopyAction)
dropAction = drag.start(QtCore.Qt.CopyAction)

View File

@ -27,6 +27,8 @@ import logging
from PyQt4 import QtGui
log = logging.getLogger(__name__)
class OpenLPDockWidget(QtGui.QDockWidget):
"""
Custom DockWidget class to handle events
@ -40,10 +42,9 @@ class OpenLPDockWidget(QtGui.QDockWidget):
if name:
self.setObjectName(name)
self.setFloating(False)
self.log = logging.getLogger(u'OpenLPDockWidget')
self.log.debug(u'Init done')
log.debug(u'Init done')
def closeEvent(self, event):
self.parent.settingsmanager.setUIItemVisibility(
self.objectName(), False)
event.accept()
event.accept()

View File

@ -27,6 +27,8 @@ import logging
from PyQt4 import QtCore
log = logging.getLogger(__name__)
class EventReceiver(QtCore.QObject):
"""
Class to allow events to be passed from different parts of the
@ -104,10 +106,10 @@ class EventReceiver(QtCore.QObject):
``remote_edit_clear``
Informs all components that remote edit has been aborted.
"""
global log
log = logging.getLogger(u'EventReceiver')
``presentation types``
Informs all components of the presentation types supported.
"""
def __init__(self):
"""
Initialise the event receiver, calling the parent constructor.
@ -161,4 +163,5 @@ class Receiver():
"""
Get the global ``eventreceiver`` instance.
"""
return Receiver.eventreceiver
return Receiver.eventreceiver

View File

@ -32,6 +32,8 @@ from openlp.core.lib.toolbar import *
from openlp.core.lib import contextMenuAction, contextMenuSeparator
from serviceitem import ServiceItem
log = logging.getLogger(__name__)
class MediaManagerItem(QtGui.QWidget):
"""
MediaManagerItem is a helper widget for plugins.
@ -92,9 +94,6 @@ class MediaManagerItem(QtGui.QWidget):
method is not defined, a default will be used (treat the
filename as an image).
"""
global log
log = logging.getLogger(u'MediaManagerItem')
log.info(u'Media Item loaded')
def __init__(self, parent=None, icon=None, title=None):
@ -253,7 +252,7 @@ class MediaManagerItem(QtGui.QWidget):
def addListViewToToolBar(self):
#Add the List widget
self.ListView = self.ListViewWithDnD_class()
self.ListView = self.ListViewWithDnD_class(self)
self.ListView.uniformItemSizes = True
self.ListView.setGeometry(QtCore.QRect(10, 100, 256, 591))
self.ListView.setSpacing(1)
@ -315,7 +314,7 @@ class MediaManagerItem(QtGui.QWidget):
self, self.OnNewPrompt,
self.parent.config.get_last_dir(), self.OnNewFileMasks)
log.info(u'New files(s)%s', unicode(files))
if len(files) > 0:
if files:
self.loadList(files)
dir, filename = os.path.split(unicode(files[0]))
self.parent.config.set_last_dir(dir)
@ -400,4 +399,4 @@ class MediaManagerItem(QtGui.QWidget):
if self.generateSlideData(service_item):
return service_item
else:
return None
return None

View File

@ -28,6 +28,8 @@ from PyQt4 import QtCore
from openlp.core.lib import PluginConfig, Receiver
log = logging.getLogger(__name__)
class PluginStatus(object):
"""
Defines the status of the plugin
@ -88,8 +90,6 @@ class Plugin(QtCore.QObject):
Used in the plugin manager, when a person clicks on the 'About' button.
"""
global log
log = logging.getLogger(u'Plugin')
log.info(u'loaded')
def __init__(self, name, version=None, plugin_helpers=None):
@ -127,6 +127,7 @@ class Plugin(QtCore.QObject):
self.service_manager = plugin_helpers[u'service']
self.settings = plugin_helpers[u'settings']
self.mediadock = plugin_helpers[u'toolbox']
self.maindisplay = plugin_helpers[u'maindisplay']
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_add_service_item' % self.name),
self.process_add_service_event)
@ -252,4 +253,10 @@ class Plugin(QtCore.QObject):
if self.media_item:
self.mediadock.insert_dock(self.media_item, self.icon, self.weight)
if self.settings_tab:
self.settings.insertTab(self.settings_tab, self.weight)
self.settings.insertTab(self.settings_tab, self.weight)
def can_delete_theme(self, theme):
"""
Called to ask the plugin if a theme can be deleted
"""
return True

View File

@ -29,13 +29,13 @@ import logging
from openlp.core.lib import Plugin, PluginStatus
log = logging.getLogger(__name__)
class PluginManager(object):
"""
This is the Plugin manager, which loads all the plugins,
and executes all the hooks, as and when necessary.
"""
global log
log = logging.getLogger(u'PluginMgr')
log.info(u'Plugin manager loaded')
def __init__(self, dir):
@ -54,7 +54,7 @@ class PluginManager(object):
log.debug(u'Base path %s ', self.basepath)
self.plugins = []
# this has to happen after the UI is sorted self.find_plugins(dir)
log.info(u'Plugin manager done init')
log.info(u'Plugin manager Initialised')
def find_plugins(self, dir, plugin_helpers):
"""
@ -77,7 +77,7 @@ class PluginManager(object):
if name.endswith(u'.py') and not name.startswith(u'__'):
path = os.path.abspath(os.path.join(root, name))
thisdepth = len(path.split(os.sep))
if thisdepth-startdepth > 2:
if thisdepth - startdepth > 2:
# skip anything lower down
continue
modulename, pyext = os.path.splitext(path)
@ -101,7 +101,7 @@ class PluginManager(object):
log.debug(u'Loaded plugin %s with helpers', unicode(p))
plugin_objects.append(plugin)
except TypeError:
log.error(u'loaded plugin %s has no helpers', unicode(p))
log.exception(u'loaded plugin %s has no helpers', unicode(p))
plugins_list = sorted(plugin_objects, self.order_by_weight)
for plugin in plugins_list:
if plugin.check_pre_conditions():
@ -200,6 +200,7 @@ class PluginManager(object):
% (plugin.name, plugin.is_active()))
if plugin.is_active():
plugin.initialise()
log.info(u'Initialisation Complete for %s ' % plugin.name)
if not plugin.is_active():
plugin.remove_toolbox_item()
@ -211,4 +212,5 @@ class PluginManager(object):
log.info(u'finalising plugins')
for plugin in self.plugins:
if plugin.is_active():
plugin.finalise()
plugin.finalise()
log.info(u'Finalisation Complete for %s ' % plugin.name)

View File

@ -26,14 +26,15 @@
import logging
from PyQt4 import QtGui, QtCore
from openlp.core.lib import resize_image
log = logging.getLogger(__name__)
class Renderer(object):
"""
Genarates a pixmap image of a array of text. The Text is formatted to
make sure it fits on the screen and if not extra frames are generated.
"""
global log
log = logging.getLogger(u'Renderer')
log.info(u'Renderer Loaded')
def __init__(self):
@ -41,7 +42,7 @@ class Renderer(object):
Initialise the renderer.
"""
self._rect = None
self._debug = 0
self._debug = False
self._right_margin = 64 # the amount of right indent
self._display_shadow_size_footer = 0
self._display_outline_size_footer = 0
@ -90,31 +91,9 @@ class Renderer(object):
log.debug(u'set bg image %s', filename)
self._bg_image_filename = unicode(filename)
if self._frame:
self.scale_bg_image()
def scale_bg_image(self):
"""
Scale the background image to fit the screen.
"""
assert self._frame
preview = QtGui.QImage(self._bg_image_filename)
width = self._frame.width()
height = self._frame.height()
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation)
realwidth = preview.width()
realheight = preview.height()
# and move it to the centre of the preview space
self.bg_image = QtGui.QImage(width, height,
QtGui.QImage.Format_ARGB32_Premultiplied)
self.bg_image.fill(QtCore.Qt.black)
painter = QtGui.QPainter()
painter.begin(self.bg_image)
self.background_offsetx = (width - realwidth) / 2
self.background_offsety = (height - realheight) / 2
painter.drawImage(self.background_offsetx,
self.background_offsety, preview)
painter.end()
self.bg_image = resize_image(self._bg_image_filename,
self._frame.width(),
self._frame.height())
def set_frame_dest(self, frame_width, frame_height, preview=False):
"""
@ -138,7 +117,9 @@ class Renderer(object):
self._frameOp = QtGui.QImage(frame_width, frame_height,
QtGui.QImage.Format_ARGB32_Premultiplied)
if self._bg_image_filename and not self.bg_image:
self.scale_bg_image()
self.bg_image = resize_image(self._bg_image_filename,
self._frame.width(),
self._frame.height())
if self.bg_frame is None:
self._generate_background_frame()
@ -167,17 +148,22 @@ class Renderer(object):
def pre_render_text(self, text):
metrics = QtGui.QFontMetrics(self.mainFont)
#take the width work out approx how many characters and add 50%
#work out line width
line_width = self._rect.width() - self._right_margin
#number of lines on a page - adjust for rounding up.
page_length = int(self._rect.height() / metrics.height() - 2 ) - 1
line_height = metrics.height()
if self._theme.display_shadow:
line_height += int(self._theme.display_shadow_size)
if self._theme.display_outline:
# pixels top/bottom
line_height += 2 * int(self._theme.display_outline_size)
page_length = int(self._rect.height() / line_height )
#Average number of characters in line
ave_line_width = line_width / metrics.averageCharWidth()
#Maximum size of a character
max_char_width = metrics.maxWidth()
#Min size of a character
min_char_width = metrics.width(u'i')
char_per_line = line_width / min_char_width
#Max characters pre line based on min size of a character
char_per_line = line_width / metrics.width(u'i')
log.debug(u'Page Length area height %s , metrics %s , lines %s' %
(int(self._rect.height()), metrics.height(), page_length ))
split_pages = []
@ -188,7 +174,7 @@ class Renderer(object):
#Must be a blank line so keep it.
if len(line) == 0:
line = u' '
while len(line) > 0:
while line:
pos = char_per_line
split_text = line[:pos]
#line needs splitting
@ -213,7 +199,7 @@ class Renderer(object):
split_lines.append(split_text)
line = line[pos:].lstrip()
#if we have more text add up to 10 spaces on the front.
if len(line) > 0 and self._theme.font_main_indentation > 0:
if line and self._theme.font_main_indentation > 0:
line = u'%s%s' % \
(u' '[:int(self._theme.font_main_indentation)], line)
#Text fits in a line now
@ -224,7 +210,7 @@ class Renderer(object):
len(page) == page_length:
split_pages.append(page)
page = []
if len(page) > 0 and page != u' ':
if page and page != u' ':
split_pages.append(page)
return split_pages
@ -276,8 +262,13 @@ class Renderer(object):
Results are cached for performance reasons.
"""
assert(self._theme)
self.bg_frame = QtGui.QImage(self._frame.width(), self._frame.height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
if self._theme.background_mode == u'transparent':
self.bg_frame = \
QtGui.QPixmap(self._frame.width(), self._frame.height())
self.bg_frame.fill(QtCore.Qt.transparent)
else:
self.bg_frame = QtGui.QImage(self._frame.width(), self._frame.height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
log.debug(u'render background %s start', self._theme.background_type)
painter = QtGui.QPainter()
painter.begin(self.bg_frame)
@ -415,13 +406,21 @@ class Renderer(object):
Defaults to *False*. Whether or not this is a live screen.
"""
x, y = tlcorner
maxx = self._rect.width();
maxy = self._rect.height();
maxx = self._rect.width()
maxy = self._rect.height()
lines = []
lines.append(line)
startx = x
starty = y
rightextent = None
self.painter = QtGui.QPainter()
self.painter.begin(self._frame)
self.painter.setRenderHint(QtGui.QPainter.Antialiasing)
if self._theme.display_slideTransition:
self.painter2 = QtGui.QPainter()
self.painter2.begin(self._frameOp)
self.painter2.setRenderHint(QtGui.QPainter.Antialiasing)
self.painter2.setOpacity(0.7)
# dont allow alignment messing with footers
if footer:
align = 0
@ -459,7 +458,7 @@ class Renderer(object):
x = maxx - w
# centre
elif align == 2:
x = (maxx - w) / 2;
x = (maxx - w) / 2
rightextent = x + w
if live:
# now draw the text, and any outlines/shadows
@ -467,49 +466,21 @@ class Renderer(object):
self._get_extent_and_render(line, footer,
tlcorner=(x + display_shadow_size, y + display_shadow_size),
draw=True, color = self._theme.display_shadow_color)
if self._theme.display_outline:
self._get_extent_and_render(line, footer,
(x + display_outline_size, y), draw=True,
color = self._theme.display_outline_color)
self._get_extent_and_render(line, footer,
(x, y + display_outline_size), draw=True,
color = self._theme.display_outline_color)
self._get_extent_and_render(line, footer,
(x, y - display_outline_size), draw=True,
color = self._theme.display_outline_color)
self._get_extent_and_render(line, footer,
(x - display_outline_size, y), draw=True,
color = self._theme.display_outline_color)
if display_outline_size > 1:
self._get_extent_and_render(line, footer,
(x + display_outline_size, y + display_outline_size),
draw=True,
color = self._theme.display_outline_color)
self._get_extent_and_render(line, footer,
(x - display_outline_size, y + display_outline_size),
draw=True,
color = self._theme.display_outline_color)
self._get_extent_and_render(line, footer,
(x + display_outline_size, y - display_outline_size),
draw=True,
color = self._theme.display_outline_color)
self._get_extent_and_render(line, footer,
(x - display_outline_size, y - display_outline_size),
draw=True,
color = self._theme.display_outline_color)
self._get_extent_and_render(line, footer,tlcorner=(x, y),
draw=True)
self._get_extent_and_render(line, footer, tlcorner=(x, y), draw=True,
outline_size=display_outline_size,
outline_color=self._theme.display_outline_color)
y += h
if linenum == 0:
self._first_line_right_extent = rightextent
# draw a box around the text - debug only
if self._debug:
painter = QtGui.QPainter()
painter.begin(self._frame)
painter.setPen(QtGui.QPen(QtGui.QColor(0,255,0)))
painter.drawRect(startx, starty, rightextent-startx, y-starty)
painter.end()
self.painter.setPen(QtGui.QPen(QtGui.QColor(0,255,0)))
self.painter.drawRect(startx, starty, rightextent-startx, y-starty)
brcorner = (rightextent, y)
self.painter.end()
if self._theme.display_slideTransition:
self.painter2.end()
return brcorner
def _set_theme_font(self):
@ -519,6 +490,7 @@ class Renderer(object):
footer_weight = 50
if self._theme.font_footer_weight == u'Bold':
footer_weight = 75
#TODO Add myfont.setPixelSize((screen_height / 100) * font_size)
self.footerFont = QtGui.QFont(self._theme.font_footer_name,
self._theme.font_footer_proportion, # size
footer_weight, # weight
@ -534,7 +506,7 @@ class Renderer(object):
self.mainFont.setPixelSize(self._theme.font_main_proportion)
def _get_extent_and_render(self, line, footer, tlcorner=(0, 0), draw=False,
color=None):
color=None, outline_size=None, outline_color=None):
"""
Find bounding box of text - as render_single_line. If draw is set,
actually draw the text to the current DC as well return width and
@ -556,45 +528,42 @@ class Renderer(object):
Defaults to *None*. The colour to draw with.
"""
# setup defaults
painter = QtGui.QPainter()
painter.begin(self._frame)
painter.setRenderHint(QtGui.QPainter.Antialiasing);
if footer :
font = self.footerFont
else:
font = self.mainFont
painter.setFont(font)
if color is None:
if footer:
painter.setPen(QtGui.QColor(self._theme.font_footer_color))
else:
painter.setPen(QtGui.QColor(self._theme.font_main_color))
else:
painter.setPen(QtGui.QColor(color))
x, y = tlcorner
metrics = QtGui.QFontMetrics(font)
w = metrics.width(line)
h = metrics.height() - 2
h = metrics.height()
if draw:
painter.drawText(x, y + metrics.ascent(), line)
painter.end()
if self._theme.display_slideTransition:
# Print 2nd image with 70% weight
painter = QtGui.QPainter()
painter.begin(self._frameOp)
painter.setRenderHint(QtGui.QPainter.Antialiasing);
painter.setOpacity(0.7)
painter.setFont(font)
self.painter.setFont(font)
if color is None:
if footer:
painter.setPen(QtGui.QColor(self._theme.font_footer_color))
pen = QtGui.QColor(self._theme.font_footer_color)
else:
painter.setPen(QtGui.QColor(self._theme.font_main_color))
pen = QtGui.QColor(self._theme.font_main_color)
else:
painter.setPen(QtGui.QColor(color))
if draw:
painter.drawText(x, y + metrics.ascent(), line)
painter.end()
pen = QtGui.QColor(color)
x, y = tlcorner
if outline_size:
path = QtGui.QPainterPath()
path.addText(QtCore.QPointF(x, y + metrics.ascent()), font, line)
self.painter.setBrush(self.painter.pen().brush())
self.painter.setPen(QtGui.QPen(QtGui.QColor(outline_color), outline_size))
self.painter.drawPath(path)
self.painter.setPen(pen)
self.painter.drawText(x, y + metrics.ascent(), line)
if self._theme.display_slideTransition:
# Print 2nd image with 70% weight
if outline_size:
path = QtGui.QPainterPath()
path.addText(QtCore.QPointF(x, y + metrics.ascent()), font, line)
self.painter2.setBrush(self.painter2.pen().brush())
self.painter2.setPen(QtGui.QPen(QtGui.QColor(outline_color), outline_size))
self.painter2.drawPath(path)
self.painter2.setFont(font)
self.painter2.setPen(pen)
self.painter2.drawText(x, y + metrics.ascent(), line)
return (w, h)
def snoop_Image(self, image, image2=None):
@ -609,4 +578,4 @@ class Renderer(object):
"""
image.save(u'renderer.png', u'png')
if image2:
image2.save(u'renderer2.png', u'png')
image2.save(u'renderer2.png', u'png')

View File

@ -25,11 +25,13 @@
import logging
from PyQt4 import QtGui, QtCore
from PyQt4 import QtCore
from renderer import Renderer
from openlp.core.lib import ThemeLevel
log = logging.getLogger(__name__)
class RenderManager(object):
"""
Class to pull all Renderer interactions into one place. The plugins will
@ -39,37 +41,29 @@ class RenderManager(object):
``theme_manager``
The ThemeManager instance, used to get the current theme details.
``screen_list``
The list of screens available.
``screens``
Contains information about the Screens.
``screen_number``
Defaults to *0*. The index of the output/display screen.
"""
global log
log = logging.getLogger(u'RenderManager')
log.info(u'RenderManager Loaded')
def __init__(self, theme_manager, screen_list, screen_number=0):
def __init__(self, theme_manager, screens, screen_number=0):
"""
Initialise the render manager.
"""
log.debug(u'Initilisation started')
self.screen_list = screen_list
self.screens = screens
self.theme_manager = theme_manager
self.displays = len(screen_list)
if (screen_number + 1) > len(screen_list):
self.current_display = 0
else:
self.current_display = screen_number
self.renderer = Renderer()
self.calculate_default(self.screen_list[self.current_display][u'size'])
self.screens.set_current_display(screen_number)
self.calculate_default(self.screens.current[u'size'])
self.theme = u''
self.service_theme = u''
self.theme_level = u''
self.override_background = None
self.themedata = None
self.save_bg_frame = None
self.override_background_changed = False
def update_display(self, screen_number):
"""
@ -79,10 +73,8 @@ class RenderManager(object):
The updated index of the output/display screen.
"""
log.debug(u'Update Display')
if self.current_display != screen_number:
self.current_display = screen_number
self.calculate_default(
self.screen_list[self.current_display][u'size'])
self.calculate_default(self.screens.current[u'size'])
self.renderer.bg_frame = None
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
"""
@ -137,31 +129,14 @@ class RenderManager(object):
if self.theme != self.renderer.theme_name or self.themedata is None:
log.debug(u'theme is now %s', self.theme)
self.themedata = self.theme_manager.getThemeData(self.theme)
self.calculate_default(
self.screen_list[self.current_display][u'size'])
self.calculate_default(self.screens.current[u'size'])
self.renderer.set_theme(self.themedata)
self.build_text_rectangle(self.themedata)
#Replace the backgrount image from renderer with one from image
if self.override_background:
if self.save_bg_frame is None:
self.save_bg_frame = self.renderer.bg_frame
if self.override_background_changed:
self.renderer.bg_frame = self.resize_image(
self.override_background)
self.override_background_changed = False
else:
if self.override_background_changed:
self.renderer.bg_frame = self.resize_image(
self.override_background)
self.override_background_changed = False
if self.save_bg_frame:
self.renderer.bg_frame = self.save_bg_frame
self.save_bg_frame = None
def build_text_rectangle(self, theme):
"""
Builds a text block using the settings in ``theme``.
One is needed per slide
Builds a text block using the settings in ``theme``
and the size of the display screen.height.
``theme``
The theme to build a text block for.
@ -170,14 +145,14 @@ class RenderManager(object):
main_rect = None
footer_rect = None
if not theme.font_main_override:
main_rect = QtCore.QRect(10, 0, self.width - 1,
self.footer_start - 20)
main_rect = QtCore.QRect(10, 0,
self.width - 1, self.footer_start)
else:
main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
theme.font_main_width - 1, theme.font_main_height - 1)
if not theme.font_footer_override:
footer_rect = QtCore.QRect(10, self.footer_start, self.width - 1,
self.height-self.footer_start)
footer_rect = QtCore.QRect(10, self.footer_start,
self.width - 1, self.height - self.footer_start)
else:
footer_rect = QtCore.QRect(theme.font_footer_x,
theme.font_footer_y, theme.font_footer_width - 1,
@ -192,10 +167,13 @@ class RenderManager(object):
The theme to generated a preview for.
"""
log.debug(u'generate preview')
self.calculate_default(QtCore.QSize(1024, 768))
#set the default image size for previews
self.calculate_default(self.screens.preview[u'size'])
self.renderer.set_theme(themedata)
self.build_text_rectangle(themedata)
self.renderer.set_frame_dest(self.width, self.height, True)
#Reset the real screen size for subsequent render requests
self.calculate_default(self.screens.current[u'size'])
verse = u'Amazing Grace!\n'\
'How sweet the sound\n'\
'To save a wretch like me;\n'\
@ -206,6 +184,7 @@ class RenderManager(object):
footer.append(u'Public Domain')
footer.append(u'CCLI 123456')
formatted = self.renderer.format_slide(verse, False)
#Only Render the first slide page returned
return self.renderer.generate_frame_from_lines(formatted[0], footer)[u'main']
def format_slide(self, words):
@ -234,48 +213,18 @@ class RenderManager(object):
self.renderer.set_frame_dest(self.width, self.height)
return self.renderer.generate_frame_from_lines(main_text, footer_text)
def resize_image(self, image, width=0, height=0):
"""
Resize an image to fit on the current screen.
``image``
The image to resize.
"""
preview = QtGui.QImage(image)
if width == 0:
w = self.width
h = self.height
else:
w = width
h = height
preview = preview.scaled(w, h, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation)
realw = preview.width();
realh = preview.height()
# and move it to the centre of the preview space
newImage = QtGui.QImage(w, h, QtGui.QImage.Format_ARGB32_Premultiplied)
newImage.fill(QtCore.Qt.black)
painter = QtGui.QPainter(newImage)
painter.drawImage((w - realw) / 2, (h - realh) / 2, preview)
return newImage
def calculate_default(self, screen):
"""
Calculate the default dimentions of the screen.
``screen``
The QWidget instance of the screen.
The QSize of the screen.
"""
log.debug(u'calculate default %s', screen)
#size fixed so reflects the preview size.
if self.current_display == 0:
self.width = 1024
self.height = 768
else:
self.width = screen.width()
self.height = screen.height()
self.width = screen.width()
self.height = screen.height()
self.screen_ratio = float(self.height) / float(self.width)
log.debug(u'calculate default %d, %d, %f',
self.width, self.height, self.screen_ratio )
# 90% is start of footer
self.footer_start = int(self.height * 0.90)
self.footer_start = int(self.height * 0.90)

View File

@ -30,7 +30,9 @@ import uuid
from PyQt4 import QtGui
from openlp.core.lib import build_icon, Receiver
from openlp.core.lib import build_icon, Receiver, resize_image
log = logging.getLogger(__name__)
class ServiceItemType(object):
"""
@ -46,8 +48,6 @@ class ServiceItem(object):
the service manager, the slide controller, and the projection screen
compositor.
"""
global log
log = logging.getLogger(u'ServiceItem')
log.info(u'Service Item created')
def __init__(self, plugin=None):
@ -72,6 +72,8 @@ class ServiceItem(object):
self._raw_frames = []
self._display_frames = []
self._uuid = unicode(uuid.uuid1())
self.autoPreviewAllowed = False
self.notes = u''
def addIcon(self, icon):
"""
@ -102,16 +104,19 @@ class ServiceItem(object):
formated = self.RenderManager.format_slide(slide[u'raw_slide'])
for format in formated:
lines = u''
title = u''
for line in format:
if title == u'':
title = line
lines += line + u'\n'
title = lines.split(u'\n')[0]
self._display_frames.append({u'title': title, \
u'text': lines, u'verseTag': slide[u'verseTag'] })
u'text': lines.rstrip(), u'verseTag': slide[u'verseTag'] })
log.log(15, u'Formatting took %4s' % (time.time() - before))
elif self.service_item_type == ServiceItemType.Image:
for slide in self._raw_frames:
slide[u'image'] = \
self.RenderManager.resize_image(slide[u'image'])
resize_image(slide[u'image'], self.RenderManager.width,
self.RenderManager.height)
elif self.service_item_type == ServiceItemType.Command:
pass
else:
@ -119,7 +124,7 @@ class ServiceItem(object):
def render_individual(self, row):
"""
Takes an array of text and geneates an Image from the
Takes an array of text and generates an Image from the
theme. It assumes the text will fit on the screen as it
has generated by the render method above.
"""
@ -129,8 +134,12 @@ class ServiceItem(object):
else:
self.RenderManager.set_override_theme(self.theme)
format = self._display_frames[row][u'text'].split(u'\n')
frame = self.RenderManager.generate_slide(format,
self.raw_footer)
#if screen blank then do not display footer
if format[0]:
frame = self.RenderManager.generate_slide(format,
self.raw_footer)
else:
frame = self.RenderManager.generate_slide(format,u'')
return frame
def add_from_image(self, path, title, image):
@ -197,7 +206,9 @@ class ServiceItem(object):
u'icon':self.icon,
u'footer':self.raw_footer,
u'type':self.service_item_type,
u'audit':self.audit
u'audit':self.audit,
u'notes':self.notes,
u'preview':self.autoPreviewAllowed
}
service_data = []
if self.service_item_type == ServiceItemType.Text:
@ -231,6 +242,8 @@ class ServiceItem(object):
self.addIcon(header[u'icon'])
self.raw_footer = header[u'footer']
self.audit = header[u'audit']
self.autoPreviewAllowed = header[u'preview']
self.notes = header[u'notes']
if self.service_item_type == ServiceItemType.Text:
for slide in serviceitem[u'serviceitem'][u'data']:
self._raw_frames.append(slide)
@ -309,4 +322,4 @@ class ServiceItem(object):
def request_audit(self):
if self.audit:
Receiver.send_message(u'songusage_live', self.audit)
Receiver.send_message(u'songusage_live', self.audit)

View File

@ -33,7 +33,7 @@ class SettingsManager(object):
individual components.
"""
def __init__(self, screen):
self.screen = screen[0]
self.screen = screen.current
self.width = self.screen[u'size'].width()
self.height = self.screen[u'size'].height()
self.mainwindow_height = self.height * 0.8
@ -72,4 +72,4 @@ class SettingsManager(object):
u'media manager', isVisible)
def togglePreviewPanel(self, isVisible):
ConfigHelper.set_config(u'user interface', u'preview panel', isVisible)
ConfigHelper.set_config(u'user interface', u'preview panel', isVisible)

View File

@ -27,6 +27,8 @@ import logging
from xml.dom.minidom import Document
from xml.etree.ElementTree import ElementTree, XML, dump
log = logging.getLogger(__name__)
class SongXMLBuilder(object):
"""
This class builds the XML used to describe songs.
@ -42,8 +44,6 @@ class SongXMLBuilder(object):
</lyrics>
</song>
"""
global log
log = logging.getLogger(u'SongXMLBuilder')
log.info(u'SongXMLBuilder Loaded')
def __init__(self):
@ -123,8 +123,6 @@ class SongXMLParser(object):
</lyrics>
</song>
"""
global log
log = logging.getLogger(u'SongXMLParser')
log.info(u'SongXMLParser Loaded')
def __init__(self, xml):
@ -158,4 +156,4 @@ class SongXMLParser(object):
"""
Debugging aid to dump XML so that we can see what we have.
"""
return dump(self.song_xml)
return dump(self.song_xml)

View File

@ -171,7 +171,8 @@ class ThemeXML(object):
self.child_element(background, u'filename', filename)
def add_font(self, name, color, proportion, override, fonttype=u'main',
weight=u'Normal', italics=u'False', indentation=0, xpos=0, ypos=0, width=0, height=0):
weight=u'Normal', italics=u'False', indentation=0, xpos=0, ypos=0,
width=0, height=0):
"""
Add a Font.
@ -363,14 +364,14 @@ class ThemeXML(object):
master = u''
for element in iter:
element.text = unicode(element.text).decode('unicode-escape')
if len(element.getchildren()) > 0:
if element.getchildren():
master = element.tag + u'_'
else:
#background transparent tags have no children so special case
if element.tag == u'background':
for e in element.attrib.iteritems():
setattr(self, element.tag + u'_' + e[0], e[1])
if len(element.attrib) > 0:
if element.attrib:
for e in element.attrib.iteritems():
if master == u'font_' and e[0] == u'type':
master += e[1] + u'_'
@ -402,4 +403,4 @@ class ThemeXML(object):
for key in dir(self):
if key[0:1] != u'_':
theme_strings.append(u'%30s: %s' % (key, getattr(self, key)))
return u'\n'.join(theme_strings)
return u'\n'.join(theme_strings)

View File

@ -29,6 +29,8 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon
log = logging.getLogger(__name__)
class OpenLPToolbar(QtGui.QToolBar):
"""
Lots of toolbars around the place, so it makes sense to have a common way
@ -43,8 +45,7 @@ class OpenLPToolbar(QtGui.QToolBar):
self.icons = {}
self.setIconSize(QtCore.QSize(20, 20))
self.actions = {}
self.log = logging.getLogger(u'OpenLPToolbar')
self.log.debug(u'Init done')
log.debug(u'Init done')
def addToolbarButton(self, title, icon, tooltip=None, slot=None,
checkable=False):
@ -119,7 +120,7 @@ class OpenLPToolbar(QtGui.QToolBar):
if self.icons[title]:
return self.icons[title]
else:
self.log.error(u'getIconFromTitle - no icon for %s' % title)
log.error(u'getIconFromTitle - no icon for %s' % title)
return QtGui.QIcon()
def makeWidgetsInvisible(self, widgets):
@ -152,4 +153,4 @@ class OpenLPToolbar(QtGui.QToolBar):
push_button.setCheckable(True)
push_button.setFlat(True)
self.addWidget(push_button)
return push_button
return push_button

View File

@ -30,7 +30,7 @@ from PyQt4 import QtGui
DelphiColors={"clRed":0xFF0000,
"clBlue":0x0000FF,
"clYellow":0x0FFFF00,
"clYellow":0xFFFF00,
"clBlack":0x000000,
"clWhite":0xFFFFFF}
@ -113,6 +113,7 @@ class Theme(object):
root = ElementTree(element=XML(xml))
iter = root.getiterator()
for element in iter:
delphiColorChange = False
if element.tag != u'Theme':
t = element.text
val = 0
@ -128,6 +129,7 @@ class Theme(object):
pass
elif DelphiColors.has_key(t):
val = DelphiColors[t]
delphiColorChange = True
else:
try:
val = int(t)
@ -136,7 +138,10 @@ class Theme(object):
if (element.tag.find(u'Color') > 0 or
(element.tag.find(u'BackgroundParameter') == 0 and type(val) == type(0))):
# convert to a wx.Colour
val = QtGui.QColor((val>>16) & 0xFF, (val>>8)&0xFF, val&0xFF)
if not delphiColorChange:
val = QtGui.QColor(val&0xFF, (val>>8)&0xFF, (val>>16)&0xFF)
else:
val = QtGui.QColor((val>>16)&0xFF, (val>>8)&0xFF, val&0xFF)
setattr(self, element.tag, val)
def __str__(self):

View File

@ -23,16 +23,15 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
#from slidecontroller import MasterToolbar
from serviceitemform import ServiceItemNoteForm
from screen import ScreenList
from maindisplay import MainDisplay
from amendthemeform import AmendThemeForm
from slidecontroller import SlideController
from splashscreen import SplashScreen
from alertstab import AlertsTab
from generaltab import GeneralTab
from themestab import ThemesTab
from aboutform import AboutForm
from alertform import AlertForm
from pluginform import PluginForm
from settingsform import SettingsForm
from mediadockmanager import MediaDockManager
@ -42,4 +41,4 @@ from mainwindow import MainWindow
__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainWindow',
'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager',
'AmendThemeForm', 'MediaDockManager', 'ThemeLevel']
'AmendThemeForm', 'MediaDockManager', 'ServiceItemNoteForm']

View File

@ -25,7 +25,6 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon
from aboutdialog import Ui_AboutDialog
class AboutForm(QtGui.QDialog, Ui_AboutDialog):

View File

@ -1,102 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon
class AlertForm(QtGui.QDialog):
global log
log = logging.getLogger(u'AlertForm')
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.parent = parent
self.setupUi(self)
log.debug(u'Defined')
def setupUi(self, AlertForm):
AlertForm.setObjectName(u'AlertForm')
AlertForm.resize(370, 110)
icon = build_icon(u':/icon/openlp-logo-16x16.png')
AlertForm.setWindowIcon(icon)
self.AlertFormLayout = QtGui.QVBoxLayout(AlertForm)
self.AlertFormLayout.setSpacing(8)
self.AlertFormLayout.setMargin(8)
self.AlertFormLayout.setObjectName(u'AlertFormLayout')
self.AlertEntryWidget = QtGui.QWidget(AlertForm)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.AlertEntryWidget.sizePolicy().hasHeightForWidth())
self.AlertEntryWidget.setSizePolicy(sizePolicy)
self.AlertEntryWidget.setObjectName(u'AlertEntryWidget')
self.AlertEntryLabel = QtGui.QLabel(self.AlertEntryWidget)
self.AlertEntryLabel.setGeometry(QtCore.QRect(0, 0, 353, 16))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.AlertEntryLabel.sizePolicy().hasHeightForWidth())
self.AlertEntryLabel.setSizePolicy(sizePolicy)
self.AlertEntryLabel.setObjectName(u'AlertEntryLabel')
self.AlertEntryEditItem = QtGui.QLineEdit(self.AlertEntryWidget)
self.AlertEntryEditItem.setGeometry(QtCore.QRect(0, 20, 353, 26))
self.AlertEntryEditItem.setObjectName(u'AlertEntryEditItem')
self.AlertFormLayout.addWidget(self.AlertEntryWidget)
self.ButtonBoxWidget = QtGui.QWidget(AlertForm)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.ButtonBoxWidget.sizePolicy().hasHeightForWidth())
self.ButtonBoxWidget.setSizePolicy(sizePolicy)
self.ButtonBoxWidget.setObjectName(u'ButtonBoxWidget')
self.horizontalLayout = QtGui.QHBoxLayout(self.ButtonBoxWidget)
self.horizontalLayout.setSpacing(8)
self.horizontalLayout.setMargin(0)
self.horizontalLayout.setObjectName(u'horizontalLayout')
spacerItem = QtGui.QSpacerItem(267, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.DisplayButton = QtGui.QPushButton(self.ButtonBoxWidget)
self.DisplayButton.setObjectName(u'DisplayButton')
self.horizontalLayout.addWidget(self.DisplayButton)
self.CancelButton = QtGui.QPushButton(self.ButtonBoxWidget)
self.CancelButton.setObjectName(u'CancelButton')
self.horizontalLayout.addWidget(self.CancelButton)
self.AlertFormLayout.addWidget(self.ButtonBoxWidget)
self.retranslateUi(AlertForm)
QtCore.QObject.connect(self.CancelButton, QtCore.SIGNAL(u'clicked()'), AlertForm.close)
QtCore.QObject.connect(self.DisplayButton, QtCore.SIGNAL(u'clicked()'), self.onDisplayClicked)
QtCore.QMetaObject.connectSlotsByName(AlertForm)
def retranslateUi(self, AlertForm):
AlertForm.setWindowTitle(self.trUtf8('Alert Message'))
self.AlertEntryLabel.setText(self.trUtf8('Alert Text:'))
self.DisplayButton.setText(self.trUtf8('Display'))
self.CancelButton.setText(self.trUtf8('Cancel'))
def onDisplayClicked(self):
self.parent.mainDisplay.displayAlert(unicode(self.AlertEntryEditItem.text()))

View File

@ -694,8 +694,14 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
if self.allowPreview:
#calculate main number of rows
metrics = self._getThemeMetrics()
line_height = metrics.height()
if self.theme.display_shadow:
line_height += int(self.theme.display_shadow_size)
if self.theme.display_outline:
# pixels top/bottom
line_height += 2 * int(self.theme.display_outline_size)
page_length = \
(self.FontMainHeightSpinBox.value() / metrics.height() - 2) - 1
((self.FontMainHeightSpinBox.value()) / line_height )
log.debug(u'Page Length area height %s, metrics %s, lines %s' %
(self.FontMainHeightSpinBox.value(), metrics.height(),
page_length))
@ -719,4 +725,4 @@ class AmendThemeForm(QtGui.QDialog, Ui_AmendThemeDialog):
if self.theme.font_main_width < metrics.maxWidth() * 2 + 64:
self.theme.font_main_width = metrics.maxWidth() * 2 + 64
self.FontMainWidthSpinBox.setValue(self.theme.font_main_width)
return metrics
return metrics

View File

@ -87,6 +87,10 @@ class GeneralTab(SettingsTab):
self.SaveCheckServiceCheckBox.setObjectName(u'SaveCheckServiceCheckBox')
self.SettingsLayout.addWidget(self.SaveCheckServiceCheckBox)
self.GeneralLeftLayout.addWidget(self.SettingsGroupBox)
self.AutoPreviewCheckBox = QtGui.QCheckBox(self.SettingsGroupBox)
self.AutoPreviewCheckBox.setObjectName(u'AutoPreviewCheckBox')
self.SettingsLayout.addWidget(self.AutoPreviewCheckBox)
self.GeneralLeftLayout.addWidget(self.SettingsGroupBox)
self.GeneralLeftSpacer = QtGui.QSpacerItem(20, 40,
QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.GeneralLeftLayout.addItem(self.GeneralLeftSpacer)
@ -137,6 +141,8 @@ class GeneralTab(SettingsTab):
QtCore.SIGNAL(u'stateChanged(int)'), self.onShowSplashCheckBoxChanged)
QtCore.QObject.connect(self.SaveCheckServiceCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'), self.onSaveCheckServiceCheckBox)
QtCore.QObject.connect(self.AutoPreviewCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'), self.onAutoPreviewCheckBox)
QtCore.QObject.connect(self.NumberEdit,
QtCore.SIGNAL(u'editingFinished()'), self.onNumberEditLostFocus)
QtCore.QObject.connect(self.UsernameEdit,
@ -153,6 +159,7 @@ class GeneralTab(SettingsTab):
self.ShowSplashCheckBox.setText(self.trUtf8('Show the splash screen'))
self.SettingsGroupBox.setTitle(self.trUtf8('Application Settings'))
self.SaveCheckServiceCheckBox.setText(self.trUtf8('Prompt to save Service before starting New'))
self.AutoPreviewCheckBox.setText(self.trUtf8('Preview Next Song from Service Manager'))
self.CCLIGroupBox.setTitle(self.trUtf8('CCLI Details'))
self.NumberLabel.setText(self.trUtf8('CCLI Number:'))
self.UsernameLabel.setText(self.trUtf8('SongSelect Username:'))
@ -173,6 +180,9 @@ class GeneralTab(SettingsTab):
def onSaveCheckServiceCheckBox(self, value):
self.PromptSaveService = (value == QtCore.Qt.Checked)
def onAutoPreviewCheckBox(self, value):
self.AutoPreview = (value == QtCore.Qt.Checked)
def onNumberEditLostFocus(self):
self.CCLINumber = self.NumberEdit.displayText()
@ -183,7 +193,7 @@ class GeneralTab(SettingsTab):
self.Password = self.PasswordEdit.displayText()
def load(self):
for screen in self.screen_list:
for screen in self.screen_list.screen_list:
screen_name = u'%s %d' % (self.trUtf8('Screen'), screen[u'number'] + 1)
if screen[u'primary']:
screen_name = u'%s (%s)' % (screen_name, self.trUtf8('primary'))
@ -194,6 +204,7 @@ class GeneralTab(SettingsTab):
self.AutoOpen = str_to_bool(self.config.get_config(u'auto open', u'False'))
self.ShowSplash = str_to_bool(self.config.get_config(u'show splash', u'True'))
self.PromptSaveService = str_to_bool(self.config.get_config(u'save prompt', u'False'))
self.AutoPreview = str_to_bool(self.config.get_config(u'auto preview', u'False'))
self.CCLINumber = unicode(self.config.get_config(u'ccli number', u''))
self.Username = unicode(self.config.get_config(u'songselect username', u''))
self.Password = unicode(self.config.get_config(u'songselect password', u''))
@ -203,6 +214,7 @@ class GeneralTab(SettingsTab):
self.WarningCheckBox.setChecked(self.Warning)
self.AutoOpenCheckBox.setChecked(self.AutoOpen)
self.ShowSplashCheckBox.setChecked(self.ShowSplash)
self.AutoPreviewCheckBox.setChecked(self.AutoPreview)
self.NumberEdit.setText(self.CCLINumber)
self.UsernameEdit.setText(self.Username)
self.PasswordEdit.setText(self.Password)
@ -213,6 +225,7 @@ class GeneralTab(SettingsTab):
self.config.set_config(u'auto open', self.AutoOpen)
self.config.set_config(u'show splash', self.ShowSplash)
self.config.set_config(u'save prompt', self.PromptSaveService)
self.config.set_config(u'auto preview', self.AutoPreview)
self.config.set_config(u'ccli number', self.CCLINumber)
self.config.set_config(u'songselect username', self.Username)
self.config.set_config(u'songselect password', self.Password)
self.config.set_config(u'songselect password', self.Password)

View File

@ -25,25 +25,29 @@
import logging
import os
import time
from PyQt4 import QtCore, QtGui
from PyQt4.phonon import Phonon
from openlp.core.lib import Receiver
from openlp.core.lib import Receiver, resize_image
log = logging.getLogger(__name__)
class DisplayWidget(QtGui.QWidget):
"""
Customised version of QTableWidget which can respond to keyboard
events.
"""
global log
log = logging.getLogger(u'MainDisplay')
log.info(u'MainDisplay loaded')
def __init__(self, parent=None, name=None):
QtGui.QWidget.__init__(self, parent)
self.parent = parent
self.hotkey_map = {QtCore.Qt.Key_Return: 'servicemanager_next_item',
QtCore.Qt.Key_Space: 'live_slidecontroller_next_noloop',
QtCore.Qt.Key_Enter: 'live_slidecontroller_next_noloop',
QtCore.Qt.Key_0: 'servicemanager_next_item',
QtCore.Qt.Key_Backspace: 'live_slidecontroller_previous_noloop'}
def keyPressEvent(self, event):
if type(event) == QtGui.QKeyEvent:
@ -60,6 +64,9 @@ class DisplayWidget(QtGui.QWidget):
elif event.key() == QtCore.Qt.Key_PageDown:
Receiver.send_message(u'live_slidecontroller_last')
event.accept()
elif event.key() in self.hotkey_map:
Receiver.send_message(self.hotkey_map[event.key()])
event.accept()
elif event.key() == QtCore.Qt.Key_Escape:
self.resetDisplay()
event.accept()
@ -71,8 +78,6 @@ class MainDisplay(DisplayWidget):
"""
This is the form that is used to display things on the projector.
"""
global log
log = logging.getLogger(u'MainDisplay')
log.info(u'MainDisplay Loaded')
def __init__(self, parent, screens):
@ -90,31 +95,26 @@ class MainDisplay(DisplayWidget):
self.parent = parent
self.setWindowTitle(u'OpenLP Display')
self.screens = screens
self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0)
self.layout.setMargin(0)
self.layout.setObjectName(u'layout')
self.mediaObject = Phonon.MediaObject(self)
self.video = Phonon.VideoWidget()
self.video.setVisible(False)
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
Phonon.createPath(self.mediaObject, self.video)
Phonon.createPath(self.mediaObject, self.audio)
self.layout.insertWidget(0, self.video)
self.display = QtGui.QLabel(self)
self.display.setScaledContents(True)
self.layout.insertWidget(0, self.display)
self.display_image = QtGui.QLabel(self)
self.display_image.setScaledContents(True)
self.display_text = QtGui.QLabel(self)
self.display_text.setScaledContents(True)
self.display_alert = QtGui.QLabel(self)
self.display_alert.setScaledContents(True)
self.primary = True
self.displayBlank = False
self.blankFrame = None
self.frame = None
self.alertactive = False
self.timer_id = 0
self.firstTime = True
self.mediaLoaded = False
self.hasTransition = False
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'alert_text'), self.displayAlert)
self.mediaBackground = False
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'live_slide_hide'), self.hideDisplay)
QtCore.QObject.connect(Receiver.get_receiver(),
@ -126,11 +126,10 @@ class MainDisplay(DisplayWidget):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_play'), self.onMediaPlay)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_pause'), self.onMediaPaws)
QtCore.SIGNAL(u'media_pause'), self.onMediaPause)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'media_stop'), self.onMediaStop)
def setup(self, screenNumber):
"""
Sets up the screen on a particular screen.
@ -138,54 +137,88 @@ class MainDisplay(DisplayWidget):
"""
log.debug(u'Setup %s for %s ' %(self.screens, screenNumber))
self.setVisible(False)
screen = self.screens[screenNumber]
if screen[u'number'] != screenNumber:
# We will most probably never actually hit this bit, but just in
# case the index in the list doesn't match the screen number, we
# search for it.
for scrn in self.screens:
if scrn[u'number'] == screenNumber:
screen = scrn
break
self.setGeometry(screen[u'size'])
self.screen = self.screens.current
#Sort out screen locations and sizes
self.setGeometry(self.screen[u'size'])
self.display_alert.setGeometry(self.screen[u'size'])
self.video.setGeometry(self.screen[u'size'])
self.display_image.resize(self.screen[u'size'].width(),
self.screen[u'size'].height())
self.display_text.resize(self.screen[u'size'].width(),
self.screen[u'size'].height())
#Build a custom splash screen
self.InitialFrame = QtGui.QImage(
screen[u'size'].width(), screen[u'size'].height(),
self.screen[u'size'].width(),
self.screen[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png')
painter_image = QtGui.QPainter()
painter_image.begin(self.InitialFrame)
painter_image.fillRect(self.InitialFrame.rect(), QtCore.Qt.white)
painter_image.drawImage(
(screen[u'size'].width() - splash_image.width()) / 2,
(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)
self.frameView(self.InitialFrame)
self.display_image.setPixmap(QtGui.QPixmap.fromImage(self.InitialFrame))
self.repaint()
#Build a Black screen
painter = QtGui.QPainter()
self.blankFrame = QtGui.QImage(
screen[u'size'].width(), screen[u'size'].height(),
self.screen[u'size'].width(),
self.screen[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
painter.begin(self.blankFrame)
painter.fillRect(self.blankFrame.rect(), QtCore.Qt.black)
#TODO make black when testing finished
painter.fillRect(self.blankFrame.rect(), QtCore.Qt.red)
#build a blank transparent image
self.transparent = QtGui.QPixmap(self.screen[u'size'].width(),
self.screen[u'size'].height())
self.transparent.fill(QtCore.Qt.transparent)
self.display_alert.setPixmap(self.transparent)
self.frameView(self.transparent)
# To display or not to display?
if not screen[u'primary']:
if not self.screen[u'primary']:
self.showFullScreen()
self.primary = False
else:
self.setVisible(False)
self.primary = True
Receiver.send_message(u'screen_changed')
def resetDisplay(self):
Receiver.send_message(u'stop_display_loop')
if self.primary:
self.setVisible(False)
else:
self.showFullScreen()
def hideDisplay(self):
self.mediaLoaded = True
self.setVisible(False)
def showDisplay(self):
self.mediaLoaded = False
if not self.primary:
self.setVisible(True)
self.showFullScreen()
Receiver.send_message(u'flush_alert')
def addImageWithText(self, frame):
frame = resize_image(frame,
self.screen[u'size'].width(),
self.screen[u'size'].height() )
self.display_image.setPixmap(QtGui.QPixmap.fromImage(frame))
def setAlertSize(self, top, height):
self.display_alert.setGeometry(
QtCore.QRect(0, top,
self.screen[u'size'].width(), height))
def addAlertImage(self, frame, blank=False):
if blank:
self.display_alert.setPixmap(self.transparent)
else:
self.display_alert.setPixmap(frame)
def frameView(self, frame, transition=False):
"""
@ -194,87 +227,43 @@ class MainDisplay(DisplayWidget):
``frame``
Image frame to be rendered
"""
if self.timer_id != 0 :
self.displayAlert()
elif not self.displayBlank:
if not self.displayBlank:
if transition:
if self.hasTransition:
if self.frame[u'trans'] is not None:
self.display.setPixmap(QtGui.QPixmap.fromImage(self.frame[u'trans']))
self.repaint()
if frame[u'trans'] is not None:
self.display.setPixmap(QtGui.QPixmap.fromImage(frame[u'trans']))
self.repaint()
self.hasTransition = True
self.display.setPixmap(QtGui.QPixmap.fromImage(frame[u'main']))
if self.frame is not None:
self.display_text.setPixmap(QtGui.QPixmap.fromImage(self.frame))
self.repaint()
self.frame = None
if frame[u'trans'] is not None:
self.display_text.setPixmap(QtGui.QPixmap.fromImage(frame[u'trans']))
self.repaint()
self.frame = frame[u'trans']
self.display_text.setPixmap(QtGui.QPixmap.fromImage(frame[u'main']))
self.display_frame = frame[u'main']
self.repaint()
else:
self.display.setPixmap(QtGui.QPixmap.fromImage(frame))
if isinstance(frame, QtGui.QPixmap):
self.display_text.setPixmap(frame)
else:
self.display_text.setPixmap(QtGui.QPixmap.fromImage(frame))
self.display_frame = frame
if not self.isVisible():
self.setVisible(True)
self.showFullScreen()
self.frame = frame
def blankDisplay(self, blanked=True):
if blanked:
self.displayBlank = True
self.display.setPixmap(QtGui.QPixmap.fromImage(self.blankFrame))
self.display_text.setPixmap(QtGui.QPixmap.fromImage(self.blankFrame))
else:
self.displayBlank = False
if self.frame:
self.frameView(self.frame)
if blanked != self.parent.LiveController.blankButton.isChecked():
self.parent.LiveController.blankButton.setChecked(self.displayBlank)
self.parent.generalConfig.set_config(u'screen blank', self.displayBlank)
def displayAlert(self, text=u''):
"""
Called from the Alert Tab to display an alert
``text``
display text
"""
log.debug(u'display alert called %s' % text)
alertTab = self.parent.settingsForm.AlertsTab
if isinstance(self.frame, QtGui.QImage):
alertframe = QtGui.QPixmap.fromImage(self.frame)
else:
alertframe = QtGui.QPixmap.fromImage(self.frame[u'main'])
painter = QtGui.QPainter(alertframe)
top = alertframe.rect().height() * 0.9
painter.fillRect(
QtCore.QRect(
0, top, alertframe.rect().width(),
alertframe.rect().height() - top),
QtGui.QColor(alertTab.bg_color))
font = QtGui.QFont()
font.setFamily(alertTab.font_face)
font.setBold(True)
font.setPointSize(40)
painter.setFont(font)
painter.setPen(QtGui.QColor(alertTab.font_color))
x, y = (0, top)
metrics = QtGui.QFontMetrics(font)
painter.drawText(
x, y + metrics.height() - metrics.descent() - 1, text)
painter.end()
self.display.setPixmap(alertframe)
# check to see if we have a timer running
if self.timer_id == 0:
self.timer_id = self.startTimer(int(alertTab.timeout) * 1000)
def timerEvent(self, event):
if event.timerId() == self.timer_id:
if isinstance(self.frame, QtGui.QImage):
self.display.setPixmap(QtGui.QPixmap.fromImage(self.frame))
else:
self.display.setPixmap(QtGui.QPixmap.fromImage(self.frame[u'main']))
self.killTimer(self.timer_id)
self.timer_id = 0
if self.display_frame:
self.frameView(self.display_frame)
def onMediaQueue(self, message):
log.debug(u'Queue new media message %s' % message)
self.display.close()
self.display_image.close()
self.display_text.close()
self.display_alert.close()
file = os.path.join(message[1], message[2])
if self.firstTime:
self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
@ -287,29 +276,33 @@ class MainDisplay(DisplayWidget):
log.debug(u'Play the new media, Live ')
if not self.mediaLoaded and not self.displayBlank:
self.blankDisplay()
self.display_frame = self.blankFrame
self.firstTime = True
self.mediaLoaded = True
self.display.hide()
self.display_image.hide()
self.display_text.hide()
self.display_alert.hide()
self.video.setFullScreen(True)
self.video.setVisible(True)
self.mediaObject.play()
if self.primary:
self.setVisible(True)
self.setVisible(True)
self.hide()
def onMediaPaws(self):
def onMediaPause(self):
log.debug(u'Media paused by user')
self.mediaObject.pause()
def onMediaStop(self):
log.debug(u'Media stopped by user')
self.mediaObject.stop()
self.onMediaFinish()
def onMediaFinish(self):
log.debug(u'Reached end of media playlist')
if self.primary:
self.setVisible(False)
self.mediaObject.stop()
self.mediaObject.clearQueue()
self.mediaLoaded = False
self.video.setVisible(False)
self.display.show()
self.display_text.show()
self.display_image.show()
self.blankDisplay(False)

View File

@ -25,31 +25,55 @@
import os
import logging
import time
from PyQt4 import QtCore, QtGui
from openlp.core.ui import AboutForm, SettingsForm, AlertForm, \
from openlp.core.ui import AboutForm, SettingsForm, \
ServiceManager, ThemeManager, MainDisplay, SlideController, \
PluginForm, MediaDockManager
from openlp.core.lib import RenderManager, PluginConfig, build_icon, \
OpenLPDockWidget, SettingsManager, PluginManager, Receiver, str_to_bool
from openlp.core.utils import check_latest_version
from openlp.core.utils import check_latest_version, AppLocation
log = logging.getLogger(__name__)
media_manager_style = """
QToolBox::tab {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 palette(midlight), stop: 1.0 palette(mid));
stop: 0 palette(button), stop: 1.0 palette(dark));
border-width: 1px;
border-style: outset;
border-color: palette(midlight);
border-color: palette(dark);
border-radius: 5px;
}
QToolBox::tab:selected {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 palette(light), stop: 1.0 palette(mid));
border-color: palette(light);
stop: 0 palette(light), stop: 1.0 palette(button));
border-color: palette(button);
}
"""
class VersionThread(QtCore.QThread):
"""
A special Qt thread class to fetch the version of OpenLP from the website.
This is threaded so that it doesn't affect the loading time of OpenLP.
"""
def __init__(self, parent, app_version, generalConfig):
QtCore.QThread.__init__(self, parent)
self.parent = parent
self.app_version = app_version
self.generalConfig = generalConfig
def run(self):
"""
Run the thread.
"""
time.sleep(2)
version = check_latest_version(self.generalConfig, self.app_version)
#new version has arrived
if version != self.app_version:
Receiver.send_message(u'version_check', u'%s' % version)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
@ -218,13 +242,8 @@ class Ui_MainWindow(object):
self.settingsmanager.showServiceManager)
self.ViewServiceManagerItem.setIcon(ServiceManagerIcon)
self.ViewServiceManagerItem.setObjectName(u'ViewServiceManagerItem')
self.ToolsAlertItem = QtGui.QAction(MainWindow)
AlertIcon = build_icon(u':/tools/tools_alert.png')
self.ToolsAlertItem.setIcon(AlertIcon)
self.ToolsAlertItem.setObjectName(u'ToolsAlertItem')
self.PluginItem = QtGui.QAction(MainWindow)
#PluginIcon = build_icon(u':/tools/tools_alert.png')
self.PluginItem.setIcon(AlertIcon)
#self.PluginItem.setIcon(AlertIcon)
self.PluginItem.setObjectName(u'PluginItem')
self.HelpDocumentationItem = QtGui.QAction(MainWindow)
ContentsIcon = build_icon(u':/system/system_help_contents.png')
@ -283,7 +302,6 @@ class Ui_MainWindow(object):
self.OptionsMenu.addAction(self.OptionsViewMenu.menuAction())
self.OptionsMenu.addSeparator()
self.OptionsMenu.addAction(self.OptionsSettingsItem)
self.ToolsMenu.addAction(self.ToolsAlertItem)
self.ToolsMenu.addAction(self.PluginItem)
self.ToolsMenu.addSeparator()
self.ToolsMenu.addAction(self.ToolsAddToolItem)
@ -385,9 +403,6 @@ class Ui_MainWindow(object):
self.action_Preview_Panel.setStatusTip(
self.trUtf8('Toggle the visibility of the Preview Panel'))
self.action_Preview_Panel.setShortcut(self.trUtf8('F11'))
self.ToolsAlertItem.setText(self.trUtf8('&Alert'))
self.ToolsAlertItem.setStatusTip(self.trUtf8('Show an alert message'))
self.ToolsAlertItem.setShortcut(self.trUtf8('F7'))
self.PluginItem.setText(self.trUtf8('&Plugin List'))
self.PluginItem.setStatusTip(self.trUtf8('List the Plugins'))
self.PluginItem.setShortcut(self.trUtf8('Alt+F7'))
@ -415,8 +430,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
"""
The main window.
"""
global log
log = logging.getLogger(u'MainWindow')
log.info(u'MainWindow loaded')
def __init__(self, screens, applicationVersion):
@ -425,19 +438,16 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
plugins.
"""
QtGui.QMainWindow.__init__(self)
self.screenList = screens
self.screens = screens
self.applicationVersion = applicationVersion
self.serviceNotSaved = False
self.settingsmanager = SettingsManager(screens)
self.generalConfig = PluginConfig(u'General')
self.mainDisplay = MainDisplay(self, screens)
self.alertForm = AlertForm(self)
self.aboutForm = AboutForm(self, applicationVersion)
self.settingsForm = SettingsForm(self.screenList, self, self)
self.settingsForm = SettingsForm(self.screens, self, self)
# Set up the path with plugins
pluginpath = os.path.split(os.path.abspath(__file__))[0]
pluginpath = os.path.abspath(
os.path.join(pluginpath, u'..', u'..', u'plugins'))
pluginpath = AppLocation.get_directory(AppLocation.PluginsDir)
self.plugin_manager = PluginManager(pluginpath)
self.plugin_helpers = {}
# Set up the interface
@ -476,14 +486,14 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.action_Preview_Panel.setChecked)
QtCore.QObject.connect(self.HelpAboutItem,
QtCore.SIGNAL(u'triggered()'), self.onHelpAboutItemClicked)
QtCore.QObject.connect(self.ToolsAlertItem,
QtCore.SIGNAL(u'triggered()'), self.onToolsAlertItemClicked)
QtCore.QObject.connect(self.PluginItem,
QtCore.SIGNAL(u'triggered()'), self.onPluginItemClicked)
QtCore.QObject.connect(self.OptionsSettingsItem,
QtCore.SIGNAL(u'triggered()'), self.onOptionsSettingsItemClicked)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'update_global_theme'), self.defaultThemeChanged)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'version_check'), self.versionCheck)
QtCore.QObject.connect(self.FileNewItem,
QtCore.SIGNAL(u'triggered()'),
self.ServiceManagerContents.onNewService)
@ -500,7 +510,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
#RenderManager needs to call ThemeManager and
#ThemeManager needs to call RenderManager
self.RenderManager = RenderManager(self.ThemeManagerContents,
self.screenList, self.getMonitorNumber())
self.screens, self.getMonitorNumber())
#Define the media Dock Manager
self.mediaDockManager = MediaDockManager(self.MediaToolBox)
log.info(u'Load Plugins')
@ -511,6 +521,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.plugin_helpers[u'service'] = self.ServiceManagerContents
self.plugin_helpers[u'settings'] = self.settingsForm
self.plugin_helpers[u'toolbox'] = self.mediaDockManager
self.plugin_helpers[u'maindisplay'] = self.mainDisplay
self.plugin_manager.find_plugins(pluginpath, self.plugin_helpers)
# hook methods have to happen after find_plugins. Find plugins needs
# the controllers hence the hooks have moved from setupUI() to here
@ -536,20 +547,18 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
log.info(u'Load data from Settings')
self.settingsForm.postSetUp()
def versionCheck(self):
def versionCheck(self, version):
"""
Checks the version of the Application called from openlp.pyw
"""
app_version = self.applicationVersion[u'full']
version = check_latest_version(self.generalConfig, app_version)
if app_version != version:
version_text = unicode(self.trUtf8('OpenLP version %s has been updated '
'to version %s\n\nYou can obtain the latest version from http://openlp.org'))
QtGui.QMessageBox.question(None,
self.trUtf8('OpenLP Version Updated'),
version_text % (app_version, version),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok),
QtGui.QMessageBox.Ok)
version_text = unicode(self.trUtf8('OpenLP version %s has been updated '
'to version %s\n\nYou can obtain the latest version from http://openlp.org'))
QtGui.QMessageBox.question(self,
self.trUtf8('OpenLP Version Updated'),
version_text % (app_version, version),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok),
QtGui.QMessageBox.Ok)
def getMonitorNumber(self):
"""
@ -558,11 +567,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
monitor number does not exist.
"""
screen_number = int(self.generalConfig.get_config(u'monitor', 0))
monitor_exists = False
for screen in self.screenList:
if screen[u'number'] == screen_number:
monitor_exists = True
if not monitor_exists:
if not self.screens.screen_exists(screen_number):
screen_number = 0
return screen_number
@ -580,12 +585,17 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.ServiceManagerContents.onLoadService(True)
if str_to_bool(self.generalConfig.get_config(u'screen blank', False)) \
and str_to_bool(self.generalConfig.get_config(u'blank warning', False)):
QtGui.QMessageBox.question(None,
self.LiveController.onBlankDisplay(True)
QtGui.QMessageBox.question(self,
self.trUtf8('OpenLP Main Display Blanked'),
self.trUtf8('The Main Display has been blanked out'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok),
QtGui.QMessageBox.Ok)
self.LiveController.blankButton.setChecked(True)
def versionThread(self):
app_version = self.applicationVersion[u'full']
vT = VersionThread(self, app_version, self.generalConfig)
vT.start()
def onHelpAboutItemClicked(self):
"""
@ -594,12 +604,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.aboutForm.applicationVersion = self.applicationVersion
self.aboutForm.exec_()
def onToolsAlertItemClicked(self):
"""
Show the Alert form
"""
self.alertForm.exec_()
def onPluginItemClicked(self):
"""
Show the Plugin form
@ -613,7 +617,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
"""
self.settingsForm.exec_()
updated_display = self.getMonitorNumber()
if updated_display != self.RenderManager.current_display:
if updated_display != self.screens.current_display:
self.screens.set_current_display(updated_display)
self.RenderManager.update_display(updated_display)
self.mainDisplay.setup(updated_display)
self.activateWindow()
@ -623,7 +628,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
Hook to close the main window and display windows on exit
"""
if self.serviceNotSaved:
ret = QtGui.QMessageBox.question(None,
ret = QtGui.QMessageBox.question(self,
self.trUtf8('Save Changes to Service?'),
self.trUtf8('Your service has changed, do you want to save those changes?'),
QtGui.QMessageBox.StandardButtons(
@ -704,4 +709,4 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def togglePreviewPanel(self):
previewBool = self.PreviewController.Panel.isVisible()
self.PreviewController.Panel.setVisible(not previewBool)
self.settingsmanager.togglePreviewPanel(not previewBool)
self.settingsmanager.togglePreviewPanel(not previewBool)

View File

@ -25,7 +25,7 @@
import logging
log = logging.getLogger(u'MediaDockManager')
log = logging.getLogger(__name__)
class MediaDockManager(object):
@ -58,4 +58,4 @@ class MediaDockManager(object):
if self.media_dock.widget(dock_index):
if self.media_dock.widget(dock_index).ConfigSection == name:
self.media_dock.widget(dock_index).hide()
self.media_dock.removeItem(dock_index)
self.media_dock.removeItem(dock_index)

View File

@ -30,9 +30,9 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib.plugin import PluginStatus
from plugindialog import Ui_PluginViewDialog
log = logging.getLogger(__name__)
class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
global log
log = logging.getLogger(u'PluginForm')
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
@ -126,4 +126,4 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
elif self.activePlugin.status == PluginStatus.Disabled:
status_text = 'Disabled'
self.PluginListWidget.currentItem().setText(
u'%s (%s)' % (self.activePlugin.name, status_text))
u'%s (%s)' % (self.activePlugin.name, status_text))

72
openlp/core/ui/screen.py Normal file
View File

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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
log = logging.getLogger(__name__)
class ScreenList(object):
"""
Wrapper to handle the parameters of the display screen
"""
log.info(u'Screen loaded')
def __init__(self):
self.preview = None
self.current = None
self.screen_list = []
self.count = 0
self.current_display = 0
def add_screen(self, screen):
if screen[u'primary']:
self.current = screen
self.screen_list.append(screen)
self.count += 1
def screen_exists(self, number):
for screen in self.screen_list:
if screen[u'number'] == number:
return True
return False
def set_current_display(self, number):
if number + 1 > self.count:
self.current = self.screen_list[0]
self.current_display = 0
else:
self.current = self.screen_list[number]
self.preview = self.current
self.current_display = number
if self.count == 1:
self.preview = self.screen_list[0]
# if self.screen[u'number'] != screenNumber:
# # We will most probably never actually hit this bit, but just in
# # case the index in the list doesn't match the screen number, we
# # search for it.
# for scrn in self.screens:
# if scrn[u'number'] == screenNumber:
# self.screen = scrn
# break

View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from PyQt4 import QtCore, QtGui
class Ui_ServiceNoteEdit(object):
def setupUi(self, ServiceNoteEdit):
ServiceNoteEdit.setObjectName(u'ServiceNoteEdit')
ServiceNoteEdit.resize(400, 243)
self.widget = QtGui.QWidget(ServiceNoteEdit)
self.widget.setGeometry(QtCore.QRect(20, 10, 361, 223))
self.widget.setObjectName(u'widget')
self.verticalLayout = QtGui.QVBoxLayout(self.widget)
self.verticalLayout.setObjectName(u'verticalLayout')
self.textEdit = QtGui.QTextEdit(self.widget)
self.textEdit.setObjectName(u'textEdit')
self.verticalLayout.addWidget(self.textEdit)
self.buttonBox = QtGui.QDialogButtonBox(self.widget)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Save)
self.buttonBox.setObjectName(u'buttonBox')
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(ServiceNoteEdit)
QtCore.QMetaObject.connectSlotsByName(ServiceNoteEdit)
def retranslateUi(self, ServiceNoteEdit):
ServiceNoteEdit.setWindowTitle(self.trUtf8('Service Item Notes'))

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from PyQt4 import QtCore, QtGui
from serviceitemdialog import Ui_ServiceNoteEdit
class ServiceItemNoteForm(QtGui.QDialog, Ui_ServiceNoteEdit):
"""
This is the form that is used to edit the verses of the song.
"""
def __init__(self, parent=None):
"""
Constructor
"""
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
QtCore.QObject.connect(self.buttonBox,
QtCore.SIGNAL(u'accepted()'),
self.accept)
QtCore.QObject.connect(self.buttonBox,
QtCore.SIGNAL(u'rejected()'),
self.reject)

View File

@ -28,10 +28,13 @@ import logging
import cPickle
import zipfile
log = logging.getLogger(__name__)
from PyQt4 import QtCore, QtGui
from openlp.core.lib import PluginConfig, OpenLPToolbar, ServiceItem, \
ServiceItemType, contextMenuAction, contextMenuSeparator, contextMenu, \
Receiver, contextMenu, str_to_bool
contextMenuAction, Receiver, str_to_bool, build_icon
from openlp.core.ui import ServiceItemNoteForm
class ServiceManagerList(QtGui.QTreeWidget):
@ -39,23 +42,6 @@ class ServiceManagerList(QtGui.QTreeWidget):
QtGui.QTreeWidget.__init__(self,parent)
self.parent = parent
# def mousePressEvent(self, event):
# if event.button() == QtCore.Qt.RightButton:
# item = self.itemAt(event.pos())
# parentitem = item.parent()
# if parentitem is None:
# pos = item.data(0, QtCore.Qt.UserRole).toInt()[0]
# else:
# pos = parentitem.data(0, QtCore.Qt.UserRole).toInt()[0]
# serviceItem = self.parent.serviceItems[pos - 1]
# if serviceItem[u'data'].edit_enabled:
# self.parent.editAction.setVisible(True)
# else:
# self.parent.editAction.setVisible(False)
# event.accept()
# else:
# event.ignore()
def keyPressEvent(self, event):
if type(event) == QtGui.QKeyEvent:
#here accept the event and do something
@ -91,6 +77,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
just tell it what plugin to call
"""
if event.buttons() != QtCore.Qt.LeftButton:
event.ignore()
return
drag = QtGui.QDrag(self)
mimeData = QtCore.QMimeData()
@ -105,9 +92,6 @@ class ServiceManager(QtGui.QWidget):
the resources used into one OSZ file for use on any OpenLP v2 installation.
Also handles the UI tasks of moving things up and down etc.
"""
global log
log = logging.getLogger(u'ServiceManager')
def __init__(self, parent):
"""
Sets up the service manager, toolbars, list view, et al.
@ -121,6 +105,7 @@ class ServiceManager(QtGui.QWidget):
#Indicates if remoteTriggering is active. If it is the next addServiceItem call
#will replace the currently selected one.
self.remoteEditTriggered = False
self.serviceItemNoteForm = ServiceItemNoteForm()
#start with the layout
self.Layout = QtGui.QVBoxLayout(self)
self.Layout.setSpacing(0)
@ -161,62 +146,38 @@ class ServiceManager(QtGui.QWidget):
self.ServiceManagerList.setAlternatingRowColors(True)
self.ServiceManagerList.setHeaderHidden(True)
self.ServiceManagerList.setExpandsOnDoubleClick(False)
self.ServiceManagerList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ServiceManagerList.customContextMenuRequested.connect(self.contextMenu)
self.ServiceManagerList.setObjectName(u'ServiceManagerList')
# enable drop
self.ServiceManagerList.__class__.dragEnterEvent = self.dragEnterEvent
self.ServiceManagerList.__class__.dragMoveEvent = self.dragEnterEvent
self.ServiceManagerList.__class__.dropEvent = self.dropEvent
# Add a context menu to the service manager list
self.ServiceManagerList.setContextMenuPolicy(
QtCore.Qt.ActionsContextMenu)
self.editAction = contextMenuAction(
self.ServiceManagerList, ':/system/system_live.png',
self.trUtf8('&Edit Item'), self.remoteEdit)
self.ServiceManagerList.addAction(self.editAction)
self.ServiceManagerList.addAction(contextMenuSeparator(
self.ServiceManagerList))
self.ServiceManagerList.addAction(contextMenuAction(
self.ServiceManagerList, ':/system/system_preview.png',
self.trUtf8('&Preview Verse'), self.makePreview))
self.ServiceManagerList.addAction(contextMenuAction(
self.ServiceManagerList, ':/system/system_live.png',
self.trUtf8('&Show Live'), self.makeLive))
self.ServiceManagerList.addAction(contextMenuSeparator(
self.ServiceManagerList))
self.ServiceManagerList.addAction(contextMenuAction(
self.ServiceManagerList, ':/services/service_delete',
self.trUtf8('&Remove from Service'), self.onDeleteFromService))
self.ServiceManagerList.addAction(contextMenuSeparator(
self.ServiceManagerList))
self.ThemeMenu = contextMenu(
self.ServiceManagerList, '',
self.trUtf8('&Change Item Theme'))
self.ServiceManagerList.addAction(self.ThemeMenu.menuAction())
self.Layout.addWidget(self.ServiceManagerList)
# Add the bottom toolbar
self.OrderToolbar = OpenLPToolbar(self)
self.OrderToolbar.addToolbarButton(
self.trUtf8('Move to top'), u':/services/service_top.png',
self.trUtf8('Move to &top'), u':/services/service_top.png',
self.trUtf8('Move to top'), self.onServiceTop)
self.OrderToolbar.addToolbarButton(
self.trUtf8('Move up'), u':/services/service_up.png',
self.trUtf8('Move &up'), u':/services/service_up.png',
self.trUtf8('Move up order'), self.onServiceUp)
self.OrderToolbar.addToolbarButton(
self.trUtf8('Move down'), u':/services/service_down.png',
self.trUtf8('Move &down'), u':/services/service_down.png',
self.trUtf8('Move down order'), self.onServiceDown)
self.OrderToolbar.addToolbarButton(
self.trUtf8('Move to bottom'), u':/services/service_bottom.png',
self.trUtf8('Move to &bottom'), u':/services/service_bottom.png',
self.trUtf8('Move to end'), self.onServiceEnd)
self.OrderToolbar.addSeparator()
self.OrderToolbar.addToolbarButton(
self.trUtf8('Delete From Service'), u':/services/service_delete.png',
self.trUtf8('&Delete From Service'), u':/services/service_delete.png',
self.trUtf8('Delete From Service'), self.onDeleteFromService)
self.Layout.addWidget(self.OrderToolbar)
# Connect up our signals and slots
QtCore.QObject.connect(self.ThemeComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onThemeComboBoxSelected)
QtCore.QObject.connect(self.ServiceManagerList,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.makeLive)
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.makeLive)
QtCore.QObject.connect(self.ServiceManagerList,
QtCore.SIGNAL(u'itemCollapsed(QTreeWidgetItem*)'), self.collapsed)
QtCore.QObject.connect(self.ServiceManagerList,
@ -225,18 +186,99 @@ class ServiceManager(QtGui.QWidget):
QtCore.SIGNAL(u'update_themes'), self.updateThemeList)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'remote_edit_clear'), self.onRemoteEditClear)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'presentation types'), self.onPresentationTypes)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'servicemanager_next_item'), self.nextItem)
# Last little bits of setting up
self.config = PluginConfig(u'ServiceManager')
self.servicePath = self.config.get_data_path()
self.service_theme = unicode(
self.config.get_config(u'service theme', u''))
#build the context menu
self.menu = QtGui.QMenu()
self.editAction = self.menu.addAction(self.trUtf8('&Edit Item'))
self.editAction.setIcon(build_icon(u':/services/service_edit.png'))
self.notesAction = self.menu.addAction(self.trUtf8('&Notes'))
self.notesAction.setIcon(build_icon(u':/services/service_notes.png'))
self.deleteAction = self.menu.addAction(self.trUtf8('&Delete From Service'))
self.deleteAction.setIcon(build_icon(u':/services/service_delete.png'))
self.sep1 = self.menu.addAction(u'')
self.sep1.setSeparator(True)
self.previewAction = self.menu.addAction(self.trUtf8('&Preview Verse'))
self.previewAction.setIcon(build_icon(u':/system/system_preview.png'))
self.liveAction = self.menu.addAction(self.trUtf8('&Live Verse'))
self.liveAction.setIcon(build_icon(u':/system/system_live.png'))
self.sep2 = self.menu.addAction(u'')
self.sep2.setSeparator(True)
self.themeMenu = QtGui.QMenu(self.trUtf8(u'&Change Item Theme'))
self.menu.addMenu(self.themeMenu)
def contextMenu(self, point):
item = self.ServiceManagerList.itemAt(point)
if item.parent() is None:
pos = item.data(0, QtCore.Qt.UserRole).toInt()[0]
else:
pos = item.parent().data(0, QtCore.Qt.UserRole).toInt()[0]
serviceItem = self.serviceItems[pos - 1]
self.editAction.setVisible(False)
self.notesAction.setVisible(False)
if serviceItem[u'service_item'].edit_enabled:
self.editAction.setVisible(True)
if item.parent() is None:
self.notesAction.setVisible(True)
self.themeMenu.menuAction().setVisible(False)
if serviceItem[u'service_item'].is_text():
self.themeMenu.menuAction().setVisible(True)
action = self.menu.exec_(self.ServiceManagerList.mapToGlobal(point))
if action == self.editAction:
self.remoteEdit()
if action == self.deleteAction:
self.onDeleteFromService()
if action == self.notesAction:
self.onServiceItemNoteForm()
if action == self.previewAction:
self.makePreview()
if action == self.liveAction:
self.makeLive()
def onPresentationTypes(self, presentation_types):
self.presentation_types = presentation_types
def onServiceItemNoteForm(self):
item, count = self.findServiceItem()
self.serviceItemNoteForm.textEdit.setPlainText(
self.serviceItems[item][u'service_item'].notes)
if self.serviceItemNoteForm.exec_():
self.serviceItems[item][u'service_item'].notes = \
self.serviceItemNoteForm.textEdit.toPlainText()
self.repaintServiceList(item, 0)
def nextItem(self):
"""
Called by the SlideController to select the
next service item
"""
if len(self.ServiceManagerList.selectedItems()) == 0:
return
selected = self.ServiceManagerList.selectedItems()[0]
lookFor = 0
serviceIterator = QtGui.QTreeWidgetItemIterator(self.ServiceManagerList)
while serviceIterator.value():
if lookFor == 1 and serviceIterator.value().parent() is None:
self.ServiceManagerList.setCurrentItem(serviceIterator.value())
self.makeLive()
return
if serviceIterator.value() == selected:
lookFor = 1
serviceIterator += 1
def onMoveSelectionUp(self):
"""
Moves the selection up the window
Called by the up arrow
"""
serviceIterator = QTreeWidgetItemIterator(self.ServiceManagerList)
serviceIterator = QtGui.QTreeWidgetItemIterator(self.ServiceManagerList)
tempItem = None
setLastItem = False
while serviceIterator:
@ -261,7 +303,7 @@ class ServiceManager(QtGui.QWidget):
Moves the selection down the window
Called by the down arrow
"""
serviceIterator = QTreeWidgetItemIterator(self.ServiceManagerList)
serviceIterator = QtGui.QTreeWidgetItemIterator(self.ServiceManagerList)
firstItem = serviceIterator
setSelected = False
while serviceIterator:
@ -348,7 +390,7 @@ class ServiceManager(QtGui.QWidget):
if self.parent.serviceNotSaved and \
str_to_bool(PluginConfig(u'General').
get_config(u'save prompt', u'False')):
ret = QtGui.QMessageBox.question(None,
ret = QtGui.QMessageBox.question(self,
self.trUtf8('Save Changes to Service?'),
self.trUtf8('Your service is unsaved, do you want to save those '
'changes before creating a new one ?'),
@ -390,19 +432,35 @@ class ServiceManager(QtGui.QWidget):
for itemcount, item in enumerate(self.serviceItems):
serviceitem = item[u'service_item']
treewidgetitem = QtGui.QTreeWidgetItem(self.ServiceManagerList)
treewidgetitem.setText(0,serviceitem.title)
treewidgetitem.setIcon(0,serviceitem.iconic_representation)
if serviceitem.notes:
icon = QtGui.QImage(serviceitem.icon)
icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation)
overlay = QtGui.QImage(':/services/service_item_notes.png')
overlay = overlay.scaled(80, 80, QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation)
painter = QtGui.QPainter(icon)
painter.drawImage(0, 0, overlay)
painter.end()
treewidgetitem.setIcon(0, build_icon(icon))
else:
treewidgetitem.setIcon(0, serviceitem.iconic_representation)
treewidgetitem.setText(0, serviceitem.title)
treewidgetitem.setToolTip(0, serviceitem.notes)
treewidgetitem.setData(0, QtCore.Qt.UserRole,
QtCore.QVariant(item[u'order']))
treewidgetitem.setExpanded(item[u'expanded'])
for count, frame in enumerate(serviceitem.get_frames()):
treewidgetitem1 = QtGui.QTreeWidgetItem(treewidgetitem)
text = frame[u'title']
treewidgetitem1.setText(0,text[:40])
treewidgetitem1.setText(0, text[:40])
treewidgetitem1.setData(0, QtCore.Qt.UserRole,
QtCore.QVariant(count))
if serviceItem == itemcount and serviceItemCount == count:
self.ServiceManagerList.setCurrentItem(treewidgetitem1)
#preserve expanding status as setCurrentItem sets it to True
temp = item[u'expanded']
self.ServiceManagerList.setCurrentItem(treewidgetitem1)
item[u'expanded'] = temp
treewidgetitem.setExpanded(item[u'expanded'])
def onSaveService(self, quick=False):
"""
@ -433,10 +491,10 @@ class ServiceManager(QtGui.QWidget):
for item in self.serviceItems:
service.append({u'serviceitem':item[u'service_item'].get_service_repr()})
if item[u'service_item'].uses_file():
for frame in item[u'service_item'].get_frames:
for frame in item[u'service_item'].get_frames():
path_from = unicode(os.path.join(
item[u'service_item'].service_item_path,
frame.get_frame_title()))
frame[u'title']))
zip.write(path_from)
file = open(servicefile, u'wb')
cPickle.dump(service, file)
@ -499,7 +557,8 @@ class ServiceManager(QtGui.QWidget):
serviceitem = ServiceItem()
serviceitem.RenderManager = self.parent.RenderManager
serviceitem.set_from_service(item, self.servicePath)
self.addServiceItem(serviceitem)
if self.validateItem(serviceitem):
self.addServiceItem(serviceitem)
try:
if os.path.isfile(p_file):
os.remove(p_file)
@ -516,6 +575,14 @@ class ServiceManager(QtGui.QWidget):
self.serviceName = name[len(name) - 1]
self.parent.serviceChanged(True, self.serviceName)
def validateItem(self, serviceItem):
# print "---"
# print serviceItem.name
# print serviceItem.title
# print serviceItem.service_item_path
# print serviceItem.service_item_type
return True
def cleanUp(self):
"""
Empties the servicePath of temporary files
@ -538,13 +605,20 @@ class ServiceManager(QtGui.QWidget):
self.regenerateServiceItems()
def regenerateServiceItems(self):
if len(self.serviceItems) > 0:
#force reset of renderer as theme data has changed
self.parent.RenderManager.themedata = None
if self.serviceItems:
tempServiceItems = self.serviceItems
self.onNewService()
self.ServiceManagerList.clear()
self.serviceItems = []
self.isNew = True
for item in tempServiceItems:
self.addServiceItem(item[u'service_item'])
self.addServiceItem(item[u'service_item'], False, item[u'expanded'])
#Set to False as items may have changed rendering
#does not impact the saved song so True may aslo be valid
self.parent.serviceChanged(False, self.serviceName)
def addServiceItem(self, item):
def addServiceItem(self, item, rebuild=False, expand=True):
"""
Add a Service item to the list
@ -564,13 +638,16 @@ class ServiceManager(QtGui.QWidget):
if sitem == -1:
self.serviceItems.append({u'service_item': item,
u'order': len(self.serviceItems) + 1,
u'expanded':True})
u'expanded':expand})
self.repaintServiceList(len(self.serviceItems) + 1, 0)
else:
self.serviceItems.insert(sitem + 1, {u'service_item': item,
u'order': len(self.serviceItems)+1,
u'expanded':True})
u'expanded':expand})
self.repaintServiceList(sitem + 1, 0)
#if rebuilding list make sure live is fixed.
if rebuild:
self.parent.LiveController.replaceServiceManagerItem(item)
self.parent.serviceChanged(False, self.serviceName)
def makePreview(self):
@ -581,6 +658,7 @@ class ServiceManager(QtGui.QWidget):
self.parent.PreviewController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], count)
def makeLive(self):
"""
Send the current item to the Live slide controller
@ -588,6 +666,13 @@ class ServiceManager(QtGui.QWidget):
item, count = self.findServiceItem()
self.parent.LiveController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], count)
if str_to_bool(PluginConfig(u'General').
get_config(u'auto preview', u'False')):
item += 1
if self.serviceItems and item < len(self.serviceItems) and \
self.serviceItems[item][u'service_item'].autoPreviewAllowed:
self.parent.PreviewController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], 0)
def remoteEdit(self):
"""
@ -617,7 +702,7 @@ class ServiceManager(QtGui.QWidget):
else:
pos = parentitem.data(0, QtCore.Qt.UserRole).toInt()[0]
count = item.data(0, QtCore.Qt.UserRole).toInt()[0]
#adjuest for zero based arrays
#adjust for zero based arrays
pos = pos - 1
return pos, count
@ -646,7 +731,7 @@ class ServiceManager(QtGui.QWidget):
if plugin == u'ServiceManager':
startpos, startCount = self.findServiceItem()
item = self.ServiceManagerList.itemAt(event.pos())
if item == None:
if item is None:
endpos = len(self.serviceItems)
else:
parentitem = item.parent()
@ -674,7 +759,7 @@ class ServiceManager(QtGui.QWidget):
A list of current themes to be displayed
"""
self.ThemeComboBox.clear()
self.ThemeMenu.clear()
self.themeMenu.clear()
self.ThemeComboBox.addItem(u'')
for theme in theme_list:
self.ThemeComboBox.addItem(theme)
@ -682,7 +767,7 @@ class ServiceManager(QtGui.QWidget):
self.ServiceManagerList,
None,
theme , self.onThemeChangeAction)
self.ThemeMenu.addAction(action)
self.themeMenu.addAction(action)
id = self.ThemeComboBox.findText(self.service_theme,
QtCore.Qt.MatchExactly)
# Not Found
@ -697,4 +782,4 @@ class ServiceManager(QtGui.QWidget):
theme = unicode(self.sender().text())
item, count = self.findServiceItem()
self.serviceItems[item][u'service_item'].theme = theme
self.regenerateServiceItems()
self.regenerateServiceItems()

View File

@ -27,11 +27,11 @@ import logging
from PyQt4 import QtGui
from openlp.core.ui import GeneralTab, ThemesTab, AlertsTab
from openlp.core.ui import GeneralTab, ThemesTab
from openlp.core.lib import Receiver
from settingsdialog import Ui_SettingsDialog
log = logging.getLogger(u'SettingsForm')
log = logging.getLogger(__name__)
class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
@ -44,9 +44,6 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
# Themes tab
self.ThemesTab = ThemesTab(mainWindow)
self.addTab(u'Themes', self.ThemesTab)
# Alert tab
self.AlertsTab = AlertsTab()
self.addTab(u'Alerts', self.AlertsTab)
def addTab(self, name, tab):
log.info(u'Adding %s tab' % tab.tabTitle)
@ -73,4 +70,4 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
def postSetUp(self):
for tab_index in range(0, self.SettingsTabWidget.count()):
self.SettingsTabWidget.widget(tab_index).postSetUp()
self.SettingsTabWidget.widget(tab_index).postSetUp()

View File

@ -30,7 +30,10 @@ import os
from PyQt4 import QtCore, QtGui
from PyQt4.phonon import Phonon
from openlp.core.lib import OpenLPToolbar, Receiver, str_to_bool, PluginConfig
from openlp.core.lib import OpenLPToolbar, Receiver, str_to_bool, \
PluginConfig, resize_image
log = logging.getLogger(__name__)
class SlideList(QtGui.QTableWidget):
"""
@ -40,6 +43,11 @@ class SlideList(QtGui.QTableWidget):
def __init__(self, parent=None, name=None):
QtGui.QTableWidget.__init__(self, parent.Controller)
self.parent = parent
self.hotkey_map = {QtCore.Qt.Key_Return: 'servicemanager_next_item',
QtCore.Qt.Key_Space: 'live_slidecontroller_next_noloop',
QtCore.Qt.Key_Enter: 'live_slidecontroller_next_noloop',
QtCore.Qt.Key_0: 'servicemanager_next_item',
QtCore.Qt.Key_Backspace: 'live_slidecontroller_previous_noloop'}
def keyPressEvent(self, event):
if type(event) == QtGui.QKeyEvent:
@ -56,6 +64,9 @@ class SlideList(QtGui.QTableWidget):
elif event.key() == QtCore.Qt.Key_PageDown:
self.parent.onSlideSelectedLast()
event.accept()
elif event.key() in self.hotkey_map and self.parent.isLive:
Receiver.send_message(self.hotkey_map[event.key()])
event.accept()
event.ignore()
else:
event.ignore()
@ -65,9 +76,6 @@ class SlideController(QtGui.QWidget):
SlideController is the slide controller widget. This widget is what the
user uses to control the displaying of verses/slides/etc on the screen.
"""
global log
log = logging.getLogger(u'SlideController')
def __init__(self, parent, settingsmanager, isLive=False):
"""
Set up the Slide Controller.
@ -99,15 +107,14 @@ class SlideController(QtGui.QWidget):
# Type label for the top of the slide controller
self.TypeLabel = QtGui.QLabel(self.Panel)
if self.isLive:
self.TypeLabel.setText(u'<strong>%s</strong>' %
self.trUtf8('Live'))
self.TypeLabel.setText(self.trUtf8('Live'))
self.split = 1
prefix = u'live_slidecontroller'
else:
self.TypeLabel.setText(u'<strong>%s</strong>' %
self.trUtf8('Preview'))
self.TypeLabel.setText(self.trUtf8('Preview'))
self.split = 0
prefix = u'preview_slidecontroller'
self.TypeLabel.setStyleSheet(u'font-weight: bold; font-size: 12pt;')
self.TypeLabel.setAlignment(QtCore.Qt.AlignCenter)
self.PanelLayout.addWidget(self.TypeLabel)
# Splitter
@ -135,6 +142,7 @@ class SlideController(QtGui.QWidget):
self.PreviewListWidget.setEditTriggers(
QtGui.QAbstractItemView.NoEditTriggers)
self.PreviewListWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.PreviewListWidget.setAlternatingRowColors(True)
self.ControllerLayout.addWidget(self.PreviewListWidget)
# Build the full toolbar
self.Toolbar = OpenLPToolbar(self)
@ -163,9 +171,9 @@ class SlideController(QtGui.QWidget):
self.Toolbar.addToolbarSeparator(u'Close Separator')
self.blankButton = self.Toolbar.addToolbarButton(
u'Blank Screen', u':/slides/slide_close.png',
self.trUtf8('Blank Screen'), self.onBlankScreen, True)
self.trUtf8('Blank Screen'), self.onBlankDisplay, True)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'live_slide_blank'), self.onBlankDisplay)
QtCore.SIGNAL(u'live_slide_blank'), self.blankScreen)
if not self.isLive:
self.Toolbar.addToolbarSeparator(u'Close Separator')
self.Toolbar.addToolbarButton(
@ -173,7 +181,7 @@ class SlideController(QtGui.QWidget):
self.trUtf8('Move to live'), self.onGoLive)
self.Toolbar.addToolbarSeparator(u'Close Separator')
self.Toolbar.addToolbarButton(
u'Edit Song', u':/songs/song_edit.png',
u'Edit Song', u':/services/service_edit.png',
self.trUtf8('Edit and re-preview Song'), self.onEditSong)
if isLive:
self.Toolbar.addToolbarSeparator(u'Loop Separator')
@ -184,6 +192,8 @@ class SlideController(QtGui.QWidget):
u'Stop Loop', u':/media/media_stop.png',
self.trUtf8('Stop continuous loop'), self.onStopLoop)
self.DelaySpinBox = QtGui.QSpinBox()
self.DelaySpinBox.setMinimum(1)
self.DelaySpinBox.setMaximum(180)
self.Toolbar.addToolbarWidget(
u'Image SpinBox', self.DelaySpinBox)
self.DelaySpinBox.setSuffix(self.trUtf8('s'))
@ -235,6 +245,9 @@ class SlideController(QtGui.QWidget):
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
Phonon.createPath(self.mediaObject, self.video)
Phonon.createPath(self.mediaObject, self.audio)
if not self.isLive:
self.video.setGeometry(QtCore.QRect(0, 0, 300, 225))
self.video.setVisible(False)
self.SlideLayout.insertWidget(0, self.video)
# Actual preview screen
self.SlidePreview = QtGui.QLabel(self)
@ -246,7 +259,8 @@ class SlideController(QtGui.QWidget):
self.SlidePreview.sizePolicy().hasHeightForWidth())
self.SlidePreview.setSizePolicy(sizePolicy)
self.SlidePreview.setFixedSize(
QtCore.QSize(self.settingsmanager.slidecontroller_image,self.settingsmanager.slidecontroller_image / 1.3 ))
QtCore.QSize(self.settingsmanager.slidecontroller_image,
self.settingsmanager.slidecontroller_image / 1.3 ))
self.SlidePreview.setFrameShape(QtGui.QFrame.Box)
self.SlidePreview.setFrameShadow(QtGui.QFrame.Plain)
self.SlidePreview.setLineWidth(1)
@ -257,8 +271,6 @@ class SlideController(QtGui.QWidget):
# Signals
QtCore.QObject.connect(self.PreviewListWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
QtCore.QObject.connect(self.PreviewListWidget,
QtCore.SIGNAL(u'activated(QModelIndex)'), self.onSlideSelected)
if isLive:
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'update_spin_delay'), self.receiveSpinDelay)
@ -268,12 +280,19 @@ class SlideController(QtGui.QWidget):
else:
self.Toolbar.makeWidgetsInvisible(self.song_edit_list)
self.Mediabar.setVisible(False)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'stop_display_loop'), self.onStopLoop)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_first' % prefix), self.onSlideSelectedFirst)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_next' % prefix), self.onSlideSelectedNext)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_previous' % prefix), self.onSlideSelectedPrevious)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_next_noloop' % prefix), self.onSlideSelectedNextNoloop)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_previous_noloop' % prefix),
self.onSlideSelectedPreviousNoloop)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_last' % prefix), self.onSlideSelectedLast)
QtCore.QObject.connect(Receiver.get_receiver(),
@ -393,9 +412,13 @@ class SlideController(QtGui.QWidget):
if item.is_media():
self.onMediaStart(item)
elif item.is_command():
if self.isLive:
blanked = self.blankButton.isChecked()
else:
blanked = False
Receiver.send_message(u'%s_start' % item.name.lower(), \
[item.title, item.service_item_path,
item.get_frame_title(), slideno, self.isLive])
item.get_frame_title(), slideno, self.isLive, blanked])
self.displayServiceManagerItems(item, slideno)
def displayServiceManagerItems(self, serviceItem, slideno):
@ -428,9 +451,11 @@ class SlideController(QtGui.QWidget):
tag = None
#If verse handle verse number else tag only
if bits[0] == self.trUtf8('Verse'):
tag = u'%s%s' % (bits[0][0], bits[1][0] )
tag = u'%s%s' % (bits[0][0], bits[1][0:] )
row = bits[1][0:]
else:
tag = bits[0]
row = bits[0][0:1]
try:
test = self.slideList[tag]
except:
@ -441,7 +466,9 @@ class SlideController(QtGui.QWidget):
else:
label = QtGui.QLabel()
label.setMargin(4)
pixmap = self.parent.RenderManager.resize_image(frame[u'image'])
pixmap = resize_image(frame[u'image'],
self.parent.RenderManager.width,
self.parent.RenderManager.height)
label.setScaledContents(True)
label.setPixmap(QtGui.QPixmap.fromImage(pixmap))
self.PreviewListWidget.setCellWidget(framenumber, 0, label)
@ -480,18 +507,26 @@ class SlideController(QtGui.QWidget):
self.PreviewListWidget.selectRow(0)
self.onSlideSelected()
def onBlankDisplay(self):
self.blankButton.setChecked(self.parent.mainDisplay.displayBlank)
def onBlankDisplay(self, force=False):
"""
Handle the blank screen button
"""
if force:
self.blankButton.setChecked(True)
self.blankScreen(self.blankButton.isChecked())
self.parent.generalConfig.set_config(u'screen blank',
self.blankButton.isChecked())
def onBlankScreen(self, blanked):
def blankScreen(self, blanked=False):
"""
Blank the screen.
Blank the display screen.
"""
if not self.serviceItem and self.serviceItem.is_command():
if blanked:
Receiver.send_message(u'%s_blank'% self.serviceItem.name.lower())
else:
Receiver.send_message(u'%s_unblank'% self.serviceItem.name.lower())
if self.serviceItem is not None:
if self.serviceItem.is_command():
if blanked:
Receiver.send_message(u'%s_blank'% self.serviceItem.name.lower())
else:
Receiver.send_message(u'%s_unblank'% self.serviceItem.name.lower())
else:
self.parent.mainDisplay.blankDisplay(blanked)
@ -531,7 +566,7 @@ class SlideController(QtGui.QWidget):
def updatePreview(self):
rm = self.parent.RenderManager
if not rm.screen_list[rm.current_display][u'primary']:
if not rm.screens.current[u'primary']:
# Grab now, but try again in a couple of seconds if slide change is slow
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
@ -543,12 +578,15 @@ class SlideController(QtGui.QWidget):
def grabMainDisplay(self):
rm = self.parent.RenderManager
winid = QtGui.QApplication.desktop().winId()
rect = rm.screen_list[rm.current_display][u'size']
rect = rm.screens.current[u'size']
winimg = QtGui.QPixmap.grabWindow(winid, rect.x(),
rect.y(), rect.width(), rect.height())
self.SlidePreview.setPixmap(winimg)
def onSlideSelectedNext(self):
def onSlideSelectedNextNoloop(self):
self.onSlideSelectedNext(False)
def onSlideSelectedNext(self, loop=True):
"""
Go to the next slide.
"""
@ -561,11 +599,18 @@ class SlideController(QtGui.QWidget):
else:
row = self.PreviewListWidget.currentRow() + 1
if row == self.PreviewListWidget.rowCount():
row = 0
if loop:
row = 0
else:
Receiver.send_message('servicemanager_next_item')
return
self.PreviewListWidget.selectRow(row)
self.onSlideSelected()
def onSlideSelectedPrevious(self):
def onSlideSelectedPreviousNoloop(self):
self.onSlideSelectedPrevious(False)
def onSlideSelectedPrevious(self, loop=True):
"""
Go to the previous slide.
"""
@ -578,7 +623,10 @@ class SlideController(QtGui.QWidget):
else:
row = self.PreviewListWidget.currentRow() - 1
if row == -1:
row = self.PreviewListWidget.rowCount() - 1
if loop:
row = self.PreviewListWidget.rowCount() - 1
else:
row = 0
self.PreviewListWidget.selectRow(row)
self.onSlideSelected()
@ -635,7 +683,7 @@ class SlideController(QtGui.QWidget):
if self.isLive:
Receiver.send_message(u'%s_start' % item.name.lower(), \
[item.title, item.service_item_path,
item.get_frame_title(), slideno, self.isLive])
item.get_frame_title(), self.isLive, self.blankButton.isChecked()])
else:
self.mediaObject.stop()
self.mediaObject.clearQueue()
@ -659,9 +707,9 @@ class SlideController(QtGui.QWidget):
def onMediaStop(self):
if self.isLive:
Receiver.send_message(u'%s_stop'% self.serviceItem.name.lower())
Receiver.send_message(u'%s_stop'% self.serviceItem.name.lower(), self.isLive)
else:
self.mediaObject.stop()
self.video.hide()
self.SlidePreview.clear()
self.SlidePreview.show()
self.SlidePreview.clear()
self.SlidePreview.show()

View File

@ -34,17 +34,16 @@ from PyQt4 import QtCore, QtGui
from openlp.core.ui import AmendThemeForm
from openlp.core.theme import Theme
from openlp.core.lib import PluginConfig, OpenLPToolbar, contextMenuAction, \
ThemeXML, ThemeLevel, str_to_bool, get_text_file_string, build_icon, \
Receiver, contextMenuSeparator
ThemeXML, str_to_bool, get_text_file_string, build_icon, Receiver, \
contextMenuSeparator
from openlp.core.utils import ConfigHelper
log = logging.getLogger(__name__)
class ThemeManager(QtGui.QWidget):
"""
Manages the orders of Theme.
"""
global log
log = logging.getLogger(u'ThemeManager')
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
self.parent = parent
@ -108,6 +107,8 @@ class ThemeManager(QtGui.QWidget):
self.themelist = []
self.path = os.path.join(ConfigHelper.get_data_path(), u'themes')
self.checkThemesExists(self.path)
self.thumbPath = os.path.join(self.path, u'.thumbnails')
self.checkThemesExists(self.thumbPath)
self.amendThemeForm.path = self.path
# Last little bits of setting up
self.config = PluginConfig(u'themes')
@ -179,12 +180,26 @@ class ThemeManager(QtGui.QWidget):
self.trUtf8('You are unable to delete the default theme!'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
else:
for plugin in self.parent.plugin_manager.plugins:
if not plugin.can_delete_theme(theme):
QtGui.QMessageBox.critical(
self, self.trUtf8('Error'),
self.trUtf8('theme %s is use in %s plugin' % (theme, plugin.name)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
return
if unicode(self.parent.ServiceManagerContents.ThemeComboBox.currentText()) == theme:
QtGui.QMessageBox.critical(
self, self.trUtf8('Error'),
self.trUtf8('theme %s is use Service Manager' % theme),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
return
self.themelist.remove(theme)
th = theme + u'.png'
row = self.ThemeListWidget.row(item)
self.ThemeListWidget.takeItem(row)
try:
os.remove(os.path.join(self.path, th))
os.remove(os.path.join(self.thumbPath, th))
shutil.rmtree(os.path.join(self.path, theme))
except:
#if not present do not worry
@ -231,9 +246,9 @@ class ThemeManager(QtGui.QWidget):
self, self.trUtf8('Select Theme Import File'),
self.config.get_last_dir(), u'Theme (*.*)')
log.info(u'New Themes %s', unicode(files))
if len(files) > 0:
if files:
for file in files:
self.config.set_last_dir(filename)
self.config.set_last_dir(unicode(file))
self.unzipTheme(file, self.path)
self.loadThemes()
@ -246,25 +261,33 @@ class ThemeManager(QtGui.QWidget):
log.debug(u'Load themes from dir')
self.themelist = []
self.ThemeListWidget.clear()
for root, dirs, files in os.walk(self.path):
for name in files:
if name.endswith(u'.png'):
#check to see file is in theme root directory
theme = os.path.join(self.path, name)
if os.path.exists(theme):
(path, filename) = os.path.split(unicode(file))
textName = os.path.splitext(name)[0]
if textName == self.global_theme:
name = u'%s (%s)' % (textName,
self.trUtf8('default'))
else:
name = textName
item_name = QtGui.QListWidgetItem(name)
item_name.setIcon(build_icon(theme))
item_name.setData(QtCore.Qt.UserRole,
QtCore.QVariant(textName))
self.ThemeListWidget.addItem(item_name)
self.themelist.append(textName)
#root, dirs, files = os.walk(self.path)
dirList = os.listdir(self.path)
for name in dirList:
if name.endswith(u'.png'):
#check to see file is in theme root directory
theme = os.path.join(self.path, name)
if os.path.exists(theme):
(path, filename) = os.path.split(unicode(file))
textName = os.path.splitext(name)[0]
if textName == self.global_theme:
name = u'%s (%s)' % (textName,
self.trUtf8('default'))
else:
name = textName
thumb = os.path.join(self.thumbPath, u'%s.png' % textName)
item_name = QtGui.QListWidgetItem(name)
if os.path.exists(thumb):
icon = build_icon(thumb)
else:
icon = build_icon(theme)
pixmap = icon.pixmap(QtCore.QSize(88,50))
pixmap.save(thumb, u'png')
item_name.setIcon(icon)
item_name.setData(QtCore.Qt.UserRole,
QtCore.QVariant(textName))
self.ThemeListWidget.addItem(item_name)
self.themelist.append(textName)
self.pushThemes()
def pushThemes(self):
@ -302,17 +325,23 @@ class ThemeManager(QtGui.QWidget):
filexml = None
themename = None
for file in zip.namelist():
if file.endswith(os.path.sep):
theme_dir = os.path.join(dir, file)
osfile = unicode(QtCore.QDir.toNativeSeparators(file))
theme_dir = None
if osfile.endswith(os.path.sep):
theme_dir = os.path.join(dir, osfile)
if not os.path.exists(theme_dir):
os.mkdir(os.path.join(dir, file))
os.mkdir(os.path.join(dir, osfile))
else:
fullpath = os.path.join(dir, file)
names = file.split(os.path.sep)
fullpath = os.path.join(dir, osfile)
names = osfile.split(os.path.sep)
if len(names) > 1:
# not preview file
if themename is None:
themename = names[0]
if theme_dir is None:
theme_dir = os.path.join(dir, names[0])
if not os.path.exists(theme_dir):
os.mkdir(os.path.join(dir, names[0]))
xml_data = zip.read(file)
if os.path.splitext(file)[1].lower() in [u'.xml']:
if self.checkVersion1(xml_data):
@ -324,7 +353,7 @@ class ThemeManager(QtGui.QWidget):
outfile = open(fullpath, u'w')
outfile.write(filexml)
else:
outfile = open(fullpath, u'w')
outfile = open(fullpath, u'wb')
outfile.write(zip.read(file))
self.generateAndSaveImage(dir, themename, filexml)
except:
@ -332,7 +361,7 @@ class ThemeManager(QtGui.QWidget):
self, self.trUtf8('Error'),
self.trUtf8('File is not a valid theme!'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
log.exception(u'Importing theme from zip file failed')
log.exception(u'Importing theme from zip file failed %s' % filename)
finally:
if zip:
zip.close()
@ -373,7 +402,6 @@ class ThemeManager(QtGui.QWidget):
unicode(theme.BackgroundParameter2.name()), direction)
else:
newtheme.add_background_image(unicode(theme.BackgroundParameter1))
newtheme.add_font(unicode(theme.FontName),
unicode(theme.FontColor.name()),
unicode(theme.FontProportion * 3), u'False')
@ -386,10 +414,15 @@ class ThemeManager(QtGui.QWidget):
shadow = True
if theme.Outline == 1:
outline = True
vAlignCorrection = 0
if theme.VerticalAlign == 2:
vAlignCorrection = 1
elif theme.VerticalAlign == 1:
vAlignCorrection = 2
newtheme.add_display(unicode(shadow), unicode(theme.ShadowColor.name()),
unicode(outline), unicode(theme.OutlineColor.name()),
unicode(theme.HorizontalAlign), unicode(theme.VerticalAlign),
unicode(theme.WrapStyle), 0)
unicode(theme.HorizontalAlign), unicode(vAlignCorrection),
unicode(theme.WrapStyle), unicode(0))
return newtheme.extract_xml()
def saveTheme(self, name, theme_xml, theme_pretty_xml, image_from,
@ -427,8 +460,6 @@ class ThemeManager(QtGui.QWidget):
if outfile:
outfile.close()
if image_from and image_from != image_to:
print "if", image_from
print "it", image_to
try:
shutil.copyfile(image_from, image_to)
except:
@ -448,6 +479,10 @@ class ThemeManager(QtGui.QWidget):
if os.path.exists(samplepathname):
os.unlink(samplepathname)
frame.save(samplepathname, u'png')
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
icon = build_icon(frame)
pixmap = icon.pixmap(QtCore.QSize(88,50))
pixmap.save(thumb, u'png')
log.debug(u'Theme image written to %s', samplepathname)
def generateImage(self, themedata):
@ -529,4 +564,4 @@ class ThemeManager(QtGui.QWidget):
theme.font_main_y = int(theme.font_main_y.strip())
#theme.theme_mode
theme.theme_name = theme.theme_name.strip()
#theme.theme_version
#theme.theme_version

View File

@ -22,21 +22,65 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import os
import sys
import logging
import urllib2
from datetime import datetime
from registry import Registry
from confighelper import ConfigHelper
log = logging.getLogger(__name__)
__all__ = ['Registry', 'ConfigHelper']
class AppLocation(object):
"""
Retrieve a directory based on the directory type.
"""
AppDir = 1
ConfigDir = 2
DataDir = 3
PluginsDir = 4
@staticmethod
def get_directory(dir_type):
if dir_type == AppLocation.AppDir:
return os.path.abspath(os.path.split(sys.argv[0])[0])
elif dir_type == AppLocation.ConfigDir:
if os.name == u'nt':
path = os.path.join(os.getenv(u'APPDATA'), u'openlp')
elif os.name == u'mac':
path = os.path.join(os.getenv(u'HOME'), u'Library',
u'Application Support', u'openlp')
else:
try:
from xdg import BaseDirectory
path = os.path.join(BaseDirectory.xdg_config_home, u'openlp')
except ImportError:
path = os.path.join(os.getenv(u'HOME'), u'.openlp')
return path
elif dir_type == AppLocation.DataDir:
if os.name == u'nt':
path = os.path.join(os.getenv(u'APPDATA'), u'openlp', u'data')
elif os.name == u'mac':
path = os.path.join(os.getenv(u'HOME'), u'Library',
u'Application Support', u'openlp', u'Data')
else:
try:
from xdg import BaseDirectory
path = os.path.join(BaseDirectory.xdg_data_home, u'openlp')
except ImportError:
path = os.path.join(os.getenv(u'HOME'), u'.openlp', u'data')
return path
elif dir_type == AppLocation.PluginsDir:
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
if hasattr(sys, u'frozen') and sys.frozen == 1:
return os.path.join(app_path, u'plugins')
else:
return os.path.join(app_path, u'openlp', u'plugins')
log = logging.getLogger(__name__)
def check_latest_version(config, current_version):
version_string = current_version
#set to prod in the distribution confif file.
last_test = config.get_config(u'last version test', datetime.now().date())
this_test = unicode(datetime.now().date())
config.set_config(u'last version test', this_test)
@ -52,3 +96,8 @@ def check_latest_version(config, current_version):
if hasattr(e, u'reason'):
log.exception(u'Reason for failure: %s', e.reason)
return version_string
from registry import Registry
from confighelper import ConfigHelper
__all__ = [u'Registry', u'ConfigHelper', u'AppLocations', u'check_latest_version']

View File

@ -24,6 +24,8 @@
###############################################################################
import os
from openlp.core.utils import AppLocation
from openlp.core.utils.registry import Registry
class ConfigHelper(object):
@ -34,20 +36,7 @@ class ConfigHelper(object):
@staticmethod
def get_data_path():
if os.name == u'nt':
# ask OS for path to application data, set on Windows XP and Vista
path = os.path.join(os.getenv(u'APPDATA'), u'openlp', u'data')
elif os.name == u'mac':
path = os.path.join(os.getenv(u'HOME'), u'Library',
u'Application Support', u'openlp', u'Data')
else:
try:
from xdg import BaseDirectory
path = os.path.join(BaseDirectory.xdg_data_home, u'openlp')
except ImportError:
path = os.path.join(os.getenv(u'HOME'), u'.openlp', u'data')
#reg = ConfigHelper.get_registry()
#path = ConfigHelper.get_config(u'main', 'data path', path)
path = AppLocation.get_directory(AppLocation.DataDir)
if not os.path.exists(path):
os.makedirs(path)
return path
@ -81,17 +70,7 @@ class ConfigHelper(object):
current operating system, and returns an instantiation of that class.
"""
if ConfigHelper.__registry__ is None:
config_path = u''
if os.name == u'nt':
config_path = os.path.join(os.getenv(u'APPDATA'), u'openlp')
elif os.name == u'mac':
config_path = os.path.join(os.getenv(u'HOME'), u'Library',
u'Application Support', u'openlp')
else:
try:
from xdg import BaseDirectory
config_path = os.path.join(BaseDirectory.xdg_config_home, u'openlp')
except ImportError:
config_path = os.path.join(os.getenv(u'HOME'), u'.openlp')
config_path = AppLocation.get_directory(AppLocation.ConfigDir)
ConfigHelper.__registry__ = Registry(config_path)
return ConfigHelper.__registry__
return ConfigHelper.__registry__

View File

@ -25,18 +25,18 @@
import logging
log = logging.getLogger(__name__)
class Display():
global log
log = logging.getLogger(u'Display Logger')
log.info(u'Display Class loaded')
@staticmethod
def output(string):
log.debug(string);
print (string)
log.debug(string)
#print (string)
@staticmethod
def sub_output(string):
if not string is None:
log.debug(u' '+string);
print (u' '+string)
log.debug(u' '+string)
#print (u' '+string)

View File

@ -28,5 +28,5 @@ class MigrateBibles():
self.display = display
def process(self):
self.display.output(u'Bible process started');
self.display.output(u'Bible process finished');
self.display.output(u'Bible process started')
self.display.output(u'Bible process finished')

View File

@ -30,20 +30,20 @@ class MigrateFiles():
self.display = display
def process(self):
self.display.output(u'Files process started');
self.display.output(u'Files process started')
self._initial_setup()
self.display.output(u'Files process finished');
self.display.output(u'Files process finished')
def _initial_setup(self):
self.display.output(u'Initial Setup started');
self.display.output(u'Initial Setup started')
ConfigHelper.get_data_path()
self.display.sub_output(u'Config created');
self.display.sub_output(u'Config created')
ConfigHelper.get_config(u'bible', u'data path')
self.display.sub_output(u'Config created');
self.display.sub_output(u'Config created')
ConfigHelper.get_config(u'videos', u'data path')
self.display.sub_output(u'videos created');
self.display.sub_output(u'videos created')
ConfigHelper.get_config(u'images', u'data path')
self.display.sub_output(u'images created');
self.display.sub_output(u'images created')
ConfigHelper.get_config(u'presentations', u'data path')
self.display.sub_output(u'presentations created');
self.display.output(u'Initial Setup finished');
self.display.sub_output(u'presentations created')
self.display.output(u'Initial Setup finished')

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################

View File

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, build_icon, PluginStatus
from openlp.plugins.alerts.lib import AlertsManager, DBManager
from openlp.plugins.alerts.forms import AlertsTab, AlertForm, AlertEditForm
log = logging.getLogger(__name__)
class alertsPlugin(Plugin):
log.info(u'Alerts Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Alerts', u'1.9.1', plugin_helpers)
self.weight = -3
self.icon = build_icon(u':/media/media_image.png')
self.alertsmanager = AlertsManager(self)
self.manager = DBManager(self.config)
self.alertForm = AlertForm(self.manager, self)
self.alertEditForm = AlertEditForm(self.manager, self)
self.status = PluginStatus.Active
def get_settings_tab(self):
self.alertsTab = AlertsTab(self)
return self.alertsTab
def add_tools_menu_item(self, 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.
"""
log.info(u'add tools menu')
self.toolsAlertItem = QtGui.QAction(tools_menu)
AlertIcon = build_icon(u':/tools/tools_alert.png')
self.toolsAlertItem.setIcon(AlertIcon)
self.toolsAlertItem.setObjectName(u'toolsAlertItem')
self.toolsAlertItem.setText(self.trUtf8('&Alert'))
self.toolsAlertItem.setStatusTip(self.trUtf8('Show an alert message'))
self.toolsAlertItem.setShortcut(u'F7')
self.service_manager.parent.ToolsMenu.addAction(self.toolsAlertItem)
QtCore.QObject.connect(self.toolsAlertItem,
QtCore.SIGNAL(u'triggered()'), self.onAlertsTrigger)
self.toolsAlertItem.setVisible(False)
def initialise(self):
log.info(u'Alerts Initialising')
Plugin.initialise(self)
self.toolsAlertItem.setVisible(True)
def finalise(self):
log.info(u'Plugin Finalise')
self.toolsAlertItem.setVisible(False)
#stop any events being processed
def togglealertsState(self):
self.alertsActive = not self.alertsActive
self.config.set_config(u'active', self.alertsActive)
def onAlertsTrigger(self):
self.alertForm.loadList()
self.alertForm.exec_()
def onAlertsEdit(self):
self.alertEditForm.loadList()
self.alertEditForm.exec_()
def about(self):
about_text = self.trUtf8('<b>Alerts Plugin</b><br>This plugin '
'controls the displaying of alerts on the presentations screen')
return about_text

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from alertstab import AlertsTab
from alertform import AlertForm
from alerteditform import AlertEditForm

View File

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'alertform.ui'
#
# Created: Sat Feb 13 08:19:51 2010
# by: PyQt4 UI code generator 4.6.2
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_AlertDialog(object):
def setupUi(self, AlertForm):
AlertForm.setObjectName(u'AlertDialog')
AlertForm.resize(430, 320)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(u':/icon/openlp.org-icon-32.bmp'), QtGui.QIcon.Normal, QtGui.QIcon.Off)
AlertForm.setWindowIcon(icon)
self.AlertFormLayout = QtGui.QVBoxLayout(AlertForm)
self.AlertFormLayout.setSpacing(8)
self.AlertFormLayout.setMargin(8)
self.AlertFormLayout.setObjectName(u'AlertFormLayout')
self.AlertEntryWidget = QtGui.QWidget(AlertForm)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.AlertEntryWidget.sizePolicy().hasHeightForWidth())
self.AlertEntryWidget.setSizePolicy(sizePolicy)
self.AlertEntryWidget.setObjectName(u'AlertEntryWidget')
self.verticalLayout_2 = QtGui.QVBoxLayout(self.AlertEntryWidget)
self.verticalLayout_2.setObjectName(u'verticalLayout_2')
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(u'verticalLayout')
self.AlertEntryLabel = QtGui.QLabel(self.AlertEntryWidget)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.AlertEntryLabel.sizePolicy().hasHeightForWidth())
self.AlertEntryLabel.setSizePolicy(sizePolicy)
self.AlertEntryLabel.setObjectName(u'AlertEntryLabel')
self.verticalLayout.addWidget(self.AlertEntryLabel)
self.AlertEntryEditItem = QtGui.QLineEdit(self.AlertEntryWidget)
self.AlertEntryEditItem.setObjectName(u'AlertEntryEditItem')
self.verticalLayout.addWidget(self.AlertEntryEditItem)
self.AlertListWidget = QtGui.QListWidget(self.AlertEntryWidget)
self.AlertListWidget.setAlternatingRowColors(True)
self.AlertListWidget.setObjectName(u'AlertListWidget')
self.verticalLayout.addWidget(self.AlertListWidget)
self.verticalLayout_2.addLayout(self.verticalLayout)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(u'horizontalLayout')
spacerItem = QtGui.QSpacerItem(181, 38, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.DisplayButton = QtGui.QPushButton(self.AlertEntryWidget)
self.DisplayButton.setObjectName(u'DisplayButton')
self.horizontalLayout.addWidget(self.DisplayButton)
self.CancelButton = QtGui.QPushButton(self.AlertEntryWidget)
self.CancelButton.setObjectName(u'CancelButton')
self.horizontalLayout.addWidget(self.CancelButton)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.AlertFormLayout.addWidget(self.AlertEntryWidget)
self.retranslateUi(AlertForm)
QtCore.QObject.connect(self.CancelButton, QtCore.SIGNAL(u'clicked()'), self.close)
QtCore.QMetaObject.connectSlotsByName(AlertForm)
def retranslateUi(self, AlertForm):
AlertForm.setWindowTitle(self.trUtf8('Alert Message'))
self.AlertEntryLabel.setText(self.trUtf8('Alert Text:'))
self.DisplayButton.setText(self.trUtf8('Display'))
self.CancelButton.setText(self.trUtf8('Cancel'))

View File

@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'alerteditdialog.ui'
#
# Created: Sun Feb 14 16:45:10 2010
# by: PyQt4 UI code generator 4.6.2
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_AlertEditDialog(object):
def setupUi(self, AlertEditDialog):
AlertEditDialog.setObjectName(u'AlertEditDialog')
AlertEditDialog.resize(400, 300)
self.buttonBox = QtGui.QDialogButtonBox(AlertEditDialog)
self.buttonBox.setGeometry(QtCore.QRect(220, 270, 173, 27))
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel)
self.buttonBox.setObjectName(u'buttonBox')
self.layoutWidget = QtGui.QWidget(AlertEditDialog)
self.layoutWidget.setGeometry(QtCore.QRect(20, 10, 361, 251))
self.layoutWidget.setObjectName(u'layoutWidget')
self.verticalLayout_2 = QtGui.QVBoxLayout(self.layoutWidget)
self.verticalLayout_2.setObjectName(u'verticalLayout_2')
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setObjectName(u'horizontalLayout_2')
self.AlertLineEdit = QtGui.QLineEdit(self.layoutWidget)
self.AlertLineEdit.setObjectName(u'AlertLineEdit')
self.horizontalLayout_2.addWidget(self.AlertLineEdit)
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(u'horizontalLayout')
self.AlertListWidget = QtGui.QListWidget(self.layoutWidget)
self.AlertListWidget.setAlternatingRowColors(True)
self.AlertListWidget.setObjectName(u'AlertListWidget')
self.horizontalLayout.addWidget(self.AlertListWidget)
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(u'verticalLayout')
self.SaveButton = QtGui.QPushButton(self.layoutWidget)
self.SaveButton.setObjectName(u'SaveButton')
self.verticalLayout.addWidget(self.SaveButton)
self.ClearButton = QtGui.QPushButton(self.layoutWidget)
self.ClearButton.setObjectName(u'ClearButton')
self.verticalLayout.addWidget(self.ClearButton)
self.AddButton = QtGui.QPushButton(self.layoutWidget)
self.AddButton.setObjectName(u'AddButton')
self.verticalLayout.addWidget(self.AddButton)
self.EditButton = QtGui.QPushButton(self.layoutWidget)
self.EditButton.setObjectName(u'EditButton')
self.verticalLayout.addWidget(self.EditButton)
self.DeleteButton = QtGui.QPushButton(self.layoutWidget)
self.DeleteButton.setObjectName(u'DeleteButton')
self.verticalLayout.addWidget(self.DeleteButton)
self.horizontalLayout.addLayout(self.verticalLayout)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.retranslateUi(AlertEditDialog)
QtCore.QMetaObject.connectSlotsByName(AlertEditDialog)
def retranslateUi(self, AlertEditDialog):
AlertEditDialog.setWindowTitle(self.trUtf8('Maintain Alerts'))
self.SaveButton.setText(self.trUtf8('Save'))
self.ClearButton.setText(self.trUtf8('Clear'))
self.AddButton.setText(self.trUtf8('Add'))
self.EditButton.setText(self.trUtf8('Edit'))
self.DeleteButton.setText(self.trUtf8('Delete'))

View File

@ -0,0 +1,166 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from PyQt4 import QtGui, QtCore
from openlp.plugins.alerts.lib.models import AlertItem
from alerteditdialog import Ui_AlertEditDialog
class AlertEditForm(QtGui.QDialog, Ui_AlertEditDialog):
"""
Class documentation goes here.
"""
def __init__(self, manager, parent):
"""
Constructor
"""
self.manager = manager
self.parent = parent
QtGui.QDialog.__init__(self, None)
self.setupUi(self)
QtCore.QObject.connect(self.DeleteButton,
QtCore.SIGNAL(u'clicked()'),
self.onDeleteClick)
QtCore.QObject.connect(self.ClearButton,
QtCore.SIGNAL(u'clicked()'),
self.onClearClick)
QtCore.QObject.connect(self.EditButton,
QtCore.SIGNAL(u'clicked()'),
self.onEditClick)
QtCore.QObject.connect(self.AddButton,
QtCore.SIGNAL(u'clicked()'),
self.onAddClick)
QtCore.QObject.connect(self.SaveButton,
QtCore.SIGNAL(u'clicked()'),
self.onSaveClick)
QtCore.QObject.connect(self.buttonBox,
QtCore.SIGNAL(u'rejected()'), self.close)
QtCore.QObject.connect(self.AlertLineEdit,
QtCore.SIGNAL(u'textChanged(const QString&)'),
self.onTextChanged)
QtCore.QObject.connect(self.AlertListWidget,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
self.onItemSelected)
QtCore.QObject.connect(self.AlertListWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'),
self.onItemSelected)
def loadList(self):
self.AlertListWidget.clear()
alerts = self.manager.get_all_alerts()
for alert in alerts:
item_name = QtGui.QListWidgetItem(alert.text)
item_name.setData(
QtCore.Qt.UserRole, QtCore.QVariant(alert.id))
self.AlertListWidget.addItem(item_name)
self.AddButton.setEnabled(True)
self.ClearButton.setEnabled(False)
self.SaveButton.setEnabled(False)
self.EditButton.setEnabled(False)
self.DeleteButton.setEnabled(False)
def onItemSelected(self):
if self.AlertLineEdit.text():
QtGui.QMessageBox.information(self,
self.trUtf8('Item selected to Edit'),
self.trUtf8('Please Save or Clear seletced item'))
else:
self.EditButton.setEnabled(True)
self.DeleteButton.setEnabled(True)
def onDeleteClick(self):
item = self.AlertListWidget.currentItem()
if item:
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.parent.manager.delete_alert(item_id)
row = self.AlertListWidget.row(item)
self.AlertListWidget.takeItem(row)
self.AddButton.setEnabled(True)
self.SaveButton.setEnabled(False)
self.DeleteButton.setEnabled(False)
self.EditButton.setEnabled(False)
def onEditClick(self):
item = self.AlertListWidget.currentItem()
if item:
self.item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.AlertLineEdit.setText(unicode(item.text()))
self.AddButton.setEnabled(True)
self.ClearButton.setEnabled(True)
self.SaveButton.setEnabled(True)
self.DeleteButton.setEnabled(True)
self.EditButton.setEnabled(False)
def onClearClick(self):
self.AlertLineEdit.setText(u'')
self.AddButton.setEnabled(False)
self.ClearButton.setEnabled(True)
self.SaveButton.setEnabled(False)
self.DeleteButton.setEnabled(False)
self.EditButton.setEnabled(False)
def onAddClick(self):
if len(self.AlertLineEdit.text()) == 0:
QtGui.QMessageBox.information(self,
self.trUtf8('Item selected to Add'),
self.trUtf8('Missing data'))
else:
alert = AlertItem()
alert.text = unicode(self.AlertLineEdit.text())
self.manager.save_alert(alert)
self.onClearClick()
self.loadList()
def onSaveClick(self):
alert = self.manager.get_alert(self.item_id)
alert.text = unicode(self.AlertLineEdit.text())
self.manager.save_alert(alert)
self.onClearClick()
self.loadList()
def onTextChanged(self):
self.AddButton.setEnabled(True)
def onDoubleClick(self):
"""
List item has been double clicked to display it
"""
items = self.AlertListWidget.selectedIndexes()
for item in items:
bitem = self.AlertListWidget.item(item.row())
self.triggerAlert(bitem.text())
def onSingleClick(self):
"""
List item has been single clicked to add it to
the edit field so it can be changed.
"""
items = self.AlertListWidget.selectedIndexes()
for item in items:
bitem = self.AlertListWidget.item(item.row())
self.AlertEntryEditItem.setText(bitem.text())
def triggerAlert(self, text):
self.parent.alertsmanager.displayAlert(text)

View File

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from PyQt4 import QtGui, QtCore
from openlp.plugins.alerts.lib.models import AlertItem
from alertdialog import Ui_AlertDialog
class AlertForm(QtGui.QDialog, Ui_AlertDialog):
"""
Class documentation goes here.
"""
def __init__(self, manager, parent):
"""
Constructor
"""
self.manager = manager
self.parent = parent
self.history_required = True
QtGui.QDialog.__init__(self, None)
self.setupUi(self)
QtCore.QObject.connect(self.DisplayButton,
QtCore.SIGNAL(u'clicked()'),
self.onDisplayClicked)
QtCore.QObject.connect(self.AlertEntryEditItem,
QtCore.SIGNAL(u'textChanged(const QString&)'),
self.onTextChanged)
QtCore.QObject.connect(self.AlertListWidget,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
self.onDoubleClick)
QtCore.QObject.connect(self.AlertListWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'),
self.onSingleClick)
def loadList(self):
self.AlertListWidget.clear()
alerts = self.manager.get_all_alerts()
for alert in alerts:
item_name = QtGui.QListWidgetItem(alert.text)
self.AlertListWidget.addItem(item_name)
def onDisplayClicked(self):
self.triggerAlert(unicode(self.AlertEntryEditItem.text()))
if self.parent.alertsTab.save_history and self.history_required:
alert = AlertItem()
alert.text = unicode(self.AlertEntryEditItem.text())
self.manager.save_alert(alert)
self.history_required = False
self.loadList()
def onTextChanged(self):
#Data has changed by editing it so potential storage
self.history_required = True
def onDoubleClick(self):
"""
List item has been double clicked to display it
"""
items = self.AlertListWidget.selectedIndexes()
for item in items:
bitem = self.AlertListWidget.item(item.row())
self.triggerAlert(bitem.text())
self.history_required = False
def onSingleClick(self):
"""
List item has been single clicked to add it to
the edit field so it can be changed.
"""
items = self.AlertListWidget.selectedIndexes()
for item in items:
bitem = self.AlertListWidget.item(item.row())
self.AlertEntryEditItem.setText(bitem.text())
self.history_required = False
def triggerAlert(self, text):
self.parent.alertsmanager.displayAlert(text)

View File

@ -25,16 +25,15 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab
from openlp.core.lib import SettingsTab, str_to_bool
class AlertsTab(SettingsTab):
"""
AlertsTab is the alerts settings tab in the settings dialog.
"""
def __init__(self):
SettingsTab.__init__(self, u'Alerts')
self.font_color = '#ffffff'
self.bg_color = '#660000'
def __init__(self, parent, section=None):
self.parent = parent
SettingsTab.__init__(self, parent.name, section)
def setupUi(self):
self.setObjectName(u'AlertsTab')
@ -83,6 +82,22 @@ class AlertsTab(SettingsTab):
self.BackgroundColorButton.setObjectName(u'BackgroundColorButton')
self.ColorLayout.addWidget(self.BackgroundColorButton)
self.FontLayout.addWidget(self.ColorWidget)
self.FontSizeWidget = QtGui.QWidget(self.FontGroupBox)
self.FontSizeWidget.setObjectName(u'FontSizeWidget')
self.FontSizeLayout = QtGui.QHBoxLayout(self.FontSizeWidget)
self.FontSizeLayout.setSpacing(8)
self.FontSizeLayout.setMargin(0)
self.FontSizeLayout.setObjectName(u'FontSizeLayout')
self.FontSizeLabel = QtGui.QLabel(self.FontSizeWidget)
self.FontSizeLabel.setObjectName(u'FontSizeLabel')
self.FontSizeLayout.addWidget(self.FontSizeLabel)
self.FontSizeSpinBox = QtGui.QSpinBox(self.FontSizeWidget)
self.FontSizeSpinBox.setObjectName(u'FontSizeSpinBox')
self.FontSizeLayout.addWidget(self.FontSizeSpinBox)
self.FontSizeSpacer = QtGui.QSpacerItem(147, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.FontSizeLayout.addItem(self.FontSizeSpacer)
self.FontLayout.addWidget(self.FontSizeWidget)
self.TimeoutWidget = QtGui.QWidget(self.FontGroupBox)
self.TimeoutWidget.setObjectName(u'TimeoutWidget')
self.TimeoutLayout = QtGui.QHBoxLayout(self.TimeoutWidget)
@ -100,6 +115,56 @@ class AlertsTab(SettingsTab):
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.TimeoutLayout.addItem(self.TimeoutSpacer)
self.FontLayout.addWidget(self.TimeoutWidget)
self.LocationWidget = QtGui.QWidget(self.FontGroupBox)
self.LocationWidget.setObjectName(u'LocationWidget')
self.LocationLayout = QtGui.QHBoxLayout(self.LocationWidget)
self.LocationLayout.setSpacing(8)
self.LocationLayout.setMargin(0)
self.LocationLayout.setObjectName(u'LocationLayout')
self.LocationLabel = QtGui.QLabel(self.LocationWidget)
self.LocationLabel.setObjectName(u'LocationLabel')
self.LocationLayout.addWidget(self.LocationLabel)
self.LocationComboBox = QtGui.QComboBox(self.LocationWidget)
self.LocationComboBox.addItem(QtCore.QString())
self.LocationComboBox.addItem(QtCore.QString())
self.LocationComboBox.setObjectName(u'LocationComboBox')
self.LocationLayout.addWidget(self.LocationComboBox)
self.LocationSpacer = QtGui.QSpacerItem(147, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.LocationLayout.addItem(self.LocationSpacer)
self.FontLayout.addWidget(self.LocationWidget)
self.HistoryWidget = QtGui.QWidget(self.FontGroupBox)
self.HistoryWidget.setObjectName(u'HistoryWidget')
self.HistoryLayout = QtGui.QHBoxLayout(self.HistoryWidget)
self.HistoryLayout.setSpacing(8)
self.HistoryLayout.setMargin(0)
self.HistoryLayout.setObjectName(u'HistoryLayout')
self.HistoryLabel = QtGui.QLabel(self.HistoryWidget)
self.HistoryLabel.setObjectName(u'HistoryLabel')
self.HistoryLayout.addWidget(self.HistoryLabel)
self.HistoryCheckBox = QtGui.QCheckBox(self.HistoryWidget)
self.HistoryCheckBox.setObjectName(u'HistoryCheckBox')
self.HistoryLayout.addWidget(self.HistoryCheckBox)
self.HistorySpacer = QtGui.QSpacerItem(147, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.HistoryLayout.addItem(self.HistorySpacer)
self.FontLayout.addWidget(self.HistoryWidget)
self.HistoryEditWidget = QtGui.QWidget(self.FontGroupBox)
self.HistoryEditWidget.setObjectName(u'HistoryEditWidget')
self.HistoryEditLayout = QtGui.QHBoxLayout(self.HistoryEditWidget)
self.HistoryEditLayout.setSpacing(8)
self.HistoryEditLayout.setMargin(0)
self.HistoryEditLayout.setObjectName(u'HistoryEditLayout')
self.HistoryEditLabel = QtGui.QLabel(self.HistoryEditWidget)
self.HistoryEditLabel.setObjectName(u'HistoryEditLabel')
self.HistoryEditLayout.addWidget(self.HistoryEditLabel)
self.HistoryEditPushButton = QtGui.QPushButton(self.HistoryEditWidget)
self.HistoryEditPushButton.setObjectName(u'HistoryEditPushButton')
self.HistoryEditLayout.addWidget(self.HistoryEditPushButton)
self.HistoryEditSpacer = QtGui.QSpacerItem(147, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.HistoryEditLayout.addItem(self.HistoryEditSpacer)
self.FontLayout.addWidget(self.HistoryEditWidget)
self.SlideLeftLayout.addWidget(self.FontGroupBox)
self.SlideLeftSpacer = QtGui.QSpacerItem(20, 94,
QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
@ -125,7 +190,7 @@ class AlertsTab(SettingsTab):
self.PreviewLayout.setMargin(8)
self.PreviewLayout.setObjectName(u'PreviewLayout')
self.FontPreview = QtGui.QLineEdit(self.PreviewGroupBox)
self.FontPreview.setMinimumSize(QtCore.QSize(280, 100))
self.FontPreview.setFixedSize(QtCore.QSize(350, 100))
self.FontPreview.setReadOnly(True)
self.FontPreview.setFocusPolicy(QtCore.Qt.NoFocus)
self.FontPreview.setAlignment(
@ -138,24 +203,40 @@ class AlertsTab(SettingsTab):
self.SlideRightLayout.addItem(self.SlideRightSpacer)
self.AlertsLayout.addWidget(self.AlertRightColumn)
# Signals and slots
QtCore.QObject.connect(self.HistoryCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onHistoryCheckBoxChanged)
QtCore.QObject.connect(self.BackgroundColorButton,
QtCore.SIGNAL(u'pressed()'), self.onBackgroundColorButtonClicked)
QtCore.QObject.connect(self.FontColorButton,
QtCore.SIGNAL(u'pressed()'), self.onFontColorButtonClicked)
QtCore.QObject.connect(self.HistoryEditPushButton,
QtCore.SIGNAL(u'pressed()'), self.onHistoryEditButtonClicked)
QtCore.QObject.connect(self.FontComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onFontComboBoxClicked)
QtCore.QObject.connect(self.LocationComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onLocationComboBoxClicked)
QtCore.QObject.connect(self.TimeoutSpinBox,
QtCore.SIGNAL(u'valueChanged(int)'), self.onTimeoutSpinBoxChanged)
QtCore.QObject.connect(self.FontSizeSpinBox,
QtCore.SIGNAL(u'valueChanged(int)'), self.onFontSizeSpinBoxChanged)
def retranslateUi(self):
self.FontGroupBox.setTitle(self.trUtf8('Font'))
self.FontLabel.setText(self.trUtf8('Font Name:'))
self.FontColorLabel.setText(self.trUtf8('Font Color:'))
self.BackgroundColorLabel.setText(self.trUtf8('Background Color:'))
self.FontSizeLabel.setText(self.trUtf8('Font Size:'))
self.FontSizeSpinBox.setSuffix(self.trUtf8('pt'))
self.TimeoutLabel.setText(self.trUtf8('Alert timeout:'))
self.TimeoutSpinBox.setSuffix(self.trUtf8('s'))
self.LocationLabel.setText(self.trUtf8('Location:'))
self.HistoryLabel.setText(self.trUtf8('Keep History:'))
self.HistoryEditLabel.setText(self.trUtf8('Edit History:'))
self.PreviewGroupBox.setTitle(self.trUtf8('Preview'))
self.FontPreview.setText(self.trUtf8('openlp.org 2.0 rocks!'))
self.FontPreview.setText(self.trUtf8('openlp.org'))
self.LocationComboBox.setItemText(0, self.trUtf8('Top'))
self.LocationComboBox.setItemText(1, self.trUtf8('Bottom'))
def onBackgroundColorButtonClicked(self):
self.bg_color = QtGui.QColorDialog.getColor(
@ -167,6 +248,15 @@ class AlertsTab(SettingsTab):
def onFontComboBoxClicked(self):
self.updateDisplay()
def onLocationComboBoxClicked(self, location):
self.location = location
def onHistoryCheckBoxChanged(self, check_state):
self.save_history = False
# we have a set value convert to True/False
if check_state == QtCore.Qt.Checked:
self.save_history = True
def onFontColorButtonClicked(self):
self.font_color = QtGui.QColorDialog.getColor(
QtGui.QColor(self.font_color), self).name()
@ -177,19 +267,33 @@ class AlertsTab(SettingsTab):
def onTimeoutSpinBoxChanged(self):
self.timeout = self.TimeoutSpinBox.value()
def onFontSizeSpinBoxChanged(self):
self.font_size = self.FontSizeSpinBox.value()
self.updateDisplay()
def onHistoryEditButtonClicked(self):
self.parent.onAlertsEdit()
def load(self):
self.timeout = int(self.config.get_config(u'timeout', 5))
self.font_color = unicode(
self.config.get_config(u'font color', u'#ffffff'))
self.font_size = int(self.config.get_config(u'font size', 40))
self.bg_color = unicode(
self.config.get_config(u'background color', u'#660000'))
self.font_face = unicode(
self.config.get_config(u'font face', QtGui.QFont().family()))
self.location = int(self.config.get_config(u'location', 0))
self.save_history = str_to_bool(
self.config.get_config(u'save history', u'False'))
self.FontSizeSpinBox.setValue(self.font_size)
self.TimeoutSpinBox.setValue(self.timeout)
self.FontColorButton.setStyleSheet(
u'background-color: %s' % self.font_color)
self.BackgroundColorButton.setStyleSheet(
u'background-color: %s' % self.bg_color)
self.LocationComboBox.setCurrentIndex(self.location)
self.HistoryCheckBox.setChecked(self.save_history)
font = QtGui.QFont()
font.setFamily(self.font_face)
self.FontComboBox.setCurrentFont(font)
@ -199,14 +303,18 @@ class AlertsTab(SettingsTab):
self.font_face = self.FontComboBox.currentFont().family()
self.config.set_config(u'background color', unicode(self.bg_color))
self.config.set_config(u'font color', unicode(self.font_color))
self.config.set_config(u'font size', unicode(self.font_size))
self.config.set_config(u'font face', unicode(self.font_face))
self.config.set_config(u'timeout', unicode(self.timeout))
self.config.set_config(u'location',
unicode(self.LocationComboBox.currentIndex()))
self.config.set_config(u'save history', unicode(self.save_history))
def updateDisplay(self):
font = QtGui.QFont()
font.setFamily(self.FontComboBox.currentFont().family())
font.setBold(True)
font.setPointSize(16)
font.setPointSize(self.font_size)
self.FontPreview.setFont(font)
self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' % \
(self.bg_color, self.font_color))
(self.bg_color, self.font_color))

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from alertsmanager import AlertsManager
from manager import DBManager

View File

@ -0,0 +1,120 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver
log = logging.getLogger(__name__)
class AlertsManager(QtCore.QObject):
"""
AlertsTab is the Alerts settings tab in the settings dialog.
"""
log.info(u'Alert Manager loaded')
def __init__(self, parent):
QtCore.QObject.__init__(self)
self.parent = parent
self.timer_id = 0
self.alertList = []
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'flush_alert'), self.generateAlert)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'alert_text'), self.displayAlert)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'screen_changed'), self.screenChanged)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.screenChanged)
def screenChanged(self):
log.debug(u'screen changed')
self.screen = self.parent.maindisplay.screen
self.alertTab = self.parent.alertsTab
self.font = QtGui.QFont()
self.font.setFamily(self.alertTab.font_face)
self.font.setBold(True)
self.font.setPointSize(self.alertTab.font_size)
self.metrics = QtGui.QFontMetrics(self.font)
self.alertHeight = self.metrics.height() + 4
if self.alertTab.location == 0:
self.alertScreenPosition = 0
else:
self.alertScreenPosition = self.screen[u'size'].height() - self.alertHeight
self.alertHeight = self.screen[u'size'].height() - self.alertScreenPosition
self.parent.maindisplay.setAlertSize(self.alertScreenPosition, self.alertHeight)
def displayAlert(self, text=u''):
"""
Called from the Alert Tab to display an alert
``text``
display text
"""
log.debug(u'display alert called %s' % text)
self.parent.maindisplay.parent.StatusBar.showMessage(u'')
self.alertList.append(text)
if self.timer_id != 0 or self.parent.maindisplay.mediaLoaded:
self.parent.maindisplay.parent.StatusBar.showMessage(\
self.trUtf8(u'Alert message created and delayed'))
return
self.generateAlert()
def generateAlert(self):
log.debug(u'Generate Alert called')
if len(self.alertList) == 0:
return
text = self.alertList.pop(0)
alertTab = self.parent.alertsTab
alertframe = \
QtGui.QPixmap(self.screen[u'size'].width(), self.alertHeight)
alertframe.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(alertframe)
painter.fillRect(alertframe.rect(), QtCore.Qt.transparent)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.fillRect(
QtCore.QRect(
0, 0, alertframe.rect().width(),
alertframe.rect().height()),
QtGui.QColor(self.alertTab.bg_color))
painter.setFont(self.font)
painter.setPen(QtGui.QColor(self.alertTab.font_color))
x, y = (0, 2)
painter.drawText(
x, y + self.metrics.height() - self.metrics.descent() - 1, text)
painter.end()
self.parent.maindisplay.addAlertImage(alertframe)
# check to see if we have a timer running
if self.timer_id == 0:
self.timer_id = self.startTimer(int(alertTab.timeout) * 1000)
def timerEvent(self, event):
if event.timerId() == self.timer_id:
self.parent.maindisplay.addAlertImage(None, True)
self.killTimer(self.timer_id)
self.timer_id = 0
self.generateAlert()

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
class BaseModel(object):
"""
BaseModel provides a base object with a set of generic functions
"""
@classmethod
def populate(cls, **kwargs):
"""
Creates an instance of a class and populates it, returning the instance
"""
me = cls()
keys = kwargs.keys()
for key in keys:
me.__setattr__(key, kwargs[key])
return me
class AlertItem(BaseModel):
"""
Custom Slide model
"""
pass

View File

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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
from openlp.plugins.alerts.lib.models import init_models, metadata, AlertItem
log = logging.getLogger(__name__)
class DBManager():
"""
The Song Manager provides a central location for all database code. This
class takes care of connecting to the database and running all the queries.
"""
log.info(u'Alerts DB loaded')
def __init__(self, config):
"""
Creates the connection to the database, and creates the tables if they
don't exist.
"""
self.config = config
log.debug(u'Alerts Initialising')
self.db_url = u''
db_type = self.config.get_config(u'db type', u'sqlite')
if db_type == u'sqlite':
self.db_url = u'sqlite:///%s/alerts.sqlite' % \
self.config.get_data_path()
else:
self.db_url = u'%s://%s:%s@%s/%s' % \
(db_type, self.config.get_config(u'db username'),
self.config.get_config(u'db password'),
self.config.get_config(u'db hostname'),
self.config.get_config(u'db database'))
self.session = init_models(self.db_url)
metadata.create_all(checkfirst=True)
log.debug(u'Alerts Initialised')
def get_all_alerts(self):
"""
Returns the details of a Alert Show
"""
return self.session.query(AlertItem).order_by(AlertItem.text).all()
def save_alert(self, AlertItem):
"""
Saves a Alert show to the database
"""
log.debug(u'Alert added')
try:
self.session.add(AlertItem)
self.session.commit()
log.debug(u'Alert saved')
return True
except:
self.session.rollback()
log.exception(u'Alert save failed')
return False
def get_alert(self, id=None):
"""
Returns the details of a Alert
"""
if id is None:
return AlertItem()
else:
return self.session.query(AlertItem).get(id)
def delete_alert(self, id):
"""
Delete a Alert show
"""
if id != 0:
AlertItem = self.get_alert(id)
try:
self.session.delete(AlertItem)
self.session.commit()
return True
except:
self.session.rollback()
log.exception(u'Alert deleton failed')
return False
else:
return True

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from sqlalchemy import MetaData
__all__ = ['session', 'metadata', 'engine']
# SQLAlchemy database engine. Updated by model.init_model()
engine = None
# SQLAlchemy session manager. Updated by model.init_model()
session = None
# Global metadata. If you have multiple databases with overlapping table
# names, you'll need a metadata for each database
metadata = MetaData()

View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
from openlp.plugins.alerts.lib.meta import metadata
from openlp.plugins.alerts.lib.tables import *
from openlp.plugins.alerts.lib.classes import *
def init_models(url):
engine = create_engine(url)
metadata.bind = engine
session = scoped_session(sessionmaker(autoflush=True, autocommit=False,
bind=engine))
mapper(AlertItem, alerts_table)
return session

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from sqlalchemy import Column, Table, types
from openlp.plugins.alerts.lib.meta import metadata
# Definition of the "alerts" table
alerts_table = Table(u'alerts', metadata,
Column(u'id', types.Integer(), primary_key=True),
Column(u'text', types.UnicodeText, nullable=False))

View File

@ -27,25 +27,26 @@ import logging
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, build_icon
from openlp.core.lib import Plugin, build_icon, PluginStatus
from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem
log = logging.getLogger(__name__)
class BiblePlugin(Plugin):
global log
log = logging.getLogger(u'BiblePlugin')
log.info(u'Bible Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Bibles', u'1.9.0', plugin_helpers)
Plugin.__init__(self, u'Bibles', u'1.9.1', plugin_helpers)
self.weight = -9
self.icon = build_icon(u':/media/media_bible.png')
#Register the bible Manager
self.biblemanager = None
self.status = PluginStatus.Active
self.manager = None
def initialise(self):
log.info(u'bibles Initialising')
if self.biblemanager is None:
self.biblemanager = BibleManager(self.config)
if self.manager is None:
self.manager = BibleManager(self, self.config)
Plugin.initialise(self)
self.insert_toolbox_item()
self.ImportBibleItem.setVisible(True)
@ -90,4 +91,10 @@ class BiblePlugin(Plugin):
about_text = self.trUtf8('<strong>Bible Plugin</strong><br />This '
'plugin allows bible verses from different sources to be '
'displayed on the screen during the service.')
return about_text
return about_text
def can_delete_theme(self, theme):
if self.settings_tab.bible_theme == theme:
return False
return True

View File

@ -91,15 +91,6 @@ class Ui_BibleImportWizard(object):
self.OsisLayout.setMargin(0)
self.OsisLayout.setSpacing(8)
self.OsisLayout.setObjectName(u'OsisLayout')
self.OsisBibleNameLabel = QtGui.QLabel(self.OsisPage)
self.OsisBibleNameLabel.setIndent(0)
self.OsisBibleNameLabel.setObjectName(u'OsisBibleNameLabel')
self.OsisLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
self.OsisBibleNameLabel)
self.OsisBibleNameEdit = QtGui.QLineEdit(self.OsisPage)
self.OsisBibleNameEdit.setObjectName(u'OsisBibleNameEdit')
self.OsisLayout.setWidget(0, QtGui.QFormLayout.FieldRole,
self.OsisBibleNameEdit)
self.OsisLocationLabel = QtGui.QLabel(self.OsisPage)
self.OsisLocationLabel.setObjectName(u'OsisLocationLabel')
self.OsisLayout.setWidget(1, QtGui.QFormLayout.LabelRole,
@ -302,13 +293,11 @@ class Ui_BibleImportWizard(object):
self.ImportProgressLabel.setObjectName(u'ImportProgressLabel')
self.ImportLayout.addWidget(self.ImportProgressLabel)
self.ImportProgressBar = QtGui.QProgressBar(self.ImportPage)
self.ImportProgressBar.setProperty(u'value', 0)
self.ImportProgressBar.setInvertedAppearance(False)
self.ImportProgressBar.setValue(0)
self.ImportProgressBar.setObjectName(u'ImportProgressBar')
self.ImportLayout.addWidget(self.ImportProgressBar)
BibleImportWizard.addPage(self.ImportPage)
self.retranslateUi(BibleImportWizard)
self.FormatWidget.setCurrentIndex(0)
self.WebDownloadTabWidget.setCurrentIndex(0)
@ -334,7 +323,6 @@ class Ui_BibleImportWizard(object):
self.FormatComboBox.setItemText(1, self.trUtf8('CSV'))
self.FormatComboBox.setItemText(2, self.trUtf8('OpenSong'))
self.FormatComboBox.setItemText(3, self.trUtf8('Web Download'))
self.OsisBibleNameLabel.setText(self.trUtf8('Bible Name:'))
self.OsisLocationLabel.setText(self.trUtf8('File Location:'))
self.BooksLocationLabel.setText(self.trUtf8('Books Location:'))
self.VerseLocationLabel.setText(self.trUtf8('Verse Location:'))
@ -362,4 +350,4 @@ class Ui_BibleImportWizard(object):
self.ImportPage.setSubTitle(
self.trUtf8('Please wait while your Bible is imported.'))
self.ImportProgressLabel.setText(self.trUtf8('Ready.'))
#self.ImportProgressBar.setFormat(u'%p')
self.ImportProgressBar.setFormat(u'%p%')

View File

@ -23,10 +23,10 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import csv
import logging
import os
import os.path
from time import sleep
from PyQt4 import QtCore, QtGui
@ -34,6 +34,8 @@ from bibleimportwizard import Ui_BibleImportWizard
from openlp.core.lib import Receiver
from openlp.plugins.bibles.lib.manager import BibleFormat
log = logging.getLogger(__name__)
class DownloadLocation(object):
Unknown = -1
Crosswalk = 0
@ -45,8 +47,8 @@ class DownloadLocation(object):
}
@classmethod
def get_name(class_, id):
return class_.Names[id]
def get_name(cls, id):
return cls.Names[id]
class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
@ -54,12 +56,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
This is the Bible Import Wizard, which allows easy importing of Bibles
into OpenLP from other formats like OSIS, CSV and OpenSong.
"""
global log
log = logging.getLogger(u'BibleImportForm')
log.info(u'BibleImportForm loaded')
def __init__(self, parent, config, biblemanager, bibleplugin):
def __init__(self, parent, config, manager, bibleplugin):
'''
Constructor
'''
@ -68,10 +67,10 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
self.registerFields()
self.finishButton = self.button(QtGui.QWizard.FinishButton)
self.cancelButton = self.button(QtGui.QWizard.CancelButton)
self.biblemanager = biblemanager
self.manager = manager
self.config = config
self.bibleplugin = bibleplugin
self.biblemanager.set_process_dialog(self)
self.manager.set_process_dialog(self)
self.web_bible_list = {}
self.loadWebBibles()
QtCore.QObject.connect(self.LocationComboBox,
@ -96,9 +95,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
QtCore.SIGNAL(u'currentIdChanged(int)'),
self.onCurrentIdChanged)
def show(self):
def exec_(self):
self.setDefaults()
return QtGui.QWizard.show()
return QtGui.QWizard.exec_(self)
def validateCurrentPage(self):
if self.currentId() == 0:
@ -107,14 +106,6 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
elif self.currentId() == 1:
# Select page
if self.field(u'source_format').toInt()[0] == BibleFormat.OSIS:
if self.field(u'osis_biblename').toString() == u'':
QtGui.QMessageBox.critical(self,
self.trUtf8('Invalid Bible Name'),
self.trUtf8('You need to specify a name for your '
'Bible!'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
self.OsisBibleNameEdit.setFocus()
return False
if self.field(u'osis_location').toString() == u'':
QtGui.QMessageBox.critical(self,
self.trUtf8('Invalid Bible Location'),
@ -169,6 +160,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
self.CopyrightEdit.setFocus()
return False
elif self.manager.exists(
self.field(u'license_version').toString()):
QtGui.QMessageBox.critical(self,
self.trUtf8('Bible Exists'),
self.trUtf8('This Bible already exists! Please import '
'a different Bible or first delete the existing one.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
self.VersionNameEdit.setFocus()
return False
return True
if self.currentId() == 3:
# Progress page
@ -209,8 +209,6 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
def registerFields(self):
self.SelectPage.registerField(
u'source_format', self.FormatComboBox)
self.SelectPage.registerField(
u'osis_biblename', self.OsisBibleNameEdit)
self.SelectPage.registerField(
u'osis_location', self.OSISLocationEdit)
self.SelectPage.registerField(
@ -237,24 +235,23 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
u'license_permission', self.PermissionEdit)
def setDefaults(self):
self.setField(u'source_format', 0)
self.setField(u'osis_biblename', u'')
self.setField(u'osis_location', u'')
self.setField(u'csv_booksfile', u'')
self.setField(u'csv_versefile', u'')
self.setField(u'opensong_file', u'')
self.setField(u'web_location', 0)
self.setField(u'web_biblename', self.BibleComboBox)
self.setField(u'source_format', QtCore.QVariant(0))
self.setField(u'osis_location', QtCore.QVariant(''))
self.setField(u'csv_booksfile', QtCore.QVariant(''))
self.setField(u'csv_versefile', QtCore.QVariant(''))
self.setField(u'opensong_file', QtCore.QVariant(''))
self.setField(u'web_location', QtCore.QVariant(DownloadLocation.Crosswalk))
self.setField(u'web_biblename', QtCore.QVariant(self.BibleComboBox))
self.setField(u'proxy_server',
self.config.get_config(u'proxy address', u''))
QtCore.QVariant(self.config.get_config(u'proxy address', '')))
self.setField(u'proxy_username',
self.config.get_config(u'proxy username',u''))
QtCore.QVariant(self.config.get_config(u'proxy username','')))
self.setField(u'proxy_password',
self.config.get_config(u'proxy password',u''))
self.setField(u'license_version', self.VersionNameEdit)
self.setField(u'license_copyright', self.CopyrightEdit)
self.setField(u'license_permission', self.PermissionEdit)
self.onLocationComboBoxChanged(0)
QtCore.QVariant(self.config.get_config(u'proxy password','')))
self.setField(u'license_version', QtCore.QVariant(self.VersionNameEdit))
self.setField(u'license_copyright', QtCore.QVariant(self.CopyrightEdit))
self.setField(u'license_permission', QtCore.QVariant(self.PermissionEdit))
self.onLocationComboBoxChanged(DownloadLocation.Crosswalk)
def loadWebBibles(self):
"""
@ -267,29 +264,33 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
fbibles = None
try:
self.web_bible_list[DownloadLocation.Crosswalk] = {}
fbibles = open(os.path.join(filepath, u'crosswalkbooks.csv'), 'r')
for line in fbibles:
parts = line.split(u',')
self.web_bible_list[DownloadLocation.Crosswalk][parts[0]] = \
parts[1].rstrip()
books_file = open(os.path.join(filepath, u'crosswalkbooks.csv'), 'r')
dialect = csv.Sniffer().sniff(books_file.read(1024))
books_file.seek(0)
books_reader = csv.reader(books_file, dialect)
for line in books_reader:
self.web_bible_list[DownloadLocation.Crosswalk][line[0]] = \
unicode(line[1], u'utf-8').strip()
except:
log.exception(u'Crosswalk resources missing')
finally:
if fbibles:
fbibles.close()
if books_file:
books_file.close()
#Load and store BibleGateway Bibles
try:
self.web_bible_list[DownloadLocation.BibleGateway] = {}
fbibles = open(os.path.join(filepath, u'biblegateway.csv'), 'r')
for line in fbibles:
parts = line.split(u',')
self.web_bible_list[DownloadLocation.BibleGateway][parts[0]] = \
parts[1].rstrip()
books_file = open(os.path.join(filepath, u'biblegateway.csv'), 'r')
dialect = csv.Sniffer().sniff(books_file.read(1024))
books_file.seek(0)
books_reader = csv.reader(books_file, dialect)
for line in books_reader:
self.web_bible_list[DownloadLocation.BibleGateway][line[0]] = \
unicode(line[1], u'utf-8').strip()
except:
log.exception(u'Biblegateway resources missing')
finally:
if fbibles:
fbibles.close()
if books_file:
books_file.close()
def getFileName(self, title, editbox):
filename = QtGui.QFileDialog.getOpenFileName(self, title,
@ -317,22 +318,22 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
success = False
if bible_type == BibleFormat.OSIS:
# Import an OSIS bible
success = self.biblemanager.register_osis_file_bible(
unicode(self.field(u'license_version').toString()),
unicode(self.field(u'osis_location').toString())
success = self.manager.import_bible(BibleFormat.OSIS,
name=unicode(self.field(u'license_version').toString()),
filename=unicode(self.field(u'osis_location').toString())
)
elif bible_type == BibleFormat.CSV:
# Import a CSV bible
success = self.biblemanager.register_csv_file_bible(
unicode(self.field(u'license_version').toString()),
self.field(u'csv_booksfile').toString(),
self.field(u'csv_versefile').toString()
success = self.manager.import_bible(BibleFormat.CSV,
name=unicode(self.field(u'license_version').toString()),
booksfile=self.field(u'csv_booksfile').toString(),
versefile=self.field(u'csv_versefile').toString()
)
elif bible_type == BibleFormat.OpenSong:
# Import an OpenSong bible
success = self.biblemanager.register_opensong_bible(
unicode(self.field(u'license_version').toString()),
self.field(u'opensong_file').toString()
success = self.manager.import_bible(BibleFormat.OpenSong,
name=unicode(self.field(u'license_version').toString()),
filename=self.field(u'opensong_file').toString()
)
elif bible_type == BibleFormat.WebDownload:
# Import a bible from the web
@ -344,21 +345,22 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
elif download_location == DownloadLocation.BibleGateway:
bible = self.web_bible_list[DownloadLocation.BibleGateway][
unicode(self.BibleComboBox.currentText())]
success = self.biblemanager.register_http_bible(
unicode(self.field(u'license_version').toString()),
unicode(DownloadLocation.get_name(download_location)),
unicode(bible),
unicode(self.field(u'proxy_server').toString()),
unicode(self.field(u'proxy_username').toString()),
unicode(self.field(u'proxy_password').toString())
success = self.manager.import_bible(BibleFormat.WebDownload,
name=unicode(self.field(u'license_version').toString()),
download_source=unicode(DownloadLocation.get_name(download_location)),
download_name=unicode(bible),
proxy_server=unicode(self.field(u'proxy_server').toString()),
proxy_username=unicode(self.field(u'proxy_username').toString()),
proxy_password=unicode(self.field(u'proxy_password').toString())
)
if success:
self.biblemanager.save_meta_data(
self.manager.save_meta_data(
unicode(self.field(u'license_version').toString()),
unicode(self.field(u'license_version').toString()),
unicode(self.field(u'license_copyright').toString()),
unicode(self.field(u'license_permission').toString())
)
self.manager.reload_bibles()
self.ImportProgressLabel.setText(self.trUtf8('Finished import.'))
else:
self.ImportProgressLabel.setText(

View File

@ -1,190 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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 os
import logging
from common import BibleCommon
from openlp.plugins.bibles.lib.models import *
class BibleDBImpl(BibleCommon):
global log
log = logging.getLogger(u'BibleDBImpl')
log.info(u'BibleDBimpl loaded')
def __init__(self, biblepath, biblename, config):
# Connect to database
self.config = config
self.biblefile = os.path.join(biblepath, biblename + u'.sqlite')
log.debug(u'Load bible %s on path %s', biblename, self.biblefile)
db_type = self.config.get_config(u'db type', u'sqlite')
db_url = u''
if db_type == u'sqlite':
db_url = u'sqlite:///' + self.biblefile
else:
db_url = u'%s://%s:%s@%s/%s' % \
(db_type, self.config.get_config(u'db username'),
self.config.get_config(u'db password'),
self.config.get_config(u'db hostname'),
self.config.get_config(u'db database'))
self.metadata, self.session = init_models(db_url)
self.metadata.create_all(checkfirst=True)
def create_tables(self):
log.debug(u'createTables')
self.save_meta(u'dbversion', u'2')
self._load_testament(u'Old Testament')
self._load_testament(u'New Testament')
self._load_testament(u'Apocrypha')
def add_verse(self, bookid, chap, vse, text):
verse = Verse()
verse.book_id = bookid
verse.chapter = chap
verse.verse = vse
verse.text = text
self.session.add(verse)
return verse
def save_verses(self):
log.debug('Saving verses...')
self.session.commit()
def create_chapter(self, bookid, chap, textlist):
log.debug(u'create_chapter %s,%s', bookid, chap)
#text list has book and chapter as first to elements of the array
for verse_number, verse_text in textlist.iteritems():
verse = Verse()
verse.book_id = bookid
verse.chapter = chap
verse.verse = verse_number
verse.text = verse_text
self.session.add(verse)
self.session.commit()
def create_book(self, bookname, bookabbrev, testament=1):
log.debug(u'create_book %s,%s', bookname, bookabbrev)
book = Book()
book.testament_id = testament
book.name = bookname
book.abbreviation = bookabbrev
self.session.add(book)
self.session.commit()
return book
def save_meta(self, key, value):
log.debug(u'save_meta %s/%s', key, value)
bmeta = BibleMeta()
bmeta.key = key
bmeta.value = value
self.session.add(bmeta)
self.session.commit()
def get_meta(self, metakey):
log.debug(u'get meta %s', metakey)
return self.session.query(BibleMeta).filter_by(key=metakey).first()
def delete_meta(self, metakey):
biblemeta = self.get_meta(metakey)
try:
self.session.delete(biblemeta)
self.session.commit()
return True
except:
return False
def _load_testament(self, testament):
log.debug(u'load_testaments %s', testament)
test = ONTestament()
test.name = testament
self.session.add(test)
self.session.commit()
def get_bible_books(self):
log.debug(u'get_bible_books')
return self.session.query(Book).order_by(Book.id).all()
def get_max_bible_book_verses(self, bookname, chapter):
log.debug(u'get_max_bible_book_verses %s, %s', bookname, chapter)
verse = self.session.query(Verse).join(Book).filter(
Book.name == bookname).filter(
Verse.chapter == chapter).order_by(Verse.verse.desc()).first()
if verse == None:
return 0
else:
return verse.verse
def get_max_bible_book_chapter(self, bookname):
log.debug(u'get_max_bible_book_chapter %s', bookname)
verse = self.session.query(Verse).join(Book).filter(
Book.name == bookname).order_by(Verse.chapter.desc()).first()
if verse == None:
return 0
else:
return verse.chapter
def get_bible_book(self, bookname):
log.debug(u'get_bible_book %s', bookname)
book = self.session.query(Book).filter(
Book.name.like(bookname + u'%')).first()
if book is None:
book = self.session.query(Book).filter(
Book.abbreviation.like(bookname + u'%')).first()
return book
def get_bible_chapter(self, id, chapter):
log.debug(u'get_bible_chapter %s, %s', id, chapter)
return self.session.query(Verse).filter_by(chapter=chapter).filter_by(
book_id=id).first()
def get_bible_text(self, bookname, chapter, sverse, everse):
log.debug(u'get_bible_text %s, %s, %s, %s', bookname, chapter, sverse,
everse)
#Look up book name or abbreviation
book = self.get_bible_book(bookname)
if book:
bookname = book.name
log.debug(u'bookname corrected to %s' % bookname)
verses = self.session.query(Verse).join(Book).filter(
Book.name == bookname).filter(Verse.chapter == chapter).filter(
Verse.verse>=sverse).filter(Verse.verse<=everse).order_by(
Verse.verse).all()
return verses
def get_verses_from_text(self, versetext):
log.debug(u'get_verses_from_text %s',versetext)
versetext = u'%%%s%%' % versetext
verses = self.session.query(Verse).filter(
Verse.text.like(versetext)).all()
return verses
def dump_bible(self):
log.debug( u'.........Dumping Bible Database')
log.debug( '...............................Books ')
books = self.session.query(Book).all()
log.debug(books)
log.debug( u'...............................Verses ')
verses = self.session.query(Verse).all()
log.debug(verses)

View File

@ -1,228 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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
from common import BibleCommon, SearchResults
class BGExtract(BibleCommon):
global log
log = logging.getLogger(u'BibleHTTPMgr(BG_extract)')
log.info(u'BG_extract loaded')
def __init__(self, proxyurl= None):
log.debug(u'init %s', proxyurl)
self.proxyurl = proxyurl
def get_bible_chapter(self, version, bookname, chapter) :
"""
Access and decode bibles via the BibleGateway website
``Version``
The version of the bible like 31 for New International version
``bookname``
Name of the Book
``chapter``
Chapter number
"""
log.debug(u'get_bible_chapter %s,%s,%s',
version, bookname, chapter)
urlstring = \
u'http://www.biblegateway.com/passage/?search=%s+%d&version=%s' % \
(bookname, chapter, version)
log.debug(u'BibleGateway urm = %s' % urlstring)
xml_string = self._get_web_text(urlstring, self.proxyurl)
verseSearch = u'<sup class=\"versenum'
verseFootnote = u'<sup class=\'footnote'
verse = 1
i = xml_string.find(u'result-text-style-normal') + 26
xml_string = xml_string[i:len(xml_string)]
versePos = xml_string.find(verseSearch)
bible = {}
while versePos > -1:
# clear out string
verseText = u''
versePos = xml_string.find(u'</sup>', versePos) + 6
i = xml_string.find(verseSearch, versePos + 1)
# Not sure if this is needed now
if i == -1:
i = xml_string.find(u'</div', versePos + 1)
j = xml_string.find(u'<strong', versePos + 1)
if j > 0 and j < i:
i = j
verseText = xml_string[versePos + 7 : i ]
# store the verse
bible[verse] = self._clean_text(verseText)
versePos = -1
else:
verseText = xml_string[versePos: i]
start_tag = verseText.find(verseFootnote)
while start_tag > -1:
end_tag = verseText.find(u'</sup>')
verseText = verseText[:start_tag] + verseText[end_tag + 6:len(verseText)]
start_tag = verseText.find(verseFootnote)
# Chop off verse and start again
xml_string = xml_string[i:]
#look for the next verse
versePos = xml_string.find(verseSearch)
# store the verse
bible[verse] = self._clean_text(verseText)
verse += 1
return SearchResults(bookname, chapter, bible)
class CWExtract(BibleCommon):
global log
log = logging.getLogger(u'BibleHTTPMgr(CWExtract)')
log.info(u'CWExtract loaded')
def __init__(self, proxyurl=None):
log.debug(u'init %s', proxyurl)
self.proxyurl = proxyurl
def get_bible_chapter(self, version, bookname, chapter) :
log.debug(u'getBibleChapter %s,%s,%s',
version,bookname, chapter)
"""
Access and decode bibles via the Crosswalk website
``version``
The version of the bible like niv for New International Version
``bookname``
Text name of in english e.g. 'gen' for Genesis
``chapter``
Chapter number
"""
log.debug(u'get_bible_chapter %s,%s,%s',
version, bookname, chapter)
bookname = bookname.replace(u' ', u'')
urlstring = u'http://bible.crosswalk.com/OnlineStudyBible/bible.cgi?word=%s+%d&version=%s'\
% (bookname, chapter, version)
xml_string = self._get_web_text(urlstring, self.proxyurl)
## Strip Book Title from Heading to return it to system
##
i = xml_string.find(u'<title>')
j = xml_string.find(u'-', i)
book_title = xml_string[i + 7:j]
book_title = book_title.rstrip()
log.debug(u'Book Title %s', book_title)
i = book_title.rfind(u' ')
book_chapter = book_title[i+1:len(book_title)].rstrip()
book_title = book_title[:i].rstrip()
log.debug(u'Book Title %s', book_title)
log.debug(u'Book Chapter %s', book_chapter)
# Strip Verse Data from Page and build an array
i = xml_string.find(u'NavCurrentChapter')
xml_string = xml_string[i:len(xml_string)]
i = xml_string.find(u'<TABLE')
xml_string = xml_string[i:len(xml_string)]
i = xml_string.find(u'<B>')
#remove the <B> at the front
xml_string = xml_string[i + 3 :len(xml_string)]
# Remove the heading for the book
i = xml_string.find(u'<B>')
#remove the <B> at the front
xml_string = xml_string[i + 3 :len(xml_string)]
versePos = xml_string.find(u'<BLOCKQUOTE>')
bible = {}
while versePos > 0:
verseText = u''
versePos = xml_string.find(u'<B><I>', versePos) + 6
i = xml_string.find(u'</I></B>', versePos)
# Got the Chapter
verse = xml_string[versePos:i]
# move the starting position to begining of the text
versePos = i + 8
# find the start of the next verse
i = xml_string.find(u'<B><I>', versePos)
if i == -1:
i = xml_string.find(u'</BLOCKQUOTE>',versePos)
verseText = xml_string[versePos: i]
versePos = 0
else:
verseText = xml_string[versePos: i]
versePos = i
bible[verse] = self._clean_text(verseText)
return SearchResults(book_title, book_chapter, bible)
class BibleHTTPImpl():
global log
log = logging.getLogger(u'BibleHTTPMgr')
log.info(u'BibleHTTP manager loaded')
def __init__(self):
"""
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.
Init confirms the bible exists and stores the database path.
"""
self.biblesource = u''
self.proxyurl = None
self.bibleid = None
def set_proxy(self, proxyurl):
"""
Set the Proxy Url
"""
log.debug(u'set_proxy %s', proxyurl)
self.proxyurl = proxyurl
def set_bibleid(self, bibleid):
"""
Set the bible id.
The shore identifier of the the bible.
"""
log.debug(u'set_bibleid %s', bibleid)
self.bibleid = bibleid
def set_bible_source(self, biblesource):
"""
Set the source of where the bible text is coming from
"""
log.debug(u'set_bible_source %s', biblesource)
self.biblesource = biblesource
def get_bible_chapter(self, version, bookname, chapter):
"""
Receive the request and call the relevant handler methods
"""
log.debug(u'get_bible_chapter %s,%s,%s',
version, bookname, chapter)
log.debug(u'biblesource = %s', self.biblesource)
try:
if self.biblesource.lower() == u'crosswalk':
ev = CWExtract(self.proxyurl)
else:
ev = BGExtract(self.proxyurl)
return ev.get_bible_chapter(self.bibleid, bookname, chapter)
except:
log.exception("Failed to get bible chapter")

View File

@ -27,15 +27,14 @@ import logging
from PyQt4 import QtCore, QtGui
from openlp.core.lib import str_to_bool, Receiver
from openlp.core.lib import SettingsTab
from openlp.core.lib import str_to_bool, Receiver, SettingsTab
log = logging.getLogger(__name__)
class BiblesTab(SettingsTab):
"""
BiblesTab is the Bibles settings tab in the settings dialog.
"""
global log
log = logging.getLogger(u'BibleTab')
log.info(u'Bible Tab loaded')
def __init__(self, title, section=None):
@ -226,4 +225,4 @@ class BiblesTab(SettingsTab):
# Not Found
id = 0
self.bible_theme = u''
self.BibleThemeComboBox.setCurrentIndex(id)
self.BibleThemeComboBox.setCurrentIndex(id)

View File

@ -24,10 +24,99 @@
###############################################################################
import urllib2
import chardet
import logging
import re
import chardet
class SearchResults:
only_verses = re.compile(r'([\w .]+)[ ]+([0-9]+)[ ]*[:|v|V][ ]*([0-9]+)'
r'(?:[ ]*-[ ]*([0-9]+|end))?(?:[ ]*,[ ]*([0-9]+)(?:[ ]*-[ ]*([0-9]+|end))?)?',
re.UNICODE)
chapter_range = re.compile(r'([\w .]+)[ ]+([0-9]+)[ ]*[:|v|V][ ]*'
r'([0-9]+)[ ]*-[ ]*([0-9]+)[ ]*[:|v|V][ ]*([0-9]+)',
re.UNICODE)
log = logging.getLogger(__name__)
def parse_reference(reference):
"""
This is the über-awesome function that takes a person's typed in string
and converts it to a reference list, a list of references to be queried
from the Bible database files.
The reference list is a list of tuples, with each tuple structured like
this::
(book, chapter, start_verse, end_verse)
"""
reference = reference.strip()
log.debug('parse_reference("%s")', reference)
reference_list = []
# We start with the most "complicated" match first, so that they are found
# first, and we don't have any "false positives".
match = chapter_range.match(reference)
if match:
log.debug('Found a chapter range.')
book = match.group(1)
from_verse = match.group(3)
to_verse = match.group(5)
if int(match.group(2)) == int(match.group(4)):
reference_list.append(
(match.group(1), int(match.group(2)), from_verse, to_verse)
)
else:
if int(match.group(2)) > int(match.group(4)):
from_chapter = int(match.group(4))
to_chapter = int(match.group(2))
else:
from_chapter = int(match.group(2))
to_chapter = int(match.group(4))
for chapter in xrange(from_chapter, to_chapter + 1):
if chapter == from_chapter:
reference_list.append(
(match.group(1), chapter, from_verse, -1)
)
elif chapter == to_chapter:
reference_list.append(
(match.group(1), chapter, 1, to_verse)
)
else:
reference_list.append(
(match.group(1), chapter, 1, -1)
)
else:
match = only_verses.match(reference)
if match:
log.debug('Found a verse range.')
book = match.group(1)
chapter = match.group(2)
verse = match.group(3)
if match.group(4) is None:
reference_list.append((book, chapter, verse, verse))
elif match.group(5) is None:
end_verse = match.group(4)
if end_verse == u'end':
end_verse = -1
reference_list.append((book, chapter, verse, end_verse))
elif match.group(6) is None:
reference_list.extend([
(book, chapter, verse, match.group(4)),
(book, chapter, match.group(5), match.group(5))
])
else:
end_verse = match.group(6)
if end_verse == u'end':
end_verse = -1
reference_list.extend([
(book, chapter, verse, match.group(4)),
(book, chapter, match.group(5), end_verse)
])
else:
log.debug('Didn\'t find anything.')
log.debug(reference_list)
return reference_list
class SearchResults(object):
"""
Encapsulate a set of search results. This is Bible-type independant.
"""
@ -77,16 +166,8 @@ class BibleCommon(object):
"""
A common ancestor for bible download sites.
"""
global log
log = logging.getLogger(u'BibleCommon')
log.info(u'BibleCommon')
def __init__(self):
"""
An empty constructor... not sure why I'm here.
"""
pass
def _get_web_text(self, urlstring, proxyurl):
"""
Get the HTML from the web page.
@ -165,4 +246,4 @@ class BibleCommon(object):
text = text[:start_tag] + text[end_tag + 1:]
start_tag = text.find(u'<')
text = text.replace(u'>', u'')
return text.rstrip().lstrip()
return text.rstrip().lstrip()

View File

@ -25,96 +25,97 @@
import logging
import chardet
import csv
from openlp.plugins.bibles.lib.common import BibleCommon
from openlp.core.lib import Receiver
from db import BibleDB
class BibleCSVImpl(BibleCommon):
global log
log = logging.getLogger(u'BibleCSVImpl')
log.info(u'BibleCVSImpl loaded')
def __init__(self, bibledb):
log = logging.getLogger(__name__)
class CSVBible(BibleDB):
"""
This class provides a specialisation for importing of CSV Bibles.
"""
def __init__(self, parent, **kwargs):
"""
Loads a Bible from a pair of CVS files passed in
This class assumes the files contain all the information and
a clean bible is being loaded.
"""
self.bibledb = bibledb
self.loadbible = True
BibleDB.__init__(self, parent, **kwargs)
log.info(self.__class__.__name__)
if u'booksfile' not in kwargs:
raise KeyError(u'You have to supply a file to import books from.')
self.booksfile = kwargs[u'booksfile']
if u'versesfile' not in kwargs:
raise KeyError(u'You have to supply a file to import verses from.')
self.versesfile = kwargs[u'versesfile']
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlpstopimport'), self.stop_import)
def stop_import(self):
self.loadbible = False
"""
Stops the import of the Bible.
"""
log.debug('Stopping import!')
self.stop_import_flag = True
def load_data(self, booksfile, versesfile, dialogobject):
def do_import(self):
#Populate the Tables
success = True
fbooks = None
books_file = None
try:
fbooks = open(booksfile, 'r')
count = 0
for line in fbooks:
books_file = open(self.booksfile, 'r')
dialect = csv.Sniffer().sniff(books_file.read(1024))
books_file.seek(0)
books_reader = csv.reader(books_file, dialect)
for line in books_reader:
# cancel pressed
if not self.loadbible:
if self.stop_import_flag:
break
details = chardet.detect(line)
line = unicode(line, details['encoding'])
p = line.split(u',')
p1 = p[1].replace(u'"', u'')
p2 = p[2].replace(u'"', u'')
p3 = p[3].replace(u'"', u'')
self.bibledb.create_book(p2, p3, int(p1))
count += 1
#Flush the screen events
if count % 3 == 0:
Receiver.send_message(u'process_events')
count = 0
details = chardet.detect(line[1])
self.create_book(unicode(line[1], details['encoding']),
line[2], int(line[0]))
Receiver.send_message(u'process_events')
except:
log.exception(u'Loading books from file failed')
success = False
finally:
if fbooks:
fbooks.close()
if books_file:
books_file.close()
if not success:
return False
fverse = None
verse_file = None
try:
fverse = open(versesfile, 'r')
count = 0
book_ptr = None
for line in fverse:
if not self.loadbible: # cancel pressed
verse_file = open(versesfile, 'r')
dialect = csv.Sniffer().sniff(verse_file.read(1024))
verse_file.seek(0)
verse_reader = csv.reader(verse_file, dialect)
for line in verse_reader:
if self.stop_import_flag: # cancel pressed
break
details = chardet.detect(line)
line = unicode(line, details['encoding'])
# split into 3 units and leave the rest as a single field
p = line.split(u',', 3)
p0 = p[0].replace(u'"', u'')
p3 = p[3].replace(u'"', u'')
if book_ptr is not p0:
book = self.bibledb.get_bible_book(p0)
details = chardet.detect(line[3])
if book_ptr != line[0]:
book = self.get_book(line[0])
book_ptr = book.name
# increament the progress bar
dialogobject.incrementProgressBar(u'Importing %s %s' % \
book.name)
self.bibledb.add_verse(book.id, p[1], p[2], p3)
count += 1
#Every x verses repaint the screen
if count % 3 == 0:
Receiver.send_message(u'process_events')
count = 0
self.bibledb.save_verses()
self.wizard.incrementProgressBar(
u'Importing %s %s' % (book.name, line[1]))
self.commit()
self.create_verse(book.id, line[1], line[2],
unicode(line[3], details['encoding']))
Receiver.send_message(u'process_events')
self.commit()
except:
log.exception(u'Loading verses from file failed')
success = False
finally:
if fverse:
fverse.close()
if not self.loadbible:
dialogobject.incrementProgressBar(u'Import canceled!')
dialogobject.ImportProgressBar.setValue(
dialogobject.ImportProgressBar.maximum())
if verse_file:
verse_file.close()
if self.stop_import_flag:
self.wizard.incrementProgressBar(u'Import canceled!')
return False
else:
return success
return success

View File

@ -0,0 +1,286 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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 os
import logging
import chardet
from sqlalchemy import or_
from PyQt4 import QtCore
from openlp.plugins.bibles.lib.models import *
log = logging.getLogger(__name__)
class BibleDB(QtCore.QObject):
"""
This class represents a database-bound Bible. It is used as a base class
for all the custom importers, so that the can implement their own import
methods, but benefit from the database methods in here via inheritance,
rather than depending on yet another object.
"""
def __init__(self, parent, **kwargs):
"""
The constructor loads up the database and creates and initialises the
tables if the database doesn't exist.
**Required keyword arguments:**
``path``
The path to the bible database file.
``name``
The name of the database. This is also used as the file name for
SQLite databases.
``config``
The configuration object, passed in from the plugin.
"""
log.info(u'BibleDB loaded')
QtCore.QObject.__init__(self)
if u'path' not in kwargs:
raise KeyError(u'Missing keyword argument "path".')
if u'name' not in kwargs:
raise KeyError(u'Missing keyword argument "name".')
if u'config' not in kwargs:
raise KeyError(u'Missing keyword argument "config".')
self.stop_import_flag = False
self.name = kwargs[u'name']
self.config = kwargs[u'config']
self.db_file = os.path.join(kwargs[u'path'],
u'%s.sqlite' % kwargs[u'name'])
log.debug(u'Load bible %s on path %s', kwargs[u'name'], self.db_file)
db_type = self.config.get_config(u'db type', u'sqlite')
db_url = u''
if db_type == u'sqlite':
db_url = u'sqlite:///' + self.db_file
else:
db_url = u'%s://%s:%s@%s/%s' % \
(db_type, self.config.get_config(u'db username'),
self.config.get_config(u'db password'),
self.config.get_config(u'db hostname'),
self.config.get_config(u'db database'))
self.metadata, self.session = init_models(db_url)
self.metadata.create_all(checkfirst=True)
def register(self, wizard):
"""
This method basically just initialialises the database. It is called
from the Bible Manager when a Bible is imported. Descendant classes
may want to override this method to supply their own custom
initialisation as well.
"""
self.wizard = wizard
self.create_tables()
return self.name
def commit(self):
log.debug('Committing...')
self.session.commit()
def create_tables(self):
log.debug(u'createTables')
self.create_meta(u'dbversion', u'2')
self.create_testament(u'Old Testament')
self.create_testament(u'New Testament')
self.create_testament(u'Apocrypha')
def create_testament(self, testament):
log.debug(u'BibleDB.create_testament("%s")', testament)
self.session.add(Testament.populate(name=testament))
self.commit()
def create_book(self, name, abbrev, testament=1):
log.debug(u'create_book %s,%s', name, abbrev)
book = Book.populate(name=name, abbreviation=abbrev,
testament_id=testament)
self.session.add(book)
self.commit()
return book
def create_chapter(self, book_id, chapter, textlist):
log.debug(u'create_chapter %s,%s', book_id, chapter)
#text list has book and chapter as first two elements of the array
for verse_number, verse_text in textlist.iteritems():
verse = Verse.populate(
book_id = book_id,
chapter = chapter,
verse = verse_number,
text = verse_text
)
self.session.add(verse)
self.commit()
def create_verse(self, book_id, chapter, verse, text):
if not isinstance(text, unicode):
details = chardet.detect(text)
text = unicode(text, details[u'encoding'])
verse = Verse.populate(
book_id=book_id,
chapter=chapter,
verse=verse,
text=text
)
self.session.add(verse)
return verse
def create_meta(self, key, value):
log.debug(u'save_meta %s/%s', key, value)
self.session.add(BibleMeta.populate(key=key, value=value))
self.commit()
def get_books(self):
log.debug(u'BibleDB.get_books()')
return self.session.query(Book).order_by(Book.id).all()
def get_book(self, book):
log.debug(u'BibleDb.get_book("%s")', book)
db_book = self.session.query(Book)\
.filter(Book.name.like(book + u'%'))\
.first()
if db_book is None:
db_book = self.session.query(Book)\
.filter(Book.abbreviation.like(book + u'%'))\
.first()
return db_book
def get_chapter(self, id, chapter):
log.debug(u'BibleDB.get_chapter("%s", %s)', id, chapter)
return self.session.query(Verse)\
.filter_by(chapter=chapter)\
.filter_by(book_id=id)\
.first()
def get_verses(self, reference_list):
"""
This is probably the most used function. It retrieves the list of
verses based on the user's query.
``reference_list``
This is the list of references the media manager item wants. It is
a list of tuples, with the following format::
(book, 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::
[(u'Genesis', 1, 1, 1), (u'Genesis', 2, 2, 3)]
"""
log.debug(u'BibleDB.get_verses: %s', reference_list)
verse_list = []
for book, chapter, start_verse, end_verse in reference_list:
db_book = self.get_book(book)
if end_verse == -1:
end_verse = self.get_verse_count(book, chapter)
if db_book:
book = db_book.name
log.debug(u'Book name corrected to "%s"', book)
verses = self.session.query(Verse)\
.filter_by(book_id=db_book.id)\
.filter_by(chapter=chapter)\
.filter(Verse.verse >= start_verse)\
.filter(Verse.verse <= end_verse)\
.order_by(Verse.verse)\
.all()
verse_list.extend(verses)
return verse_list
def verse_search(self, text):
"""
Search for verses containing text ``text``.
``text``
The text to search for. If the text contains commas, it will be
split apart and OR'd on the list of values. If the text just
contains spaces, it will split apart and AND'd on the list of
values.
"""
log.debug(u'BibleDB.verse_search("%s")', text)
verses = self.session.query(Verse)
if text.find(u',') > -1:
or_clause = []
keywords = [u'%%%s%%' % keyword.strip() for keyword in text.split(u',')]
for keyword in keywords:
or_clause.append(Verse.text.like(keyword))
verses = verses.filter(or_(*or_clause))
else:
keywords = [u'%%%s%%' % keyword.strip() for keyword in text.split(u' ')]
for keyword in keywords:
verses = verses.filter(Verse.text.like(keyword))
verses = verses.all()
return verses
def get_chapter_count(self, book):
log.debug(u'BibleDB.get_chapter_count("%s")', book)
count = self.session.query(Verse.chapter).join(Book)\
.filter(Book.name==book)\
.distinct().count()
#verse = self.session.query(Verse).join(Book).filter(
# Book.name == bookname).order_by(Verse.chapter.desc()).first()
if not count:
return 0
else:
return count
def get_verse_count(self, book, chapter):
log.debug(u'BibleDB.get_verse_count("%s", %s)', book, chapter)
count = self.session.query(Verse).join(Book)\
.filter(Book.name==book)\
.filter(Verse.chapter==chapter)\
.count()
#verse = self.session.query(Verse).join(Book).filter(
# Book.name == bookname).filter(
# Verse.chapter == chapter).order_by(Verse.verse.desc()).first()
if not count:
return 0
else:
return count
def get_meta(self, key):
log.debug(u'get meta %s', key)
return self.session.query(BibleMeta).get(key)
def delete_meta(self, metakey):
biblemeta = self.get_meta(metakey)
try:
self.session.delete(biblemeta)
self.commit()
return True
except:
return False
def dump_bible(self):
log.debug(u'.........Dumping Bible Database')
log.debug('...............................Books ')
books = self.session.query(Book).all()
log.debug(books)
log.debug(u'...............................Verses ')
verses = self.session.query(Verse).all()
log.debug(verses)

View File

@ -0,0 +1,365 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# 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 urllib2
import os
import sqlite3
from BeautifulSoup import BeautifulSoup
from openlp.core.lib import Receiver
from common import BibleCommon, SearchResults
from db import BibleDB
from openlp.plugins.bibles.lib.models import Book
log = logging.getLogger(__name__)
class HTTPBooks(object):
cursor = None
@staticmethod
def get_cursor():
if HTTPBooks.cursor is None:
filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)),
u'..', u'resources', u'httpbooks.sqlite')
conn = sqlite3.connect(filepath)
HTTPBooks.cursor = conn.cursor()
return HTTPBooks.cursor
@staticmethod
def run_sql(query, parameters=()):
cursor = HTTPBooks.get_cursor()
cursor.execute(query, parameters)
return cursor.fetchall()
@staticmethod
def get_books():
books = HTTPBooks.run_sql(u'SELECT id, testament_id, name, '
u'abbreviation, chapters FROM books ORDER BY id')
book_list = []
for book in books:
book_list.append({
u'id': book[0],
u'testament_id': book[1],
u'name': unicode(book[2]),
u'abbreviation': unicode(book[3]),
u'chapters': book[4]
})
return book_list
@staticmethod
def get_book(name):
if not isinstance(name, unicode):
name = unicode(name)
books = HTTPBooks.run_sql(u'SELECT id, testament_id, name, '
u'abbreviation, chapters FROM books WHERE name = ? OR '
u'abbreviation = ?', (name, name))
if books:
return {
u'id': books[0][0],
u'testament_id': books[0][1],
u'name': unicode(books[0][2]),
u'abbreviation': unicode(books[0][3]),
u'chapters': books[0][4]
}
else:
return None
@staticmethod
def get_chapter(name, chapter):
if not isinstance(name, int):
chapter = int(chapter)
book = HTTPBooks.get_book(name)
chapters = HTTPBooks.run_sql(u'SELECT id, book_id, chapter, '
u'verses FROM chapters WHERE book_id = ?', (book[u'id'],))
if chapters:
return {
u'id': chapters[0][0],
u'book_id': chapters[0][1],
u'chapter': chapters[0][2],
u'verses': chapters[0][3]
}
else:
return None
@staticmethod
def get_chapter_count(book):
details = HTTPBooks.get_book(book)
if details:
return details[u'chapters']
return 0
@staticmethod
def get_verse_count(book, chapter):
details = HTTPBooks.get_chapter(book, chapter)
if details:
return details[u'verses']
return 0
class BGExtract(BibleCommon):
log.info(u'%s BGExtract loaded', __name__)
def __init__(self, proxyurl=None):
log.debug(u'init %s', proxyurl)
self.proxyurl = proxyurl
def get_bible_chapter(self, version, bookname, chapter) :
"""
Access and decode bibles via the BibleGateway website
``Version``
The version of the bible like 31 for New International version
``bookname``
Name of the Book
``chapter``
Chapter number
"""
log.debug(u'get_bible_chapter %s, %s, %s', version, bookname, chapter)
urlstring = u'http://www.biblegateway.com/passage/?search=%s+%s' \
u'&version=%s' % (bookname, chapter, version)
log.debug(u'BibleGateway url = %s' % urlstring)
xml_string = self._get_web_text(urlstring, self.proxyurl)
verseSearch = u'<sup class=\"versenum'
verseFootnote = u'<sup class=\'footnote'
verse = 1
i = xml_string.find(u'result-text-style-normal') + 26
xml_string = xml_string[i:len(xml_string)]
versePos = xml_string.find(verseSearch)
bible = {}
while versePos > -1:
# clear out string
verseText = u''
versePos = xml_string.find(u'</sup>', versePos) + 6
i = xml_string.find(verseSearch, versePos + 1)
# Not sure if this is needed now
if i == -1:
i = xml_string.find(u'</div', versePos + 1)
j = xml_string.find(u'<strong', versePos + 1)
if j > 0 and j < i:
i = j
verseText = xml_string[versePos + 7 : i ]
# store the verse
bible[verse] = self._clean_text(verseText)
versePos = -1
else:
verseText = xml_string[versePos: i]
start_tag = verseText.find(verseFootnote)
while start_tag > -1:
end_tag = verseText.find(u'</sup>')
verseText = verseText[:start_tag] + verseText[end_tag + 6:len(verseText)]
start_tag = verseText.find(verseFootnote)
# Chop off verse and start again
xml_string = xml_string[i:]
#look for the next verse
versePos = xml_string.find(verseSearch)
# store the verse
bible[verse] = self._clean_text(verseText)
verse += 1
return SearchResults(bookname, chapter, bible)
class CWExtract(BibleCommon):
log.info(u'%s CWExtract loaded', __name__)
def __init__(self, proxyurl=None):
log.debug(u'init %s', proxyurl)
self.proxyurl = proxyurl
def get_bible_chapter(self, version, bookname, chapter):
log.debug(u'%s %s, %s, %s', __name__, version, bookname, chapter)
"""
Access and decode bibles via the Crosswalk website
``version``
The version of the bible like niv for New International Version
``bookname``
Text name of in english e.g. 'gen' for Genesis
``chapter``
Chapter number
"""
log.debug(u'get_bible_chapter %s,%s,%s',
version, bookname, chapter)
bookname = bookname.replace(u' ', u'')
chapter_url = u'http://www.biblestudytools.com/%s/%s/%s.html' % \
(version, bookname.lower(), chapter)
log.debug(u'URL: %s', chapter_url)
page = urllib2.urlopen(chapter_url)
if not page:
return None
soup = BeautifulSoup(page)
htmlverses = soup.findAll(u'span', u'versetext')
verses = {}
for verse in htmlverses:
Receiver.send_message(u'process_events')
versenumber = int(verse.contents[0].contents[0])
versetext = u''
for part in verse.contents:
if str(part)[0] != u'<':
versetext = versetext + part
elif part and part.attrMap and part.attrMap[u'class'] == u'WordsOfChrist':
for subpart in part.contents:
if str(subpart)[0] != '<':
versetext = versetext + subpart
versetext = versetext.strip(u'\n\r\t ')
verses[versenumber] = versetext
return SearchResults(bookname, chapter, verses)
class HTTPBible(BibleDB):
log.info(u'%s HTTPBible loaded' , __name__)
def __init__(self, parent, **kwargs):
"""
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.
Init confirms the bible exists and stores the database path.
"""
BibleDB.__init__(self, parent, **kwargs)
if u'download_source' not in kwargs:
raise KeyError(u'Missing keyword argument "download_source"')
if u'download_name' not in kwargs:
raise KeyError(u'Missing keyword argument "download_name"')
self.download_source = kwargs[u'download_source']
self.download_name = kwargs[u'download_name']
if u'proxy_server' in kwargs:
self.proxy_server = kwargs[u'proxy_server']
else:
self.proxy_server = None
if u'proxy_username' in kwargs:
self.proxy_username = kwargs[u'proxy_username']
else:
self.proxy_username = None
if u'proxy_password' in kwargs:
self.proxy_password = kwargs[u'proxy_password']
else:
self.proxy_password = None
def do_import(self):
self.wizard.ImportProgressBar.setMaximum(2)
self.wizard.incrementProgressBar('Registering bible...')
self.create_meta(u'download source', self.download_source)
self.create_meta(u'download name', self.download_name)
if self.proxy_server:
self.create_meta(u'proxy server', self.proxy_server)
if self.proxy_username:
# store the proxy userid
self.create_meta(u'proxy username', self.proxy_username)
if self.proxy_password:
# store the proxy password
self.create_meta(u'proxy password', self.proxy_password)
self.wizard.incrementProgressBar('Registered.')
return True
def get_verses(self, reference_list):
"""
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::
(book, 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::
[(u'Genesis', 1, 1, 1), (u'Genesis', 2, 2, 3)]
"""
for reference in reference_list:
log.debug('Reference: %s', reference)
book = reference[0]
db_book = self.get_book(book)
if not db_book:
book_details = self.lookup_book(book)
if not book_details:
Receiver.send_message(u'bible_nobook')
return []
db_book = self.create_book(book_details[u'name'],
book_details[u'abbreviation'], book_details[u'testament_id'])
book = db_book.name
if BibleDB.get_verse_count(self, book, reference[1]) == 0:
Receiver.send_message(u'bible_showprogress')
Receiver.send_message(u'process_events')
search_results = self.get_chapter(self.name, book, reference[1])
if search_results and search_results.has_verselist():
## We have found a book of the bible lets check to see
## if it was there. By reusing the returned book name
## we get a correct book. For example it is possible
## to request ac and get Acts back.
bookname = search_results.get_book()
# check to see if book/chapter exists
db_book = self.get_book(bookname)
self.create_chapter(db_book.id, search_results.get_chapter(),
search_results.get_verselist())
Receiver.send_message(u'bible_hideprogress')
Receiver.send_message(u'process_events')
return BibleDB.get_verses(self, reference_list)
def get_chapter(self, version, book, chapter):
"""
Receive the request and call the relevant handler methods
"""
log.debug(u'get_chapter %s, %s, %s', version, book, chapter)
log.debug(u'source = %s', self.download_source)
try:
if self.download_source.lower() == u'crosswalk':
ev = CWExtract(self.proxy_server)
else:
ev = BGExtract(self.proxy_server)
return ev.get_bible_chapter(self.download_name, book, chapter)
except:
log.exception("Failed to get bible chapter")
return None
def get_books(self):
return [Book.populate(name=book['name']) for book in HTTPBooks.get_books()]
def lookup_book(self, book):
return HTTPBooks.get_book(book)
def get_chapter_count(self, book):
return HTTPBooks.get_chapter_count(book)
def get_verse_count(self, book, chapter):
return HTTPBooks.get_verse_count(book, chapter)
def set_proxy_server(self, server):
self.proxy_server = server

View File

@ -26,340 +26,202 @@
import logging
import os
from bibleOpenSongimpl import BibleOpenSongImpl
from bibleOSISimpl import BibleOSISImpl
from bibleCSVimpl import BibleCSVImpl
from bibleDBimpl import BibleDBImpl
from bibleHTTPimpl import BibleHTTPImpl
from common import parse_reference
from opensong import OpenSongBible
from osis import OSISBible
from csvbible import CSVBible
from db import BibleDB
from http import HTTPBible
log = logging.getLogger(__name__)
class BibleMode(object):
"""
This is basically an enumeration class which specifies the mode of a Bible.
Mode refers to whether or not a Bible in OpenLP is a full Bible or needs to
be downloaded from the Internet on an as-needed basis.
"""
Full = 1
Partial = 2
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.
"""
Unknown = -1
OSIS = 0
CSV = 1
OpenSong = 2
WebDownload = 3
@classmethod
def get_handler(class_, id):
if id == class_.OSIS:
return BibleOSISImpl
elif id == class_.CSV:
return BibleCSVImpl
elif id == class_.OpenSong:
return BibleOpenSongImpl
elif id == class_.WebDownload:
return BibleHTTPImpl
@staticmethod
def get_class(id):
"""
Return the appropriate imeplementation class.
"""
if id == BibleFormat.OSIS:
return OSISBible
elif id == BibleFormat.CSV:
return CSVBible
elif id == BibleFormat.OpenSong:
return OpenSongBible
elif id == BibleFormat.WebDownload:
return HTTPBible
else:
return None
@staticmethod
def list():
return [
BibleFormat.OSIS,
BibleFormat.CSV,
BibleFormat.OpenSong,
BibleFormat.WebDownload
]
class BibleManager(object):
"""
The Bible manager which holds and manages all the Bibles.
"""
global log
log = logging.getLogger(u'BibleManager')
log.info(u'Bible manager loaded')
def __init__(self, config):
def __init__(self, parent, config):
"""
Finds all the bibles defined for the system and creates an
interface object for each bible containing connection
information. Throws Exception if no Bibles are found.
Finds all the bibles defined for the system and creates an interface
object for each bible containing connection information. Throws
Exception if no Bibles are found.
Init confirms the bible exists and stores the database path.
``config``
The plugin's configuration object.
"""
self.config = config
log.debug(u'Bible Initialising')
self.config = config
self.parent = parent
self.web = u'Web'
# dict of bible database objects
self.bible_db_cache = None
# dict of bible http readers
self.bible_http_cache = None
self.biblePath = self.config.get_data_path()
#get proxy name for screen
self.proxyname = self.config.get_config(u'proxy name')
self.bibleSuffix = u'sqlite'
self.dialogobject = None
self.db_cache = None
self.path = self.config.get_data_path()
self.proxy_name = self.config.get_config(u'proxy name')
self.suffix = u'sqlite'
self.import_wizard = None
self.reload_bibles()
self.media = None
def reload_bibles(self):
"""
Reloads the Bibles from the available Bible databases on disk. If a web
Bible is encountered, an instance of HTTPBible is loaded instead of the
BibleDB class.
"""
log.debug(u'Reload bibles')
files = self.config.get_files(self.bibleSuffix)
files = self.config.get_files(self.suffix)
log.debug(u'Bible Files %s', files)
self.bible_db_cache = {}
self.bible_http_cache = {}
# books of the bible with testaments
self.book_testaments = {}
# books of the bible with chapter count
self.book_chapters = []
# books of the bible with abbreviation
self.book_abbreviations = {}
self.web_bibles_present = False
for f in files:
nme = f.split(u'.')
bname = nme[0]
self.bible_db_cache[bname] = BibleDBImpl(self.biblePath,
bname, self.config)
self.db_cache = {}
for filename in files:
name, extension = os.path.splitext(filename)
self.db_cache[name] = BibleDB(self.parent, path=self.path, name=name, config=self.config)
# look to see if lazy load bible exists and get create getter.
biblesource = self.bible_db_cache[bname].get_meta(u'WEB')
if biblesource:
self.web_bibles_present = True
nhttp = BibleHTTPImpl()
# tell The Server where to get the verses from.
nhttp.set_bible_source(biblesource.value)
self.bible_http_cache [bname] = nhttp
# look to see if lazy load bible exists and get create getter.
meta = self.bible_db_cache[bname].get_meta(u'proxy')
proxy = None
if meta:
proxy = meta.value
# tell The Server where to get the verses from.
nhttp.set_proxy(proxy)
# look to see if lazy load bible exists and get create getter.
bibleid = self.bible_db_cache[bname].get_meta(u'bibleid').value
# tell The Server where to get the verses from.
nhttp.set_bibleid(bibleid)
else:
# makes the Full / partial code easier.
self.bible_http_cache [bname] = None
if self.web_bibles_present:
# books of the bible linked to bibleid {osis, name}
self.book_testaments = {}
# books of the bible linked to bibleid {osis, abbrev}
self.book_abbreviations = {}
filepath = os.path.split(os.path.abspath(__file__))[0]
filepath = os.path.abspath(os.path.join(
filepath, u'..', u'resources',u'httpbooks.csv'))
fbibles = None
try:
fbibles = open(filepath, u'r')
for line in fbibles:
p = line.split(u',')
self.book_abbreviations[p[0]] = p[1].replace(u'\n', '')
self.book_testaments[p[0]] = p[2].replace(u'\n', '')
self.book_chapters.append({u'book':p[0], u'total':p[3].replace(u'\n', '')})
except:
log.exception(u'Failed to load bible')
finally:
if fbibles:
fbibles.close()
log.debug(u'Bible Initialised')
source = self.db_cache[name].get_meta(u'download source')
if source:
download_name = self.db_cache[name].get_meta(u'download name').value
meta_proxy = self.db_cache[name].get_meta(u'proxy url')
web_bible = HTTPBible(self.parent, path=self.path, name=name,
config=self.config, download_source=source.value,
download_name=download_name)
if meta_proxy:
web_bible.set_proxy_server(meta_proxy.value)
#del self.db_cache[name]
self.db_cache[name] = web_bible
log.debug(u'Bibles reloaded')
def set_process_dialog(self, dialogobject):
def set_process_dialog(self, wizard):
"""
Sets the reference to the dialog with the progress bar on it.
``dialogobject``
The reference to the dialog.
``dialog``
The reference to the import wizard.
"""
self.dialogobject = dialogobject
self.import_wizard = wizard
def import_bible(self, type, **kwargs):
"""
Register a bible in the bible cache, and then import the verses.
``type``
What type of Bible,
What type of Bible, one of the ``BibleFormat`` values.
``**kwargs``
Keyword arguments to send to the actual importer class.
"""
pass
class_ = BibleFormat.get_class(type)
kwargs['path'] = self.path
kwargs['config'] = self.config
importer = class_(self.parent, **kwargs)
name = importer.register(self.import_wizard)
self.db_cache[name] = importer
return importer.do_import()
def register_http_bible(self, biblename, biblesource, bibleid,
proxyurl=None, proxyid=None, proxypass=None):
def get_bibles(self):
"""
Return a list of bibles from a given URL. The selected Bible
can then be registered and LazyLoaded into a database.
``biblename``
The name of the bible to register.
``biblesource``
Where this Bible stores it's verses.
``bibleid``
The identifier for a Bible.
``proxyurl``
Defaults to *None*. An optional URL to a proxy server.
``proxyid``
Defaults to *None*. A username for logging into the proxy
server.
``proxypass``
Defaults to *None*. The password to accompany the username.
"""
log.debug(u'register_HTTP_bible %s, %s, %s, %s, %s, %s',
biblename, biblesource, bibleid, proxyurl, proxyid, proxypass)
if self._is_new_bible(biblename):
# Create new Bible
nbible = BibleDBImpl(self.biblePath, biblename, self.config)
# Create Database
nbible.create_tables()
self.bible_db_cache[biblename] = nbible
nhttp = BibleHTTPImpl()
nhttp.set_bible_source(biblesource)
self.bible_http_cache[biblename] = nhttp
# register a lazy loading interest
nbible.save_meta(u'WEB', biblesource)
# store the web id of the bible
nbible.save_meta(u'bibleid', bibleid)
if proxyurl:
# store the proxy URL
nbible.save_meta(u'proxy', proxyurl)
nhttp.set_proxy(proxyurl)
if proxyid:
# store the proxy userid
nbible.save_meta(u'proxyid', proxyid)
if proxypass:
# store the proxy password
nbible.save_meta(u'proxypass', proxypass)
return True
else:
log.debug(u'register_http_file_bible %s not created already exists',
biblename)
return False
def register_csv_file_bible(self, biblename, booksfile, versefile):
"""
Method to load a bible from a set of files into a database.
If the database exists it is deleted and the database is reloaded
from scratch.
"""
log.debug(u'register_CSV_file_bible %s,%s,%s',
biblename, booksfile, versefile)
if self._is_new_bible(biblename):
# Create new Bible
nbible = BibleDBImpl(self.biblePath, biblename, self.config)
# Create database
nbible.create_tables()
# Cache the database for use later
self.bible_db_cache[biblename] = nbible
# Create the loader and pass in the database
bcsv = BibleCSVImpl(nbible)
return bcsv.load_data(booksfile, versefile, self.dialogobject)
else:
log.debug(u'register_csv_file_bible %s not created already exists',
biblename)
return False
def register_osis_file_bible(self, biblename, osisfile):
"""
Method to load a bible from a osis xml file extracted from Sword bible
viewer. If the database exists it is deleted and the database is
reloaded from scratch.
"""
log.debug(u'register_OSIS_file_bible %s, %s', biblename, osisfile)
if self._is_new_bible(biblename):
# Create new Bible
nbible = BibleDBImpl(self.biblePath, biblename, self.config)
# Create Database
nbible.create_tables()
# Cache the database for use later
self.bible_db_cache[biblename] = nbible
# Create the loader and pass in the database
bosis = BibleOSISImpl(self.biblePath, nbible)
return bosis.load_data(osisfile, self.dialogobject)
else:
log.debug(
u'register_OSIS_file_bible %s, %s not created already exists',
biblename, osisfile)
return False
def register_opensong_bible(self, biblename, opensongfile):
"""
Method to load a bible from an OpenSong xml file. If the database
exists it is deleted and the database is reloaded from scratch.
"""
log.debug(u'register_opensong_file_bible %s, %s', biblename, opensongfile)
if self._is_new_bible(biblename):
# Create new Bible
nbible = BibleDBImpl(self.biblePath, biblename, self.config)
# Create Database
nbible.create_tables()
# Cache the database for use later
self.bible_db_cache[biblename] = nbible
# Create the loader and pass in the database
bcsv = BibleOpenSongImpl(self.biblePath, nbible)
bcsv.load_data(opensongfile, self.dialogobject)
return True
else:
log.debug(u'register_opensong_file_bible %s, %s not created '
u'already exists', biblename, opensongfile)
return False
def get_bibles(self, mode=BibleMode.Full):
"""
Returns a list of Books of the bible. When ``mode`` is set to
``BibleMode.Full`` this method returns all the Bibles for the
Advanced Search, and when the mode is ``BibleMode.Partial``
this method returns all the bibles for the Quick Search.
Returns a list of the names of available Bibles.
"""
log.debug(u'get_bibles')
bible_list = []
for bible_name, bible_object in self.bible_db_cache.iteritems():
if self.bible_http_cache[bible_name]:
bible_name = u'%s (%s)' % (bible_name, self.web)
bible_list.append(bible_name)
return bible_list
return [name for name, bible in self.db_cache.iteritems()]
def is_bible_web(self, bible):
pos_end = bible.find(u' (%s)' % self.web)
if pos_end != -1:
return True, bible[:pos_end]
return False, bible
def get_bible_books(self):
def get_books(self, bible):
"""
Returns a list of the books of the bible
"""
log.debug(u'get_bible_books')
return self.book_chapters
Returns a list of Bible books, and the number of chapters in that book.
def get_book_chapter_count(self, book):
``bible``
Unicode. The Bible to get the list of books from.
"""
log.debug(u'BibleManager.get_books("%s")', bible)
return [
{
u'name': book.name,
u'chapters': self.db_cache[bible].get_chapter_count(book.name)
}
for book in self.db_cache[bible].get_books()
]
def get_chapter_count(self, bible, book):
"""
Returns the number of Chapters for a given book
"""
log.debug(u'get_book_chapter_count %s', book)
return self.book_chapters[book]
return self.db_cache[bible].get_chapter_count(book)
def get_book_verse_count(self, bible, book, chapter):
def get_verse_count(self, bible, book, chapter):
"""
Returns all the number of verses for a given
book and chapterMaxBibleBookVerses
"""
log.debug(u'get_book_verse_count %s,%s,%s', bible, book, chapter)
web, bible = self.is_bible_web(bible)
if web:
count = self.bible_db_cache[bible].get_max_bible_book_verses(
book, chapter)
if count == 0:
# Make sure the first chapter has been downloaded
self.get_verse_text(bible, book, chapter, chapter, 1, 1)
count = self.bible_db_cache[bible].get_max_bible_book_verses(
book, chapter)
return count
else:
return self.bible_db_cache[bible].get_max_bible_book_verses(
book, chapter)
log.debug(u'BibleManager.get_verse_count("%s", "%s", %s)', bible, book, chapter)
return self.db_cache[bible].get_verse_count(book, chapter)
def get_verse_from_text(self, bible, versetext):
def get_verses(self, bible, versetext):
"""
Returns all the number of verses for a given
book and chapterMaxBibleBookVerses
Parses a scripture reference, fetches the verses from the Bible
specified, and returns a list of ``Verse`` objects.
``bible``
Unicode. The Bible to use.
``versetext``
Unicode. The scripture reference. Valid scripture references are:
- Genesis 1:1
- Genesis 1:1-10
- Genesis 1:1-2:10
"""
log.debug(u'get_verses_from_text %s,%s', bible, versetext)
web, bible = self.is_bible_web(bible)
return self.bible_db_cache[bible].get_verses_from_text(versetext)
log.debug(u'BibleManager.get_verses("%s", "%s")', bible, versetext)
reflist = parse_reference(versetext)
return self.db_cache[bible].get_verses(reflist)
def save_meta_data(self, bible, version, copyright, permissions):
"""
@ -367,124 +229,28 @@ class BibleManager(object):
"""
log.debug(u'save_meta data %s,%s, %s,%s',
bible, version, copyright, permissions)
self.bible_db_cache[bible].save_meta(u'Version', version)
self.bible_db_cache[bible].save_meta(u'Copyright', copyright)
self.bible_db_cache[bible].save_meta(u'Permissions', permissions)
self.db_cache[bible].create_meta(u'Version', version)
self.db_cache[bible].create_meta(u'Copyright', copyright)
self.db_cache[bible].create_meta(u'Permissions', permissions)
def get_meta_data(self, bible, key):
"""
Returns the meta data for a given key
"""
log.debug(u'get_meta %s,%s', bible, key)
web, bible = self.is_bible_web(bible)
return self.bible_db_cache[bible].get_meta(key)
return self.db_cache[bible].get_meta(key)
def get_verse_text(self, bible, bookname, schapter, echapter, sverse,
everse=0):
"""
Returns a list of verses for a given Book, Chapter and ranges of verses.
If the end verse(everse) is less then the start verse(sverse)
then only one verse is returned
``bible``
The name of the bible to be used
Rest can be guessed at !
"""
text = []
self.media.setQuickMessage(u'')
log.debug(u'get_verse_text %s,%s,%s,%s,%s,%s',
bible, bookname, schapter, echapter, sverse, everse)
# check to see if book/chapter exists fow HTTP bibles and load cache
# if necessary
web, bible = self.is_bible_web(bible)
if self.bible_http_cache[bible]:
book = self.bible_db_cache[bible].get_bible_book(bookname)
if book is None:
log.debug(u'get_verse_text : new book')
for chapter in range(schapter, echapter + 1):
self.media.setQuickMessage(
unicode(self.media.trUtf8('Downloading %s: %s')) %
(bookname, chapter))
search_results = \
self.bible_http_cache[bible].get_bible_chapter(
bible, bookname, chapter)
if search_results.has_verselist() :
## We have found a book of the bible lets check to see
## if it was there. By reusing the returned book name
## we get a correct book. For example it is possible
## to request ac and get Acts back.
bookname = search_results.get_book()
# check to see if book/chapter exists
book = self.bible_db_cache[bible].get_bible_book(
bookname)
if book is None:
## Then create book, chapter and text
book = self.bible_db_cache[bible].create_book(
bookname, self.book_abbreviations[bookname],
self.book_testaments[bookname])
log.debug(u'New http book %s, %s, %s',
book, book.id, book.name)
self.bible_db_cache[bible].create_chapter(
book.id, search_results.get_chapter(),
search_results.get_verselist())
else:
## Book exists check chapter and texts only.
v = self.bible_db_cache[bible].get_bible_chapter(
book.id, chapter)
if v is None:
self.media.setQuickMessage(
unicode(self.media.trUtf8('%Downloading %s: %s'))\
% (bookname, chapter))
self.bible_db_cache[bible].create_chapter(
book.id, chapter,
search_results.get_verselist())
else:
log.debug(u'get_verse_text : old book')
for chapter in range(schapter, echapter + 1):
v = self.bible_db_cache[bible].get_bible_chapter(
book.id, chapter)
if v is None:
try:
self.media.setQuickMessage(\
unicode(self.media.trUtf8('Downloading %s: %s'))
% (bookname, chapter))
search_results = \
self.bible_http_cache[bible].get_bible_chapter(
bible, bookname, chapter)
if search_results.has_verselist():
self.bible_db_cache[bible].create_chapter(
book.id, search_results.get_chapter(),
search_results.get_verselist())
except:
log.exception(u'Problem getting scripture online')
#Now get verses from database
if schapter == echapter:
text = self.bible_db_cache[bible].get_bible_text(bookname,
schapter, sverse, everse)
else:
for i in range (schapter, echapter + 1):
if i == schapter:
start = sverse
end = self.get_book_verse_count(bible, bookname, i)
elif i == echapter:
start = 1
end = everse
else:
start = 1
end = self.get_book_verse_count(bible, bookname, i)
txt = self.bible_db_cache[bible].get_bible_text(
bookname, i, start, end)
text.extend(txt)
return text
def _is_new_bible(self, name):
def exists(self, name):
"""
Check cache to see if new bible
"""
for bible, o in self.bible_db_cache.iteritems():
if not isinstance(name, unicode):
name = unicode(name)
for bible, db_object in self.db_cache.iteritems():
log.debug(u'Bible from cache in is_new_bible %s', bible)
if not isinstance(bible, unicode):
bible = unicode(bible)
if bible == name:
return False
return True
return True
return False

View File

@ -31,7 +31,8 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, Receiver, str_to_bool, \
BaseListWithDnD
from openlp.plugins.bibles.forms import ImportWizardForm
from openlp.plugins.bibles.lib.manager import BibleMode
log = logging.getLogger(__name__)
class BibleListView(BaseListWithDnD):
"""
@ -41,13 +42,13 @@ class BibleListView(BaseListWithDnD):
self.PluginName = u'Bibles'
BaseListWithDnD.__init__(self, parent)
def resizeEvent(self, event):
self.parent.onListViewResize(event.size().width(), event.size().width())
class BibleMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Bibles.
"""
global log
log = logging.getLogger(u'BibleMediaItem')
log.info(u'Bible Media Item loaded')
def __init__(self, parent, icon, title):
@ -56,6 +57,7 @@ class BibleMediaItem(MediaManagerItem):
self.IconPath = u'songs/song'
self.ListViewWithDnD_class = BibleListView
self.servicePath = None
self.lastReference = []
MediaManagerItem.__init__(self, parent, icon, title)
# place to store the search results
self.search_results = {}
@ -241,6 +243,24 @@ class BibleMediaItem(MediaManagerItem):
QtCore.SIGNAL(u'pressed()'), self.onQuickSearchButton)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_updated'), self.configUpdated)
# Other stuff
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'bible_showprogress'), self.onSearchProgressShow)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'bible_hideprogress'), self.onSearchProgressHide)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'bible_nobook'), self.onNoBookFound)
def addListViewToToolBar(self):
MediaManagerItem.addListViewToToolBar(self)
# Progress Bar
self.SearchProgress = QtGui.QProgressBar(self)
self.SearchProgress.setFormat('%p%')
self.SearchProgress.setMaximum(3)
self.SearchProgress.setGeometry(self.ListView.geometry().left(),
self.ListView.geometry().top(), 81, 23)
self.SearchProgress.setVisible(False)
self.SearchProgress.setObjectName(u'SearchProgress')
def configUpdated(self):
if str_to_bool(
@ -281,7 +301,7 @@ class BibleMediaItem(MediaManagerItem):
def initialise(self):
log.debug(u'bible manager initialise')
self.parent.biblemanager.media = self
self.parent.manager.media = self
self.loadBibles()
self.configUpdated()
log.debug(u'bible manager initialise complete')
@ -301,23 +321,40 @@ class BibleMediaItem(MediaManagerItem):
self.AdvancedSecondBibleComboBox.clear()
self.QuickSecondBibleComboBox.addItem(u'')
self.AdvancedSecondBibleComboBox.addItem(u'')
bibles = self.parent.biblemanager.get_bibles(BibleMode.Full)
bibles = self.parent.manager.get_bibles()
# load bibles into the combo boxes
first = True
for bible in bibles:
self.QuickVersionComboBox.addItem(bible)
self.QuickSecondBibleComboBox.addItem(bible)
# Without HTTP
bibles = self.parent.biblemanager.get_bibles(BibleMode.Partial)
first = True
# load bibles into the combo boxes
for bible in bibles:
self.AdvancedVersionComboBox.addItem(bible)
self.AdvancedSecondBibleComboBox.addItem(bible)
if first:
first = False
# use the first bible as the trigger
self.initialiseBible(bible)
def onListViewResize(self, width, height):
self.SearchProgress.setGeometry(self.ListView.geometry().x(),
(self.ListView.geometry().y() + self.ListView.geometry().height())\
- 23, 81, 23)
def onSearchProgressShow(self):
self.SearchProgress.setVisible(True)
self.SearchProgress.setMinimum(0)
self.SearchProgress.setMaximum(2)
self.SearchProgress.setValue(1)
def onSearchProgressHide(self):
self.SearchProgress.setVisible(False)
def onNoBookFound(self):
QtGui.QMessageBox.critical(self,
self.trUtf8('No Book Found'),
self.trUtf8('No matching book could be found in this Bible.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok),
QtGui.QMessageBox.Ok
)
def onAdvancedVersionComboBox(self):
self.initialiseBible(
unicode(self.AdvancedVersionComboBox.currentText()))
@ -330,11 +367,8 @@ class BibleMediaItem(MediaManagerItem):
self.AdvancedBookComboBox.itemData(item).toInt()[0])
def onNewClick(self):
#self.bibleimportform = BibleImportForm(
# self.parent.config, self.parent.biblemanager, self)
#self.bibleimportform.exec_()
self.bibleimportform = ImportWizardForm(self, self.parent.config,
self.parent.biblemanager, self.parent)
self.parent.manager, self.parent)
self.bibleimportform.exec_()
self.reloadBibles()
@ -343,14 +377,13 @@ class BibleMediaItem(MediaManagerItem):
self.adjustComboBox(frm, self.verses, self.AdvancedToVerse)
def onAdvancedToChapter(self):
text1 = unicode(self.AdvancedFromChapter.currentText())
text2 = unicode(self.AdvancedToChapter.currentText())
if text1 != text2:
frm = unicode(self.AdvancedFromChapter.currentText())
to = unicode(self.AdvancedToChapter.currentText())
if frm != to:
bible = unicode(self.AdvancedVersionComboBox.currentText())
book = unicode(self.AdvancedBookComboBox.currentText())
# get the verse count for new chapter
verses = self.parent.biblemanager.get_book_verse_count(
bible, book, int(text2))
verses = self.parent.manager.get_verse_count(bible, book, int(to))
self.adjustComboBox(1, verses, self.AdvancedToVerse)
def onAdvancedSearchButton(self):
@ -361,10 +394,13 @@ class BibleMediaItem(MediaManagerItem):
chapter_to = int(self.AdvancedToChapter.currentText())
verse_from = int(self.AdvancedFromVerse.currentText())
verse_to = int(self.AdvancedToVerse.currentText())
self.search_results = self.parent.biblemanager.get_verse_text(
bible, book, chapter_from, chapter_to, verse_from, verse_to)
versetext = u'%s %s:%s-%s:%s' % (book, chapter_from, verse_from, \
chapter_to, verse_to)
self.search_results = self.parent.manager.get_verses(bible, versetext)
if self.ClearAdvancedSearchComboBox.currentIndex() == 0:
self.ListView.clear()
self.lastReference = []
self.lastReference.append(versetext)
self.displayResults(bible)
def onAdvancedFromChapter(self):
@ -373,7 +409,7 @@ class BibleMediaItem(MediaManagerItem):
cf = int(self.AdvancedFromChapter.currentText())
self.adjustComboBox(cf, self.chapters_from, self.AdvancedToChapter)
# get the verse count for new chapter
vse = self.parent.biblemanager.get_book_verse_count(bible, book, cf)
vse = self.parent.manager.get_verse_count(bible, book, cf)
self.adjustComboBox(1, vse, self.AdvancedFromVerse)
self.adjustComboBox(1, vse, self.AdvancedToVerse)
@ -383,11 +419,9 @@ class BibleMediaItem(MediaManagerItem):
text = unicode(self.QuickSearchEdit.displayText())
if self.ClearQuickSearchComboBox.currentIndex() == 0:
self.ListView.clear()
if self.QuickSearchComboBox.currentIndex() == 1:
self.search_results = self.parent.biblemanager.get_verse_from_text(
bible, text)
else:
self.searchByReference(bible, text)
self.lastReference = []
self.lastReference.append(text)
self.search_results = self.parent.manager.get_verses(bible, text)
if self.search_results:
self.displayResults(bible)
@ -400,60 +434,64 @@ class BibleMediaItem(MediaManagerItem):
raw_slides = []
raw_footer = []
bible_text = u''
service_item.autoPreviewAllowed = True
#If we want to use a 2nd translation / version
bible2 = u''
if self.SearchTabWidget.currentIndex() == 0:
bible2 = unicode(self.QuickSecondBibleComboBox.currentText())
else:
bible2 = unicode(self.AdvancedSecondBibleComboBox.currentText())
if bible2:
bible2_verses = []
for scripture in self.lastReference:
bible2_verses.extend(self.parent.manager.get_verses(bible2, scripture))
bible2_version = self.parent.manager.get_meta_data(bible2, u'Version')
bible2_copyright = self.parent.manager.get_meta_data(bible2, u'Copyright')
bible2_permission = self.parent.manager.get_meta_data(bible2, u'Permission')
# Let's loop through the main lot, and assemble our verses
for item in items:
bitem = self.ListView.item(item.row())
text = unicode((bitem.data(QtCore.Qt.UserRole)).toString())
search_verse = text[:text.find(u'(')]
bible = text[text.find(u'(') + 1:-1]
self.searchByReference(bible, search_verse)
book = self.search_results[0].book.name
chapter = unicode(self.search_results[0].chapter)
verse = unicode(self.search_results[0].verse)
text = self.search_results[0].text
reference = bitem.data(QtCore.Qt.UserRole).toPyObject()
bible = unicode(reference[QtCore.QString('bible')])
book = unicode(reference[QtCore.QString('book')])
chapter = unicode(reference[QtCore.QString('chapter')])
verse = unicode(reference[QtCore.QString('verse')])
text = unicode(reference[QtCore.QString('text')])
version = unicode(reference[QtCore.QString('version')])
copyright = unicode(reference[QtCore.QString('copyright')])
permission = unicode(reference[QtCore.QString('permission')])
if self.parent.settings_tab.display_style == 1:
loc = self.formatVerse(old_chapter, chapter, verse, u'(u', u')')
verse_text = self.formatVerse(old_chapter, chapter, verse, u'(u', u')')
elif self.parent.settings_tab.display_style == 2:
loc = self.formatVerse(old_chapter, chapter, verse, u'{', u'}')
verse_text = self.formatVerse(old_chapter, chapter, verse, u'{', u'}')
elif self.parent.settings_tab.display_style == 3:
loc = self.formatVerse(old_chapter, chapter, verse, u'[', u']')
verse_text = self.formatVerse(old_chapter, chapter, verse, u'[', u']')
else:
loc = self.formatVerse(old_chapter, chapter, verse, u'', u'')
verse_text = self.formatVerse(old_chapter, chapter, verse, u'', u'')
old_chapter = chapter
footer = u'%s (%s %s)' % (book, self.version, self.copyright)
#If not found throws and error so add.s
try:
raw_footer.index(footer)
except:
footer = u'%s (%s %s)' % (book, version, copyright)
#If not found add to footer
if footer not in raw_footer:
raw_footer.append(footer)
#If we want to use a 2nd translation / version
bible2 = u''
if self.SearchTabWidget.currentIndex() == 0:
bible2 = unicode(self.QuickSecondBibleComboBox.currentText())
else:
bible2 = unicode(self.AdvancedSecondBibleComboBox.currentText())
if len(bible2) > 0:
self.searchByReference(bible2, search_verse)
footer = u'%s (%s %s)' % (book, self.version, self.copyright)
#If not found throws and error so add.s
try:
raw_footer.index(footer)
except:
if bible2:
footer = u'%s (%s %s)' % (book, version, copyright)
#If not found add to footer
if footer not in raw_footer:
raw_footer.append(footer)
bible_text = u'%s %s \n\n\n %s %s)' % \
(loc, text, loc, self.search_results[0].text)
bible_text = u'%s %s \n\n %s %s' % \
(verse_text, text, verse_text, bible2_verses[item.row()].text)
raw_slides.append(bible_text)
bible_text = u''
else:
#Paragraph style force new line per verse
if self.parent.settings_tab.layout_style == 1:
text = text + u'\n\n'
bible_text = u'%s %s %s' % (bible_text, loc, text)
bible_text = u'%s %s %s' % (bible_text, verse_text, text)
#if we are verse per slide then create slide
if self.parent.settings_tab.layout_style == 0:
raw_slides.append(bible_text)
bible_text = u''
service_item.title = u'%s %s' % (book, loc)
service_item.title = u'%s %s' % (book, verse_text)
if len(self.parent.settings_tab.bible_theme) == 0:
service_item.theme = None
else:
@ -467,40 +505,39 @@ class BibleMediaItem(MediaManagerItem):
return True
def formatVerse(self, old_chapter, chapter, verse, opening, closing):
loc = opening
verse_text = opening
if old_chapter != chapter:
loc += chapter + u':'
verse_text += chapter + u':'
elif not self.parent.settings_tab.show_new_chapters:
loc += chapter + u':'
loc += verse
loc += closing
return loc
verse_text += chapter + u':'
verse_text += verse
verse_text += closing
return verse_text
def reloadBibles(self):
log.debug(u'Reloading Bibles')
self.parent.biblemanager.reload_bibles()
self.parent.manager.reload_bibles()
self.loadBibles()
def initialiseBible(self, bible):
log.debug(u'initialiseBible %s', bible)
book_data = self.parent.biblemanager.get_bible_books()
book_data = self.parent.manager.get_books(bible)
self.AdvancedBookComboBox.clear()
first = True
for book in book_data:
row = self.AdvancedBookComboBox.count()
self.AdvancedBookComboBox.addItem(book[u'book'])
self.AdvancedBookComboBox.addItem(book[u'name'])
self.AdvancedBookComboBox.setItemData(
row, QtCore.QVariant(book[u'total']))
row, QtCore.QVariant(book[u'chapters']))
if first:
first = False
self.initialiseChapterVerse(
bible, book[u'book'], book[u'total'])
bible, book[u'name'], book[u'chapters'])
def initialiseChapterVerse(self, bible, book, chapters):
log.debug(u'initialiseChapterVerse %s, %s', bible, book)
self.chapters_from = chapters
self.verses = self.parent.biblemanager.get_book_verse_count(bible,
book, 1)
self.verses = self.parent.manager.get_verse_count(bible, book, 1)
if self.verses == 0:
self.AdvancedSearchButton.setEnabled(False)
self.AdvancedMessage.setText(self.trUtf8('Bible not fully loaded'))
@ -519,12 +556,30 @@ class BibleMediaItem(MediaManagerItem):
combo.addItem(unicode(i))
def displayResults(self, bible):
version = self.parent.manager.get_meta_data(bible, u'Version')
copyright = self.parent.manager.get_meta_data(bible, u'Copyright')
permission = self.parent.manager.get_meta_data(bible, u'Permission')
if not permission:
permission = u''
else:
permission = permission.value
for count, verse in enumerate(self.search_results):
bible_text = u' %s %d:%d (%s)' % (verse.book.name,
verse.chapter, verse.verse, bible)
bible_text = u' %s %d:%d (%s)' % \
(verse.book.name, verse.chapter, verse.verse, bible)
bible_verse = QtGui.QListWidgetItem(bible_text)
bible_verse.setData(QtCore.Qt.UserRole,
QtCore.QVariant(bible_text))
#bible_verse.setData(QtCore.Qt.UserRole,
# QtCore.QVariant(bible_text))
vdict = {
'bible': QtCore.QVariant(bible),
'version': QtCore.QVariant(version.value),
'copyright': QtCore.QVariant(copyright.value),
'permission': QtCore.QVariant(permission),
'book': QtCore.QVariant(verse.book.name),
'chapter': QtCore.QVariant(verse.chapter),
'verse': QtCore.QVariant(verse.verse),
'text': QtCore.QVariant(verse.text)
}
bible_verse.setData(QtCore.Qt.UserRole, QtCore.QVariant(vdict))
self.ListView.addItem(bible_verse)
row = self.ListView.setCurrentRow(count)
if row:
@ -532,85 +587,4 @@ class BibleMediaItem(MediaManagerItem):
def searchByReference(self, bible, search):
log.debug(u'searchByReference %s, %s', bible, search)
book = u''
start_chapter = u''
end_chapter = u''
start_verse = u''
end_verse = u''
search = search.replace(u' ', u' ').strip()
#original = search
message = None
# Remove book beware 0 index arrays
for i in range (len(search)-1, 0, - 1):
if search[i] == u' ':
book = search[:i]
# remove book from string
search = search[i:]
break
# allow V or v for verse instead of :
search = search.replace(u'v', ':')
search = search.replace(u'V', ':')
search = search.strip()
colon = search.find(u':')
if colon == -1:
# number : found
i = search.rfind(u' ')
if i == -1:
chapter = u''
else:
chapter = search[i:len(search)]
hyphen = chapter.find(u'-')
if hyphen != -1:
start_chapter= chapter[:hyphen]
end_chapter= chapter[hyphen + 1:len(chapter)]
else:
start_chapter = chapter
else:
# more complex
sp = search.split(u'-') #find first
sp1 = sp[0].split(u':')
if len(sp1) == 1:
start_chapter = sp1[0]
start_verse = 1
else:
start_chapter = sp1[0]
start_verse = sp1[1]
if len(sp)== 1:
end_chapter = start_chapter
end_verse = start_verse
else:
sp1 = sp[1].split(u':')
if len(sp1) == 1:
end_chapter = start_chapter
end_verse = sp1[0]
else:
end_chapter = sp1[0]
end_verse = sp1[1]
if end_chapter == u'':
end_chapter = start_chapter.rstrip()
if start_verse == u'':
if end_verse == u'':
start_verse = 1
else:
start_verse = end_verse
if end_verse == u'':
end_verse = 99
if start_chapter == u'':
message = self.trUtf8('No chapter found for search criteria')
log.debug(u'results = %s @ %s : %s @ %s : %s'% \
(unicode(book), unicode(start_chapter), unicode(end_chapter),
unicode(start_verse), unicode(end_verse)))
if message is None:
self.search_results = None
self.search_results = self.parent.biblemanager.get_verse_text(
bible, book, int(start_chapter), int(end_chapter),
int(start_verse), int(end_verse))
self.copyright = unicode(self.parent.biblemanager.get_meta_data(
bible, u'Copyright').value)
self.permissions = unicode(self.parent.biblemanager.get_meta_data(
bible, u'Permissions').value)
self.version = unicode(self.parent.biblemanager.get_meta_data(
bible, u'Version').value)
else:
QtGui.QMessageBox.information(
self, self.trUtf8('Information'), message)
self.search_results = self.parent.manager.get_verses(bible, search)

View File

@ -50,7 +50,7 @@ class BibleMeta(BaseModel):
pass
class ONTestament(BaseModel):
class Testament(BaseModel):
"""
Bible Testaments
"""
@ -101,8 +101,8 @@ verse_table = Table(u'verse', metadata,
Column(u'text', types.UnicodeText, index=True),
)
mapper(BibleMeta, meta_table)
mapper(ONTestament, testament_table,
mapper(Testament, testament_table,
properties={'books': relation(Book, backref='testament')})
mapper(Book, book_table,
properties={'verses': relation(Verse, backref='book')})
mapper(Verse, verse_table)
mapper(Verse, verse_table)

View File

@ -23,39 +23,31 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import os
import os.path
import logging
import chardet
import codecs
from lxml import objectify
from lxml import objectify
from PyQt4 import QtCore
from openlp.core.lib import Receiver
from db import BibleDB
class BibleOpenSongImpl():
log = logging.getLogger(__name__)
class OpenSongBible(BibleDB):
"""
OSIS Bible format importer class.
OpenSong Bible format importer class.
"""
global log
log = logging.getLogger(__name__)
log.info(u'BibleOpenSongImpl loaded')
def __init__(self, biblepath, bibledb):
def __init__(self, parent, **kwargs):
"""
Constructor to create and set up an instance of the
BibleOpenSongImpl class.
``biblepath``
This does not seem to be used.
``bibledb``
A reference to a Bible database object.
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.info(u'BibleOpenSongImpl Initialising')
self.bibledb = bibledb
self.loadbible = True
log.debug(__name__)
BibleDB.__init__(self, parent, **kwargs)
if 'filename' not in kwargs:
raise KeyError(u'You have to supply a file name to import from.')
self.filename = kwargs['filename']
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlpstopimport'), self.stop_import)
@ -63,64 +55,55 @@ class BibleOpenSongImpl():
"""
Stops the import of the Bible.
"""
self.loadbible = False
log.debug('Stopping import!')
self.stop_import_flag = True
def load_data(self, bible_file, dialogobject=None):
def do_import(self):
"""
Loads a Bible from file.
``bible_file``
The file to import from.
``dialogobject``
The Import dialog, so that we can increase the counter on
the progress bar.
"""
log.info(u'Load data for %s' % bible_file)
bible_file = unicode(bible_file)
detect_file = None
try:
detect_file = open(bible_file, u'r')
details = chardet.detect(detect_file.read(2048))
except:
log.exception(u'Failed to detect OpenSong file encoding')
return
finally:
if detect_file:
detect_file.close()
opensong_bible = None
log.debug(u'Starting OpenSong import from "%s"' % self.filename)
self.filename = unicode(self.filename, u'utf-8')
self.wizard.incrementProgressBar(u'Preparing for import...')
file = None
success = True
try:
opensong_bible = codecs.open(bible_file, u'r', details['encoding'])
opensong = objectify.parse(opensong_bible)
# 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()
for book in bible.b:
if not self.loadbible:
if self.stop_import_flag:
break
dbbook = self.bibledb.create_book(book.attrib[u'n'],
book.attrib[u'n'][:4])
db_book = self.create_book(unicode(book.attrib[u'n']),
unicode(book.attrib[u'n'][:4]))
for chapter in book.c:
if not self.loadbible:
if self.stop_import_flag:
break
for verse in chapter.v:
if not self.loadbible:
if self.stop_import_flag:
break
self.bibledb.add_verse(dbbook.id, chapter.attrib[u'n'],
verse.attrib[u'n'], verse.text)
self.create_verse(
db_book.id,
int(chapter.attrib[u'n']),
int(verse.attrib[u'n']),
unicode(verse.text)
)
Receiver.send_message(u'process_events')
dialogobject.incrementProgressBar(u'Importing %s %s' % \
(dbbook.name, str(chapter.attrib[u'n'])))
self.bibledb.save_verses()
self.wizard.incrementProgressBar(
QtCore.QString('%s %s %s' % (self.trUtf8('Importing'),\
db_book.name, chapter.attrib[u'n'])))
self.commit()
except:
log.exception(u'Loading bible from OpenSong file failed')
success = False
finally:
if opensong_bible:
opensong_bible.close()
if not self.loadbible:
dialogobject.incrementProgressBar(u'Import canceled!')
dialogobject.ImportProgressBar.setValue(
dialogobject.ImportProgressBar.maximum())
if file:
file.close()
if self.stop_import:
self.wizard.incrementProgressBar(u'Import canceled!')
return False
else:
return success

View File

@ -33,27 +33,26 @@ import re
from PyQt4 import QtCore
from openlp.core.lib import Receiver
from db import BibleDB
class BibleOSISImpl():
log = logging.getLogger(__name__)
class OSISBible(BibleDB):
"""
OSIS Bible format importer class.
"""
global log
log = logging.getLogger(u'BibleOSISImpl')
log.info(u'BibleOSISImpl loaded')
def __init__(self, biblepath, bibledb):
def __init__(self, parent, **kwargs):
"""
Constructor to create and set up an instance of the
BibleOSISImpl class.
``biblepath``
This does not seem to be used.
``bibledb``
A reference to a Bible database object.
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.info(u'BibleOSISImpl Initialising')
log.debug(__name__)
BibleDB.__init__(self, parent, **kwargs)
if u'filename' not in kwargs:
raise KeyError(u'You have to supply a file name to import from.')
self.filename = kwargs[u'filename']
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>')
@ -66,13 +65,11 @@ class BibleOSISImpl():
self.w_regex = re.compile(r'<w (.*?)>')
self.q_regex = re.compile(r'<q (.*?)>')
self.spaces_regex = re.compile(r'([ ]{2,})')
self.bibledb = bibledb
self.books = {}
filepath = os.path.split(os.path.abspath(__file__))[0]
filepath = os.path.abspath(os.path.join(
filepath, u'..', u'resources', u'osisbooks.csv'))
fbibles = None
self.loadbible = True
try:
fbibles = open(filepath, u'r')
for line in fbibles:
@ -92,24 +89,18 @@ class BibleOSISImpl():
Stops the import of the Bible.
"""
log.debug('Stopping import!')
self.loadbible = False
self.stop_import_flag = True
def load_data(self, osisfile_record, dialogobject=None):
def do_import(self):
"""
Loads a Bible from file.
``osisfile_record``
The file to import from.
``dialogobject``
The Import dialog, so that we can increase the counter on
the progress bar.
"""
log.info(u'Load data for %s' % osisfile_record)
log.debug(u'Starting OSIS import from "%s"' % self.filename)
self.wizard.incrementProgressBar(u'Detecting encoding (this may take a few minutes)...')
detect_file = None
try:
detect_file = open(osisfile_record, u'r')
details = chardet.detect(detect_file.read(3000))
detect_file = open(self.filename, u'r')
details = chardet.detect(detect_file.read())
except:
log.exception(u'Failed to detect OSIS file encoding')
return
@ -119,12 +110,12 @@ class BibleOSISImpl():
osis = None
success = True
try:
osis = codecs.open(osisfile_record, u'r', details['encoding'])
osis = codecs.open(self.filename, u'r', details['encoding'])
last_chapter = 0
testament = 1
db_book = None
for file_record in osis:
if not self.loadbible:
if self.stop_import_flag:
break
match = self.verse_regex.search(file_record)
if match:
@ -136,19 +127,19 @@ class BibleOSISImpl():
log.debug('New book: "%s"', self.books[book][0])
if book == u'Matt':
testament += 1
db_book = self.bibledb.create_book(
db_book = self.create_book(
unicode(self.books[book][0]),
unicode(self.books[book][1]),
testament)
if last_chapter == 0:
if book == u'Gen':
dialogobject.ImportProgressBar.setMaximum(1188)
self.wizard.ImportProgressBar.setMaximum(1188)
else:
dialogobject.ImportProgressBar.setMaximum(260)
self.wizard.ImportProgressBar.setMaximum(260)
if last_chapter != chapter:
if last_chapter != 0:
self.bibledb.save_verses()
dialogobject.incrementProgressBar(
self.commit()
self.wizard.incrementProgressBar(
u'Importing %s %s...' % \
(self.books[match.group(1)][0], chapter))
last_chapter = chapter
@ -170,20 +161,18 @@ class BibleOSISImpl():
.replace(u'</lg>', u'').replace(u'</q>', u'')\
.replace(u'</div>', u'')
verse_text = self.spaces_regex.sub(u' ', verse_text)
self.bibledb.add_verse(db_book.id, chapter, verse, verse_text)
self.create_verse(db_book.id, chapter, verse, verse_text)
Receiver.send_message(u'process_events')
self.bibledb.save_verses()
dialogobject.incrementProgressBar(u'Finishing import...')
self.commit()
self.wizard.incrementProgressBar(u'Finishing import...')
except:
log.exception(u'Loading bible from OSIS file failed')
success = False
finally:
if osis:
osis.close()
if not self.loadbible:
dialogobject.incrementProgressBar(u'Import canceled!')
dialogobject.ImportProgressBar.setValue(
dialogobject.ImportProgressBar.maximum())
if self.stop_import_flag:
self.wizard.incrementProgressBar(u'Import canceled!')
return False
else:
return success
return success

View File

@ -1,66 +0,0 @@
Genesis,Gen,1,50
Exodus,Exod,1,40
Leviticus,Lev,1,27
Numbers,Num,1,36
Deuteronomy,Deut,1,34
Joshua,Josh,1,24
Judges,Judg,1,21
Ruth,Ruth,1,4
1 Samual,1Sam,1,31
2 Samual,2Sam,1,24
1 Kings,1Kgs,1,22
2 Kings,2Kgs,1,25
1 Chronicles,1Chr,1,29
2 Chronicles,2Chr,1,36
Ezra,Esra,1,10
Nehemiah,Neh,1,13
Esther,Esth,1,10
Job,Job,1,42
Psalms,Ps,1,150
Proverbs,Prov,1,31
Ecclesiastes,Eccl,1,12
Song of Songs,Song,1,8
Isaiah,Isa,1,66
Jeremiah,Jer,1,5
Lamentations,Lam,1,5
Ezekiel,Ezek,1,48
Daniel,Dan,1,12
Hosea,Hos,1,14
Joel,Joel,1,3
Amos,Amos,1,9
Obad,Obad,1,1
Jonah,Jonah,1,4
Micah,Mic,1,7
Naham,Nah,1,3
Habakkuk,Hab,1,3
Zephaniah,Zeph,1,3
Haggai,Hag,1,2
Zechariah,Zech,1,3
Malachi,Mal,1,4
Matthew,Matt,2,28
Mark,Mark,2,16
Luke,Luke,2,24
John,John,2,21
Acts,Acts,2,28
Romans,Rom,2,16
1 Corinthans,1Cor,2,16
2 Corinthans,2Cor,2,13
Galatians,Gal,2,6
Ephesians,Eph,2,6
Philippians,Phil,2,4
Colossians,Col,2,4
1 Thessalonians,1Thess,2,5
2 Thessalonians,2Thess,2,3
1 Timothy,1Tim,2,6
2 Timothy,2Tim,2,4
Titus,Titus,2,3
Philemon,Phlm,2,1
Hebrews,Heb,2,13
James,Jas,2,5
1 Peter,1Pet,2,5
2 Peter,2Pet,2,3
1 John,1John,2,5
2 John,2John,2,1
3 John,3John,2,1
Jude,Jude,2,1
Revelation,Rev,2,22
1 Genesis Gen 1 50
2 Exodus Exod 1 40
3 Leviticus Lev 1 27
4 Numbers Num 1 36
5 Deuteronomy Deut 1 34
6 Joshua Josh 1 24
7 Judges Judg 1 21
8 Ruth Ruth 1 4
9 1 Samual 1Sam 1 31
10 2 Samual 2Sam 1 24
11 1 Kings 1Kgs 1 22
12 2 Kings 2Kgs 1 25
13 1 Chronicles 1Chr 1 29
14 2 Chronicles 2Chr 1 36
15 Ezra Esra 1 10
16 Nehemiah Neh 1 13
17 Esther Esth 1 10
18 Job Job 1 42
19 Psalms Ps 1 150
20 Proverbs Prov 1 31
21 Ecclesiastes Eccl 1 12
22 Song of Songs Song 1 8
23 Isaiah Isa 1 66
24 Jeremiah Jer 1 5
25 Lamentations Lam 1 5
26 Ezekiel Ezek 1 48
27 Daniel Dan 1 12
28 Hosea Hos 1 14
29 Joel Joel 1 3
30 Amos Amos 1 9
31 Obad Obad 1 1
32 Jonah Jonah 1 4
33 Micah Mic 1 7
34 Naham Nah 1 3
35 Habakkuk Hab 1 3
36 Zephaniah Zeph 1 3
37 Haggai Hag 1 2
38 Zechariah Zech 1 3
39 Malachi Mal 1 4
40 Matthew Matt 2 28
41 Mark Mark 2 16
42 Luke Luke 2 24
43 John John 2 21
44 Acts Acts 2 28
45 Romans Rom 2 16
46 1 Corinthans 1Cor 2 16
47 2 Corinthans 2Cor 2 13
48 Galatians Gal 2 6
49 Ephesians Eph 2 6
50 Philippians Phil 2 4
51 Colossians Col 2 4
52 1 Thessalonians 1Thess 2 5
53 2 Thessalonians 2Thess 2 3
54 1 Timothy 1Tim 2 6
55 2 Timothy 2Tim 2 4
56 Titus Titus 2 3
57 Philemon Phlm 2 1
58 Hebrews Heb 2 13
59 James Jas 2 5
60 1 Peter 1Pet 2 5
61 2 Peter 2Pet 2 3
62 1 John 1John 2 5
63 2 John 2John 2 1
64 3 John 3John 2 1
65 Jude Jude 2 1
66 Revelation Rev 2 22

Binary file not shown.

View File

@ -26,9 +26,10 @@
import logging
from forms import EditCustomForm
from openlp.core.lib import Plugin, build_icon
from openlp.core.lib import Plugin, build_icon, PluginStatus
from openlp.plugins.custom.lib import CustomManager, CustomMediaItem, CustomTab
log = logging.getLogger(__name__)
class CustomPlugin(Plugin):
"""
@ -39,17 +40,15 @@ class CustomPlugin(Plugin):
the songs plugin has become restrictive. Examples could be
Welcome slides, Bible Reading information, Orders of service.
"""
global log
log = logging.getLogger(u'CustomPlugin')
log.info(u'Custom Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Custom', u'1.9.0', plugin_helpers)
Plugin.__init__(self, u'Custom', u'1.9.1', plugin_helpers)
self.weight = -5
self.custommanager = CustomManager(self.config)
self.edit_custom_form = EditCustomForm(self.custommanager)
self.icon = build_icon(u':/media/media_custom.png')
self.status = PluginStatus.Active
def get_settings_tab(self):
return CustomTab(self.name)
@ -72,4 +71,9 @@ class CustomPlugin(Plugin):
'allows slides to be displayed on the screen in the same way '
'songs are. This plugin provides greater freedom over the '
'songs plugin.<br>')
return about_text
return about_text
def can_delete_theme(self, theme):
if len(self.custommanager.get_customs_for_theme(theme)) == 0:
return True
return False

View File

@ -30,12 +30,12 @@ from editcustomdialog import Ui_customEditDialog
from openlp.core.lib import SongXMLBuilder, SongXMLParser, Receiver
from openlp.plugins.custom.lib.models import CustomSlide
log = logging.getLogger(__name__)
class EditCustomForm(QtGui.QDialog, Ui_customEditDialog):
"""
Class documentation goes here.
"""
global log
log = logging.getLogger(u'EditCustomForm')
log.info(u'Custom Editor loaded')
def __init__(self, custommanager, parent = None):
"""
@ -153,10 +153,10 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog):
sxml.add_verse_to_lyrics(u'custom', unicode(count),
unicode(self.VerseListView.item(i).text()))
count += 1
self.customSlide.title = unicode(self.TitleEdit.displayText())
self.customSlide.text = unicode(sxml.extract_xml())
self.customSlide.credits = unicode(self.CreditEdit.displayText())
self.customSlide.theme_name = unicode(self.ThemeComboBox.currentText())
self.customSlide.title = unicode(self.TitleEdit.displayText(), u'utf-8')
self.customSlide.text = unicode(sxml.extract_xml(), u'utf-8')
self.customSlide.credits = unicode(self.CreditEdit.displayText(), u'utf-8')
self.customSlide.theme_name = unicode(self.ThemeComboBox.currentText(), u'utf-8')
self.custommanager.save_slide(self.customSlide)
return True
@ -254,7 +254,7 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog):
if self.VerseListView.count() == 0:
self.VerseTextEdit.setFocus()
return False, self.trUtf8('You need to enter a slide')
if len(self.VerseTextEdit.toPlainText()) > 0:
if self.VerseTextEdit.toPlainText():
self.VerseTextEdit.setFocus()
return False, self.trUtf8('You have unsaved data')
return True, u''
return True, u''

View File

@ -27,14 +27,13 @@ import logging
from openlp.plugins.custom.lib.models import init_models, metadata, CustomSlide
log = logging.getLogger(__name__)
class CustomManager():
"""
The Song Manager provides a central location for all database code. This
class takes care of connecting to the database and running all the queries.
"""
global log
log = logging.getLogger(u'CustomManager')
log.info(u'Custom manager loaded')
def __init__(self, config):
@ -78,7 +77,7 @@ class CustomManager():
return True
except:
self.session.rollback()
log.excertion(u'Custom Slide save failed')
log.exception(u'Custom Slide save failed')
return False
def get_custom(self, id=None):
@ -94,7 +93,7 @@ class CustomManager():
"""
Delete a Custom slide show
"""
if id !=0:
if id != 0:
customslide = self.get_custom(id)
try:
self.session.delete(customslide)
@ -102,7 +101,10 @@ class CustomManager():
return True
except:
self.session.rollback()
log.excertion(u'Custom Slide deleton failed')
log.exception(u'Custom Slide deleton failed')
return False
else:
return True
return True
def get_customs_for_theme(self, theme):
return self.session.query(CustomSlide).filter(CustomSlide.theme_name == theme).all()

View File

@ -30,6 +30,8 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, SongXMLParser, BaseListWithDnD,\
Receiver, str_to_bool
log = logging.getLogger(__name__)
class CustomListView(BaseListWithDnD):
def __init__(self, parent=None):
self.PluginName = u'Custom'
@ -39,8 +41,6 @@ class CustomMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Custom Slides.
"""
global log
log = logging.getLogger(u'CustomMediaItem')
log.info(u'Custom Media Item loaded')
def __init__(self, parent, icon, title):
@ -144,13 +144,14 @@ class CustomMediaItem(MediaManagerItem):
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
else:
item_id = self.remoteCustom
service_item.autoPreviewAllowed = True
customSlide = self.parent.custommanager.get_custom(item_id)
title = customSlide.title
credit = customSlide.credits
service_item.edit_enabled = True
service_item.editId = item_id
theme = customSlide.theme_name
if len(theme) is not 0 :
if theme:
service_item.theme = theme
songXML = SongXMLParser(customSlide.text)
verseList = songXML.get_verses()
@ -159,10 +160,10 @@ class CustomMediaItem(MediaManagerItem):
service_item.title = title
for slide in raw_slides:
service_item.add_from_text(slide[:30], slide)
if str_to_bool(self.parent.config.get_config(u'display footer', True)) or \
len(credit) > 0:
raw_footer.append(title + u' '+ credit)
if str_to_bool(self.parent.config.get_config(u'display footer', True)) \
or credit:
raw_footer.append(title + u' ' + credit)
else:
raw_footer.append(u'')
service_item.raw_footer = raw_footer
return True
return True

View File

@ -27,11 +27,11 @@ from sqlalchemy import Column, Table, types
from openlp.plugins.custom.lib.meta import metadata
# Definition of the "songs" table
# Definition of the "custom slide" table
custom_slide_table = Table(u'custom_slide', metadata,
Column(u'id', types.Integer(), primary_key=True),
Column(u'title', types.Unicode(255), nullable=False),
Column(u'text', types.UnicodeText, nullable=False),
Column(u'credits', types.UnicodeText),
Column(u'theme_name', types.Unicode(128))
)
)

View File

@ -25,18 +25,19 @@
import logging
from openlp.core.lib import Plugin, build_icon
from openlp.core.lib import Plugin, build_icon, PluginStatus
from openlp.plugins.images.lib import ImageMediaItem, ImageTab
log = logging.getLogger(__name__)
class ImagePlugin(Plugin):
global log
log = logging.getLogger(u'ImagePlugin')
log.info(u'Image Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Images', u'1.9.0', plugin_helpers)
Plugin.__init__(self, u'Images', u'1.9.1', plugin_helpers)
self.weight = -7
self.icon = build_icon(u':/media/media_image.png')
self.status = PluginStatus.Active
def initialise(self):
log.info(u'Plugin Initialising')
@ -60,6 +61,6 @@ class ImagePlugin(Plugin):
'together and presented on the live controller it is possible '
'to turn them into a timed loop.<br<br>From the plugin if the '
'<i>Override background</i> is chosen and an image is selected '
'any somgs which are rendered will use the selected image from '
'any songs which are rendered will use the selected image from '
'the background instead of the one provied by the theme.<br>')
return about_text
return about_text

View File

@ -49,6 +49,7 @@ class ImageTab(SettingsTab):
self.TimeoutLabel.setObjectName(u'TimeoutLabel')
self.TimeoutLayout.addWidget(self.TimeoutLabel)
self.TimeoutSpinBox = QtGui.QSpinBox(self.ImageSettingsGroupBox)
self.TimeoutSpinBox.setMinimum(1)
self.TimeoutSpinBox.setMaximum(180)
self.TimeoutSpinBox.setObjectName(u'TimeoutSpinBox')
self.TimeoutLayout.addWidget(self.TimeoutSpinBox)
@ -78,4 +79,4 @@ class ImageTab(SettingsTab):
Receiver.send_message(u'update_spin_delay', self.loop_delay)
def postSetUp(self):
Receiver.send_message(u'update_spin_delay', self.loop_delay)
Receiver.send_message(u'update_spin_delay', self.loop_delay)

View File

@ -29,6 +29,8 @@ import os
from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon
log = logging.getLogger(__name__)
# We have to explicitly create separate classes for each plugin
# in order for DnD to the Service manager to work correctly.
class ImageListView(BaseListWithDnD):
@ -40,8 +42,6 @@ class ImageMediaItem(MediaManagerItem):
"""
This is the custom media manager item for images.
"""
global log
log = logging.getLogger(u'ImageMediaItem')
log.info(u'Image Media Item loaded')
def __init__(self, parent, icon, title):
@ -61,7 +61,7 @@ class ImageMediaItem(MediaManagerItem):
def retranslateUi(self):
self.OnNewPrompt = self.trUtf8('Select Image(s)')
self.OnNewFileMasks = \
self.trUtf8('Images (*.jpg *jpeg *.gif *.png *.bmp)')
self.trUtf8('Images (*.jpg *jpeg *.gif *.png *.bmp);; All files (*)')
def requiredIcons(self):
MediaManagerItem.requiredIcons(self)
@ -144,6 +144,7 @@ class ImageMediaItem(MediaManagerItem):
items = self.ListView.selectedIndexes()
if items:
service_item.title = self.trUtf8('Image(s)')
service_item.autoPreviewAllowed = True
for item in items:
bitem = self.ListView.item(item.row())
filename = unicode((bitem.data(QtCore.Qt.UserRole)).toString())
@ -172,7 +173,6 @@ class ImageMediaItem(MediaManagerItem):
filename = unicode((bitem.data(QtCore.Qt.UserRole)).toString())
self.OverrideLabel.setText(bitem.text())
frame = QtGui.QImage(unicode(filename))
self.parent.render_manager.override_background = frame
self.parent.render_manager.override_background_changed = True
self.parent.maindisplay.addImageWithText(frame)
else:
MediaManagerItem.onPreviewClick(self)
MediaManagerItem.onPreviewClick(self)

View File

@ -30,6 +30,8 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, build_icon
log = logging.getLogger(__name__)
class MediaListView(BaseListWithDnD):
def __init__(self, parent=None):
self.PluginName = u'Media'
@ -39,9 +41,7 @@ class MediaMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Media Slides.
"""
global log
log = logging.getLogger(u'MediaMediaItem')
log.info(u'Media Media Item loaded')
log.info(u'%s MediaMediaItem loaded', __name__)
def __init__(self, parent, icon, title):
self.PluginNameShort = u'Media'
@ -54,15 +54,16 @@ class MediaMediaItem(MediaManagerItem):
self.PreviewFunction = self.video_get_preview
MediaManagerItem.__init__(self, parent, icon, title)
self.ServiceItemIconName = u':/media/media_video.png'
self.MainDisplay = self.parent.live_controller.parent.mainDisplay
self.MainDisplay = self.parent.maindisplay
def initPluginNameVisible(self):
self.PluginNameVisible = self.trUtf8('Media')
def retranslateUi(self):
self.OnNewPrompt = self.trUtf8('Select Media')
self.OnNewFileMasks = self.trUtf8('Videos (*.avi *.mpeg *.mpg'
'*.mp4);;Audio (*.ogg *.mp3 *.wma);;All files (*)')
self.OnNewFileMasks = self.trUtf8('Videos (%s);;'
'Audio (%s);;'
'All files (*)' % (self.parent.video_list, self.parent.audio_list))
def requiredIcons(self):
MediaManagerItem.requiredIcons(self)
@ -84,7 +85,7 @@ class MediaMediaItem(MediaManagerItem):
for item in items:
bitem = self.ListView.item(item.row())
filename = unicode((bitem.data(QtCore.Qt.UserRole)).toString())
frame = u':/media/media_video.png'
frame = u':/media/image_clapperboard.png'
(path, name) = os.path.split(filename)
service_item.add_from_command(path, name, frame)
return True
@ -110,4 +111,4 @@ class MediaMediaItem(MediaManagerItem):
img = self.video_get_preview()
item_name.setIcon(build_icon(img))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
self.ListView.addItem(item_name)
self.ListView.addItem(item_name)

View File

@ -25,20 +25,41 @@
import logging
from openlp.core.lib import Plugin, build_icon
from openlp.core.lib import Plugin, build_icon, PluginStatus
from openlp.plugins.media.lib import MediaMediaItem
from PyQt4.phonon import Phonon
log = logging.getLogger(__name__)
class MediaPlugin(Plugin):
global log
log = logging.getLogger(u'MediaPlugin')
log.info(u'Media Plugin loaded')
log.info(u'%s MediaPlugin loaded', __name__)
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Media', u'1.9.0', plugin_helpers)
Plugin.__init__(self, u'Media', u'1.9.1', plugin_helpers)
self.weight = -6
self.icon = build_icon(u':/media/media_video.png')
# passed with drag and drop messages
self.dnd_id = u'Media'
self.status = PluginStatus.Active
self.audio_list = u''
self.video_list = u''
for mimetype in Phonon.BackendCapabilities.availableMimeTypes():
mimetype = unicode(mimetype)
type = mimetype.split(u'audio/x-')
self.audio_list, mimetype = self._add_to_list(self.audio_list, type, mimetype)
type = mimetype.split(u'audio/')
self.audio_list, mimetype = self._add_to_list(self.audio_list, type, mimetype)
type = mimetype.split(u'video/x-')
self.video_list, mimetype = self._add_to_list(self.video_list, type, mimetype)
type = mimetype.split(u'video/')
self.video_list, mimetype = self._add_to_list(self.video_list, type, mimetype)
def _add_to_list(self, list, value, type):
if len(value) == 2:
if list.find(value[1]) == -1:
list += u'*.%s ' % value[1]
type = u''
return list, type
def initialise(self):
log.info(u'Plugin Initialising')
@ -56,4 +77,4 @@ class MediaPlugin(Plugin):
def about(self):
about_text = self.trUtf8('<b>Media Plugin</b><br>This plugin '
'allows the playing of audio and video media')
return about_text
return about_text

View File

@ -25,6 +25,7 @@
# OOo API documentation:
# http://api.openoffice.org/docs/common/ref/com/sun/star/presentation/XSlideShowController.html
# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic/Getting_Information_about_UNO_Objects#Inspecting_interfaces_during_debugging
# http://docs.go-oo.org/sd/html/classsd_1_1SlideShow.html
# http://www.oooforum.org/forum/viewtopic.phtml?t=5252
# http://wiki.services.openoffice.org/wiki/Documentation/DevGuide/Working_with_Presentations
@ -44,7 +45,9 @@ else:
from PyQt4 import QtCore
from presentationcontroller import PresentationController
from presentationcontroller import PresentationController, PresentationDocument
log = logging.getLogger(__name__)
class ImpressController(PresentationController):
"""
@ -52,9 +55,7 @@ class ImpressController(PresentationController):
It creates the runtime environment, loads and closes the presentation as
well as triggering the correct activities based on the users input
"""
global log
log = logging.getLogger(u'ImpressController')
log.info(u'loaded')
log.info(u'ImpressController loaded')
def __init__(self, plugin):
"""
@ -62,10 +63,10 @@ class ImpressController(PresentationController):
"""
log.debug(u'Initialising')
PresentationController.__init__(self, plugin, u'Impress')
self.supports = [u'.odp']
self.alsosupports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx']
self.process = None
self.document = None
self.presentation = None
self.controller = None
self.desktop = None
def check_available(self):
"""
@ -85,7 +86,7 @@ class ImpressController(PresentationController):
It is not displayed to the user but is available to the UNO interface
when required.
"""
log.debug(u'start Openoffice')
log.debug(u'start process Openoffice')
if os.name == u'nt':
self.manager = self.get_com_servicemanager()
self.manager._FlagAsMethod(u'Bridge_GetStruct')
@ -97,20 +98,88 @@ class ImpressController(PresentationController):
self.process.startDetached(cmd)
self.process.waitForStarted()
def get_uno_desktop(self):
log.debug(u'get UNO Desktop Openoffice')
ctx = None
loop = 0
log.debug(u'get UNO Desktop Openoffice - getComponentContext')
context = uno.getComponentContext()
log.debug(u'get UNO Desktop Openoffice - createInstaneWithContext - UnoUrlResolver')
resolver = context.ServiceManager.createInstanceWithContext(
u'com.sun.star.bridge.UnoUrlResolver', context)
while ctx is None and loop < 3:
try:
log.debug(u'get UNO Desktop Openoffice - resolve')
ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext')
except:
log.exception(u'Unable to find running instance ')
self.start_process()
loop += 1
try:
self.manager = ctx.ServiceManager
log.debug(u'get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
desktop = self.manager.createInstanceWithContext(
"com.sun.star.frame.Desktop", ctx )
return desktop
except:
log.exception(u'Failed to get UNO desktop')
return None
def get_com_desktop(self):
log.debug(u'get COM Desktop OpenOffice')
try:
desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop')
return desktop
except:
log.exception(u'Failed to get COM desktop')
return None
def get_com_servicemanager(self):
log.debug(u'get_com_servicemanager openoffice')
try:
return Dispatch(u'com.sun.star.ServiceManager')
except:
log.exception(u'Failed to get COM service manager')
return None
def kill(self):
"""
Called at system exit to clean up any running presentations
"""
log.debug(u'Kill')
self.close_presentation()
log.debug(u'Kill OpenOffice')
for doc in self.docs:
doc.close_presentation()
if os.name != u'nt':
desktop = self.get_uno_desktop()
else:
desktop = self.get_com_desktop()
docs = desktop.getComponents()
if docs.hasElements():
log.debug(u'OpenOffice not terminated')
else:
try:
desktop.terminate()
log.debug(u'OpenOffice killed')
except:
pass
log.exception(u'Failed to terminate OpenOffice')
def load_presentation(self, presentation):
def add_doc(self, name):
log.debug(u'Add Doc OpenOffice')
doc = ImpressDocument(self, name)
self.docs.append(doc)
return doc
class ImpressDocument(PresentationDocument):
def __init__(self, controller, presentation):
log.debug(u'Init Presentation OpenOffice')
self.controller = controller
self.document = None
self.presentation = None
self.control = None
self.store_filename(presentation)
def load_presentation(self):
"""
Called when a presentation is added to the SlideController.
It builds the environment, starts communcations with the background
@ -121,20 +190,21 @@ class ImpressController(PresentationController):
``presentation``
The file name of the presentatios to the run.
"""
log.debug(u'LoadPresentation')
self.store_filename(presentation)
log.debug(u'Load Presentation OpenOffice')
#print "s.dsk1 ", self.desktop
if os.name == u'nt':
desktop = self.get_com_desktop()
desktop = self.controller.get_com_desktop()
if desktop is None:
self.start_process()
desktop = self.get_com_desktop()
url = u'file:///' + presentation.replace(u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20')
self.controller.start_process()
desktop = self.controller.get_com_desktop()
url = u'file:///' + self.filepath.replace(u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20')
else:
desktop = self.get_uno_desktop()
url = uno.systemPathToFileUrl(presentation)
desktop = self.controller.get_uno_desktop()
url = uno.systemPathToFileUrl(self.filepath)
if desktop is None:
return
self.desktop = desktop
#print "s.dsk2 ", self.desktop
properties = []
properties.append(self.create_property(u'Minimized', True))
properties = tuple(properties)
@ -145,17 +215,17 @@ class ImpressController(PresentationController):
log.exception(u'Failed to load presentation')
return
self.presentation = self.document.getPresentation()
self.presentation.Display = self.plugin.render_manager.current_display + 1
self.controller = None
self.presentation.Display = self.controller.plugin.render_manager.screens.current_display + 1
self.control = None
self.create_thumbnails()
def create_thumbnails(self):
"""
Create thumbnail images for presentation
"""
log.debug(u'create thumbnails OpenOffice')
if self.check_thumbnails():
return
if os.name == u'nt':
thumbdir = u'file:///' + self.thumbnailpath.replace(
u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20')
@ -169,129 +239,120 @@ class ImpressController(PresentationController):
for idx in range(pages.getCount()):
page = pages.getByIndex(idx)
doc.getCurrentController().setCurrentPage(page)
doc.storeToURL(thumbdir + u'/' + self.thumbnailprefix +
unicode(idx+1) + u'.png', props)
path = u'%s/%s%s.png'% (thumbdir, self.controller.thumbnailprefix,
unicode(idx + 1))
try:
doc.storeToURL(path , props)
except:
log.exception(u'%s\nUnable to store preview' % path)
def create_property(self, name, value):
log.debug(u'create property OpenOffice')
if os.name == u'nt':
prop = self.manager.Bridge_GetStruct(u'com.sun.star.beans.PropertyValue')
prop = self.controller.manager.Bridge_GetStruct(u'com.sun.star.beans.PropertyValue')
else:
prop = PropertyValue()
prop.Name = name
prop.Value = value
return prop
def get_uno_desktop(self):
log.debug(u'getUNODesktop')
ctx = None
loop = 0
context = uno.getComponentContext()
resolver = context.ServiceManager.createInstanceWithContext(
u'com.sun.star.bridge.UnoUrlResolver', context)
while ctx is None and loop < 3:
try:
ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext')
except:
self.start_process()
loop += 1
try:
self.manager = ctx.ServiceManager
desktop = self.manager.createInstanceWithContext(
"com.sun.star.frame.Desktop", ctx )
return desktop
except:
log.exception(u'Failed to get UNO desktop')
return None
def get_com_desktop(self):
log.debug(u'getCOMDesktop')
try:
desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop')
return desktop
except:
log.exception(u'Failed to get COM desktop')
return None
def get_com_servicemanager(self):
log.debug(u'get_com_servicemanager')
try:
return Dispatch(u'com.sun.star.ServiceManager')
except:
log.exception(u'Failed to get COM service manager')
return None
def close_presentation(self):
"""
Close presentation and clean up objects
Triggerent by new object being added to SlideController orOpenLP
being shut down
Triggered by new object being added to SlideController or OpenLP
being shutdown
"""
log.debug(u'close Presentation OpenOffice')
if self.document:
if self.presentation:
self.presentation.end()
self.presentation = None
self.document.dispose()
try:
self.presentation.end()
self.presentation = None
self.document.dispose()
except:
#We tried!
pass
self.document = None
self.controller.remove_doc(self)
def is_loaded(self):
log.debug(u'is loaded OpenOffice')
#print "is_loaded "
if self.presentation is None or self.document is None:
#print "no present or document"
return False
try:
if self.document.getPresentation() is None:
#print "no getPresentation"
return False
except:
return False
return True
def is_active(self):
log.debug(u'is active OpenOffice')
#print "is_active "
if not self.is_loaded():
#print "False "
return False
if self.controller is None:
#print "self.con ", self.control
if self.control is None:
return False
return self.controller.isRunning() and self.controller.isActive()
return True
def unblank_screen(self):
return self.controller.resume()
log.debug(u'unblank screen OpenOffice')
return self.control.resume()
def blank_screen(self):
self.controller.blankScreen(0)
log.debug(u'blank screen OpenOffice')
self.control.blankScreen(0)
def is_blank(self):
"""
Returns true if screen is blank
"""
log.debug(u'is blank OpenOffice')
return self.control.isPaused()
def stop_presentation(self):
self.controller.deactivate()
log.debug(u'stop presentation OpenOffice')
self.control.deactivate()
def start_presentation(self):
if self.controller is None or not self.controller.isRunning():
log.debug(u'start presentation OpenOffice')
if self.control is None or not self.control.isRunning():
self.presentation.start()
# start() returns before the getCurrentComponent is ready. Try for 5 seconds
i = 1
while self.desktop.getCurrentComponent() is None and i < 50:
time.sleep(0.1)
i = i + 1
self.controller = self.desktop.getCurrentComponent().Presentation.getController()
self.control = self.desktop.getCurrentComponent().Presentation.getController()
else:
self.controller.activate()
self.control.activate()
self.goto_slide(1)
def get_slide_number(self):
return self.controller.getCurrentSlideIndex() + 1
return self.control.getCurrentSlideIndex() + 1
def get_slide_count(self):
return self.document.getDrawPages().getCount()
def goto_slide(self, slideno):
self.controller.gotoSlideIndex(slideno-1)
self.control.gotoSlideIndex(slideno-1)
def next_step(self):
"""
Triggers the next effect of slide on the running presentation
"""
self.controller.gotoNextEffect()
self.control.gotoNextEffect()
def previous_step(self):
"""
Triggers the previous slide on the running presentation
"""
self.controller.gotoPreviousSlide()
self.control.gotoPreviousSlide()
def get_slide_preview_file(self, slide_no):
"""
@ -301,8 +362,43 @@ class ImpressController(PresentationController):
The slide an image is required for, starting at 1
"""
path = os.path.join(self.thumbnailpath,
self.thumbnailprefix + unicode(slide_no) + u'.png')
self.controller.thumbnailprefix + unicode(slide_no) + u'.png')
if os.path.isfile(path):
return path
else:
return None
return None
def get_slide_text(self, slide_no):
"""
Returns the text on the slide
``slide_no``
The slide the text is required for, starting at 1
"""
doc = self.document
pages = doc.getDrawPages()
text = ''
page = pages.getByIndex(slide_no - 1)
for idx in range(page.getCount()):
shape = page.getByIndex(idx)
if shape.supportsService("com.sun.star.drawing.Text"):
text += shape.getString() + '\n'
return text
def get_slide_notes(self, slide_no):
"""
Returns the text on the slide
``slide_no``
The slide the notes are required for, starting at 1
"""
doc = self.document
pages = doc.getDrawPages()
text = ''
page = pages.getByIndex(slide_no - 1)
notes = page.getNotesPage()
for idx in range(notes.getCount()):
shape = notes.getByIndex(idx)
if shape.supportsService("com.sun.star.drawing.Text"):
text += shape.getString() + '\n'
return text

View File

@ -31,6 +31,8 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, BaseListWithDnD
from openlp.plugins.presentations.lib import MessageListener
log = logging.getLogger(__name__)
# We have to explicitly create separate classes for each plugin
# in order for DnD to the Service manager to work correctly.
class PresentationListView(BaseListWithDnD):
@ -43,8 +45,6 @@ class PresentationMediaItem(MediaManagerItem):
This is the Presentation media manager item for Presentation Items.
It can present files using Openoffice
"""
global log
log = logging.getLogger(u'PresentationsMediaItem')
log.info(u'Presentations Media Item loaded')
def __init__(self, parent, icon, title, controllers):
@ -52,18 +52,27 @@ class PresentationMediaItem(MediaManagerItem):
self.PluginNameShort = u'Presentation'
self.ConfigSection = title
self.IconPath = u'presentations/presentation'
self.Automatic = u''
# this next is a class, not an instance of a class - it will
# be instanced by the base MediaManagerItem
self.ListViewWithDnD_class = PresentationListView
MediaManagerItem.__init__(self, parent, icon, title)
self.message_listener = MessageListener(controllers)
self.message_listener = MessageListener(self)
def initPluginNameVisible(self):
self.PluginNameVisible = self.trUtf8('Presentation')
def retranslateUi(self):
self.OnNewPrompt = self.trUtf8('Select Presentation(s)')
self.OnNewFileMasks = self.trUtf8('Presentations (*.ppt *.pps *.odp)')
self.Automatic = self.trUtf8('Automatic')
fileType = 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.OnNewFileMasks = self.trUtf8('Presentations (%s)' % fileType)
def requiredIcons(self):
MediaManagerItem.requiredIcons(self)
@ -100,6 +109,9 @@ class PresentationMediaItem(MediaManagerItem):
#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)
def loadList(self, list):
currlist = self.getFileList()
@ -129,7 +141,9 @@ class PresentationMediaItem(MediaManagerItem):
self.ConfigSection, self.getFileList())
filepath = unicode((item.data(QtCore.Qt.UserRole)).toString())
for cidx in self.controllers:
self.controllers[cidx].presentation_deleted(filepath)
doc = self.controllers[cidx].add_doc(filepath)
doc.presentation_deleted()
self.controllers[cidx].remove_doc(doc)
def generateSlideData(self, service_item):
items = self.ListView.selectedIndexes()
@ -137,18 +151,39 @@ class PresentationMediaItem(MediaManagerItem):
return False
service_item.title = unicode(self.DisplayTypeComboBox.currentText())
service_item.shortname = unicode(self.DisplayTypeComboBox.currentText())
controller = self.controllers[service_item.shortname]
shortname = service_item.shortname
for item in items:
bitem = self.ListView.item(item.row())
filename = unicode((bitem.data(QtCore.Qt.UserRole)).toString())
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)
controller.store_filename(filename)
if controller.get_slide_preview_file(1) is None:
controller.load_presentation(filename)
doc = controller.add_doc(filename)
if doc.get_slide_preview_file(1) is None:
doc.load_presentation()
i = 1
img = controller.get_slide_preview_file(i)
img = doc.get_slide_preview_file(i)
while img:
service_item.add_from_command(path, name, img)
i = i + 1
img = controller.get_slide_preview_file(i)
return True
img = doc.get_slide_preview_file(i)
controller.remove_doc(doc)
return True
def findControllerByType(self, filename):
filetype = os.path.splitext(filename)[1]
if not filetype:
return None
for controller in self.controllers:
if self.controllers[controller].enabled:
if filetype in self.controllers[controller].supports:
return controller
for controller in self.controllers:
if self.controllers[controller].enabled:
if filetype in self.controllers[controller].alsosupports:
return controller
return None

View File

@ -30,136 +30,160 @@ from PyQt4 import QtCore
from openlp.core.lib import Receiver
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
"""
global log
log = logging.getLogger(u'Controller')
log.info(u'Controller loaded')
def __init__(self, live):
self.isLive = live
self.doc = None
log.info(u'%s controller loaded' % live)
def addHandler(self, controller, file):
def addHandler(self, controller, file, isBlank):
log.debug(u'Live = %s, addHandler %s' % (self.isLive, file))
self.controller = controller
if self.controller.is_loaded():
self.shutdown(None)
self.controller.load_presentation(file)
if self.doc is not None:
self.shutdown()
self.doc = self.controller.add_doc(file)
self.doc.load_presentation()
if self.isLive:
self.controller.start_presentation()
self.doc.start_presentation()
if isBlank:
self.blank()
Receiver.send_message(u'live_slide_hide')
self.controller.slidenumber = 0
self.doc.slidenumber = 0
def activate(self):
log.debug(u'Live = %s, activate' % self.isLive)
if self.controller.is_active():
if self.doc.is_active():
return
if not self.controller.is_loaded():
self.controller.load_presentation(self.controller.filepath)
if not self.doc.is_loaded():
self.doc.load_presentation()
if self.isLive:
self.controller.start_presentation()
if self.controller.slidenumber > 1:
self.controller.goto_slide(self.controller.slidenumber)
self.doc.start_presentation()
if self.doc.slidenumber > 1:
self.doc.goto_slide(self.doc.slidenumber)
def slide(self, slide, live):
log.debug(u'Live = %s, slide' % live)
# if not isLive:
# return
if not live:
return
if self.doc.is_blank():
self.doc.slidenumber = int(slide) + 1
return
self.activate()
self.controller.goto_slide(int(slide) + 1)
self.controller.poll_slidenumber(live)
self.doc.goto_slide(int(slide) + 1)
self.doc.poll_slidenumber(live)
def first(self, message):
def first(self):
"""
Based on the handler passed at startup triggers the first slide
"""
log.debug(u'Live = %s, first' % self.isLive)
print "first ", message
if not self.isLive:
return
if self.doc.is_blank():
self.doc.slidenumber = 1
return
self.activate()
self.controller.start_presentation()
self.controller.poll_slidenumber(self.isLive)
self.doc.start_presentation()
self.doc.poll_slidenumber(self.isLive)
def last(self, message):
def last(self):
"""
Based on the handler passed at startup triggers the first slide
"""
log.debug(u'Live = %s, last' % self.isLive)
print "last ", message
if not self.isLive:
return
if self.doc.is_blank():
self.doc.slidenumber = self.doc.get_slide_count()
return
self.activate()
self.controller.goto_slide(self.controller.get_slide_count())
self.controller.poll_slidenumber(self.isLive)
self.doc.goto_slide(self.doc.get_slide_count())
self.doc.poll_slidenumber(self.isLive)
def next(self, message):
def next(self):
"""
Based on the handler passed at startup triggers the next slide event
"""
log.debug(u'Live = %s, next' % self.isLive)
print "next ", message
if not self.isLive:
return
if self.doc.is_blank():
if self.doc.slidenumber < self.doc.get_slide_count():
self.doc.slidenumber = self.doc.slidenumber + 1
return
self.activate()
self.controller.next_step()
self.controller.poll_slidenumber(self.isLive)
self.doc.next_step()
self.doc.poll_slidenumber(self.isLive)
def previous(self, message):
def previous(self):
"""
Based on the handler passed at startup triggers the previous slide event
"""
log.debug(u'Live = %s, previous' % self.isLive)
if not self.isLive:
return
print "previous ", message
if self.doc.is_blank():
if self.doc.slidenumber > 1:
self.doc.slidenumber = self.doc.slidenumber - 1
return
self.activate()
self.controller.previous_step()
self.controller.poll_slidenumber(self.isLive)
self.doc.previous_step()
self.doc.poll_slidenumber(self.isLive)
def shutdown(self, message):
def shutdown(self):
"""
Based on the handler passed at startup triggers slide show to shut down
"""
log.debug(u'Live = %s, shutdown' % self.isLive)
self.controller.close_presentation()
self.controller.slidenumber = 0
if self.isLive:
Receiver.send_message(u'live_slide_show')
self.doc.close_presentation()
self.doc = None
#self.doc.slidenumber = 0
#self.timer.stop()
def blank(self):
log.debug(u'Live = %s, blank' % self.isLive)
if not self.isLive:
return
if not self.controller.is_loaded():
if not self.doc.is_loaded():
return
if not self.controller.is_active():
if not self.doc.is_active():
return
self.controller.blank_screen()
self.doc.blank_screen()
def unblank(self):
if not self.is_live:
log.debug(u'Live = %s, unblank' % self.isLive)
if not self.isLive:
return
self.activate()
self.controller.unblank_screen()
if self.doc.slidenumber and self.doc.slidenumber != self.doc.get_slide_number():
self.doc.goto_slide(self.doc.slidenumber)
self.doc.unblank_screen()
def poll(self):
self.doc.poll_slidenumber(self.isLive)
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
"""
global log
log = logging.getLogger(u'MessageListener')
log.info(u'Message Listener loaded')
def __init__(self, controllers):
self.controllers = controllers
def __init__(self, mediaitem):
self.controllers = mediaitem.controllers
self.mediaitem = mediaitem
self.previewHandler = Controller(False)
self.liveHandler = Controller(True)
self.isLive = None
# messages are sent from core.ui.slidecontroller
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'presentations_start'), self.startup)
@ -189,11 +213,17 @@ class MessageListener(object):
Save the handler as any new presentations start here
"""
log.debug(u'Startup called with message %s' % message)
self.handler, file, isLive = self.decodeMessage(message)
self.handler, file, isLive, isBlank = self.decodeMessage(message)
if self.handler == self.mediaitem.Automatic:
self.handler = self.mediaitem.findControllerByType(file)
if not self.handler:
return
if isLive:
self.liveHandler.addHandler(self.controllers[self.handler], file)
controller = self.liveHandler
else:
self.previewHandler.addHandler(self.controllers[self.handler], file)
controller = self.previewHandler
controller.addHandler(self.controllers[self.handler], file, isBlank)
def slide(self, message):
slide, live = self.splitMessage(message)
@ -202,48 +232,42 @@ class MessageListener(object):
else:
self.previewHandler.slide(slide, live)
def first(self, message):
if self.isLive:
self.liveHandler.first(message)
def first(self, isLive):
if isLive:
self.liveHandler.first()
else:
self.previewHandler.first(message)
self.previewHandler.first()
def last(self, message):
if self.isLive:
self.liveHandler.last(message)
def last(self, isLive):
if isLive:
self.liveHandler.last()
else:
self.previewHandler.last(message)
self.previewHandler.last()
def next(self, message):
if self.isLive:
self.liveHandler.next(message)
def next(self, isLive):
if isLive:
self.liveHandler.next()
else:
self.previewHandler.next(message)
self.previewHandler.next()
def previous(self, message):
if self.isLive:
self.liveHandler.previous(message)
def previous(self, isLive):
if isLive:
self.liveHandler.previous()
else:
self.previewHandler.previous(message)
self.previewHandler.previous()
def shutdown(self, message):
if self.isLive:
self.liveHandler.shutdown(message)
def shutdown(self, isLive):
if isLive:
self.liveHandler.shutdown()
Receiver.send_message(u'live_slide_show')
else:
self.previewHandler.shutdown(message)
self.previewHandler.shutdown()
def blank(self):
if self.isLive:
self.liveHandler.blank()
else:
self.previewHandler.blank()
self.liveHandler.blank()
def unblank(self):
if self.isLive:
self.liveHandler.unblank()
else:
self.previewHandler.unblank()
self.liveHandler.unblank()
def splitMessage(self, message):
"""
@ -265,7 +289,7 @@ class MessageListener(object):
Message containing Presentaion handler name and file to be presented.
"""
file = os.path.join(message[1], message[2])
return message[0], file, message[4]
return message[0], file, message[4], message[5]
def timeout(self):
self.controller.poll_slidenumber(self.is_live)
self.liveHandler.poll()

View File

@ -31,7 +31,9 @@ if os.name == u'nt':
import _winreg
import win32ui
from presentationcontroller import PresentationController
from presentationcontroller import PresentationController, PresentationDocument
log = logging.getLogger(__name__)
# PPT API documentation:
# http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx
@ -42,9 +44,7 @@ class PowerpointController(PresentationController):
It creates the runtime Environment , Loads the and Closes the Presentation
As well as triggering the correct activities based on the users input
"""
global log
log = logging.getLogger(u'PowerpointController')
log.info(u'loaded')
log.info(u'PowerpointController loaded')
def __init__(self, plugin):
"""
@ -52,8 +52,8 @@ class PowerpointController(PresentationController):
"""
log.debug(u'Initialising')
PresentationController.__init__(self, plugin, u'Powerpoint')
self.supports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx']
self.process = None
self.presentation = None
def check_available(self):
"""
@ -77,121 +77,145 @@ class PowerpointController(PresentationController):
self.process.Visible = True
self.process.WindowState = 2
def is_loaded(self):
"""
Returns true if a presentation is loaded
"""
try:
if not self.process.Visible:
return False
if self.process.Windows.Count == 0:
return False
if self.process.Presentations.Count == 0:
return False
except:
return False
return True
def kill(self):
"""
Called at system exit to clean up any running presentations
"""
for doc in self.docs:
doc.close_presentation()
if self.process is None:
return
if self.process.Presentations.Count > 0:
return
try:
self.process.Quit()
except:
pass
self.process = None
def load_presentation(self, presentation):
"""
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.
def add_doc(self, name):
log.debug(u'Add Doc PowerPoint')
doc = PowerpointDocument(self, name)
self.docs.append(doc)
return doc
``presentation``
The file name of the presentations to run.
"""
log.debug(u'LoadPresentation')
self.store_filename(presentation)
try:
if not self.process.Visible:
self.start_process()
except:
self.start_process()
try:
self.process.Presentations.Open(presentation, False, False, True)
except:
return
self.presentation = self.process.Presentations(self.process.Presentations.Count)
self.create_thumbnails()
class PowerpointDocument(PresentationDocument):
def create_thumbnails(self):
"""
Create the thumbnail images for the current presentation.
Note an alternative and quicker method would be do
self.presentation.Slides[n].Copy()
thumbnail = QApplication.clipboard.image()
But for now we want a physical file since it makes
life easier elsewhere
"""
if self.check_thumbnails():
return
self.presentation.Export(os.path.join(self.thumbnailpath, '')
, 'png', 600, 480)
def __init__(self, controller, presentation):
log.debug(u'Init Presentation Powerpoint')
self.presentation = None
self.controller = controller
self.store_filename(presentation)
def close_presentation(self):
"""
Close presentation and clean up objects
Triggerent by new object being added to SlideController orOpenLP
being shut down
"""
if self.presentation == None:
return
try:
self.presentation.Close()
except:
pass
self.presentation = None
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.
def is_active(self):
"""
Returns true if a presentation is currently active
"""
if not self.is_loaded():
``presentation``
The file name of the presentations to run.
"""
log.debug(u'LoadPresentation')
if not self.controller.process.Visible:
self.controller.start_process()
#try:
self.controller.process.Presentations.Open(self.filepath, False, False, True)
#except:
# return
self.presentation = self.controller.process.Presentations(
self.controller.process.Presentations.Count)
self.create_thumbnails()
def create_thumbnails(self):
"""
Create the thumbnail images for the current presentation.
Note an alternative and quicker method would be do
self.presentation.Slides[n].Copy()
thumbnail = QApplication.clipboard.image()
But for now we want a physical file since it makes
life easier elsewhere
"""
if self.check_thumbnails():
return
self.presentation.Export(os.path.join(self.thumbnailpath, '')
, 'png', 640, 480)
def close_presentation(self):
"""
Close presentation and clean up objects
Triggerent by new object being added to SlideController orOpenLP
being shut down
"""
if self.presentation is None:
return
try:
self.presentation.Close()
except:
pass
self.presentation = None
self.controller.remove_doc(self)
def is_loaded(self):
"""
Returns true if a presentation is loaded
"""
try:
if not self.controller.process.Visible:
return False
try:
if self.presentation.SlideShowWindow == None:
return False
if self.presentation.SlideShowWindow.View == None:
return False
except:
if self.controller.process.Windows.Count == 0:
return False
return True
if self.controller.process.Presentations.Count == 0:
return False
except:
return False
return True
def unblank_screen(self):
"""
Unblanks (restores) the presentationn
"""
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate()
def blank_screen(self):
"""
Blanks the screen
"""
self.presentation.SlideShowWindow.View.State = 3
def is_active(self):
"""
Returns true if a presentation is currently active
"""
if not self.is_loaded():
return False
try:
if self.presentation.SlideShowWindow is None:
return False
if self.presentation.SlideShowWindow.View is None:
return False
except:
return False
return True
def stop_presentation(self):
"""
Stops the current presentation and hides the output
"""
self.presentation.SlideShowWindow.View.Exit()
def unblank_screen(self):
"""
Unblanks (restores) the presentationn
"""
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate()
def blank_screen(self):
"""
Blanks the screen
"""
self.presentation.SlideShowWindow.View.State = 3
def is_blank(self):
"""
Returns true if screen is blank
"""
return self.presentation.SlideShowWindow.View.State == 3
def stop_presentation(self):
"""
Stops the current presentation and hides the output
"""
self.presentation.SlideShowWindow.View.Exit()
if os.name == u'nt':
def start_presentation(self):
"""
Starts a presentation from the beginning
@ -206,53 +230,83 @@ class PowerpointController(PresentationController):
dpi = 96
self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.GotoSlide(1)
rendermanager = self.plugin.render_manager
rect = rendermanager.screen_list[rendermanager.current_display][u'size']
rendermanager = self.controller.plugin.render_manager
rect = rendermanager.screens.current[u'size']
self.presentation.SlideShowWindow.Top = rect.y() * 72 / dpi
self.presentation.SlideShowWindow.Height = rect.height() * 72 / dpi
self.presentation.SlideShowWindow.Left = rect.x() * 72 / dpi
self.presentation.SlideShowWindow.Width = rect.width() * 72 / dpi
def get_slide_number(self):
"""
Returns the current slide number
"""
return self.presentation.SlideShowWindow.View.CurrentShowPosition
def get_slide_number(self):
"""
Returns the current slide number
"""
return self.presentation.SlideShowWindow.View.CurrentShowPosition
def get_slide_count(self):
"""
Returns total number of slides
"""
return self.presentation.Slides.Count
def get_slide_count(self):
"""
Returns total number of slides
"""
return self.presentation.Slides.Count
def goto_slide(self, slideno):
"""
Moves to a specific slide in the presentation
"""
self.presentation.SlideShowWindow.View.GotoSlide(slideno)
def goto_slide(self, slideno):
"""
Moves to a specific slide in the presentation
"""
self.presentation.SlideShowWindow.View.GotoSlide(slideno)
def next_step(self):
"""
Triggers the next effect of slide on the running presentation
"""
self.presentation.SlideShowWindow.View.Next()
def next_step(self):
"""
Triggers the next effect of slide on the running presentation
"""
self.presentation.SlideShowWindow.View.Next()
def previous_step(self):
"""
Triggers the previous slide on the running presentation
"""
self.presentation.SlideShowWindow.View.Previous()
def previous_step(self):
"""
Triggers the previous slide on the running presentation
"""
self.presentation.SlideShowWindow.View.Previous()
def get_slide_preview_file(self, slide_no):
"""
Returns an image path containing a preview for the requested slide
def get_slide_preview_file(self, slide_no):
"""
Returns an image path containing a preview for the requested slide
``slide_no``
The slide an image is required for, starting at 1
"""
path = os.path.join(self.thumbnailpath,
self.thumbnailprefix + unicode(slide_no) + u'.png')
if os.path.isfile(path):
return path
else:
return None
``slide_no``
The slide an image is required for, starting at 1
"""
path = os.path.join(self.thumbnailpath,
self.controller.thumbnailprefix + unicode(slide_no) + u'.png')
if os.path.isfile(path):
return path
else:
return None
def get_slide_text(self, slide_no):
"""
Returns the text on the slide
``slide_no``
The slide the text is required for, starting at 1
"""
text = ''
shapes = self.presentation.Slides(slide_no).Shapes
for idx in range(shapes.Count):
shape = shapes(idx + 1)
if shape.HasTextFrame:
text += shape.TextFrame.TextRange.Text + '\n'
return text
def get_slide_notes(self, slide_no):
"""
Returns the text on the slide
``slide_no``
The slide the notes are required for, starting at 1
"""
text = ''
shapes = self.presentation.Slides(slide_no).NotesPage.Shapes
for idx in range(shapes.Count):
shape = shapes(idx + 1)
if shape.HasTextFrame:
text += shape.TextFrame.TextRange.Text + '\n'
return text

View File

@ -30,7 +30,9 @@ if os.name == u'nt':
from ctypes import *
from ctypes.wintypes import RECT
from presentationcontroller import PresentationController
from presentationcontroller import PresentationController, PresentationDocument
log = logging.getLogger(__name__)
class PptviewController(PresentationController):
"""
@ -38,9 +40,7 @@ class PptviewController(PresentationController):
It creates the runtime Environment , Loads the and Closes the Presentation
As well as triggering the correct activities based on the users input
"""
global log
log = logging.getLogger(u'PptviewController')
log.info(u'loaded')
log.info(u'PPTViewController loaded')
def __init__(self, plugin):
"""
@ -49,7 +49,7 @@ class PptviewController(PresentationController):
log.debug(u'Initialising')
self.process = None
PresentationController.__init__(self, plugin, u'Powerpoint Viewer')
self.pptid = None
self.supports = [u'.ppt', u'.pps', u'.pptx', u'.ppsx']
def check_available(self):
"""
@ -89,123 +89,147 @@ class PptviewController(PresentationController):
Called at system exit to clean up any running presentations
"""
log.debug(u'Kill')
self.close_presentation()
for doc in self.docs:
doc.close_presentation()
def load_presentation(self, presentation):
"""
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.
def add_doc(self, name):
log.debug(u'Add Doc PPTView')
doc = PptviewDocument(self, name)
self.docs.append(doc)
return doc
``presentation``
The file name of the presentations to run.
"""
log.debug(u'LoadPresentation')
self.store_filename(presentation)
if self.pptid >= 0:
self.close_presentation()
rendermanager = self.plugin.render_manager
rect = rendermanager.screen_list[rendermanager.current_display][u'size']
rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom())
filepath = str(presentation.replace(u'/', u'\\'));
try:
self.pptid = self.process.OpenPPT(filepath, None, rect,
str(os.path.join(self.thumbnailpath, self.thumbnailprefix)))
self.stop_presentation()
except:
log.exception(u'Failed to load presentation')
class PptviewDocument(PresentationDocument):
def close_presentation(self):
"""
Close presentation and clean up objects
Triggerent by new object being added to SlideController orOpenLP
being shut down
"""
self.process.ClosePPT(self.pptid)
self.pptid = -1
def __init__(self, controller, presentation):
log.debug(u'Init Presentation PowerPoint')
self.presentation = None
self.pptid = None
self.blanked = False
self.controller = controller
self.store_filename(presentation)
def is_loaded(self):
"""
Returns true if a presentation is loaded
"""
if self.pptid < 0:
return False
if self.get_slide_count() < 0:
return False
return True
def load_presentation(self):
"""
Called when a presentation is added to the SlideController.
It builds the environment, starts communcations with the background
PptView task started earlier.
def is_active(self):
"""
Returns true if a presentation is currently active
"""
return self.is_loaded()
``presentation``
The file name of the presentations to run.
"""
log.debug(u'LoadPresentation')
#if self.pptid >= 0:
# self.close_presentation()
rendermanager = self.controller.plugin.render_manager
rect = rendermanager.screens.current[u'size']
rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom())
filepath = str(self.filepath.replace(u'/', u'\\'))
try:
self.pptid = self.controller.process.OpenPPT(filepath, None, rect,
str(os.path.join(self.thumbnailpath, self.controller.thumbnailprefix)))
self.stop_presentation()
except:
log.exception(u'Failed to load presentation')
def blank_screen(self):
"""
Blanks the screen
"""
self.process.Blank(self.pptid)
def close_presentation(self):
"""
Close presentation and clean up objects
Triggerent by new object being added to SlideController orOpenLP
being shut down
"""
self.controller.process.ClosePPT(self.pptid)
self.pptid = -1
self.controller.remove_doc(self)
def unblank_screen(self):
"""
Unblanks (restores) the presentationn
"""
self.process.Unblank(self.pptid)
def is_loaded(self):
"""
Returns true if a presentation is loaded
"""
if self.pptid < 0:
return False
if self.get_slide_count() < 0:
return False
return True
def stop_presentation(self):
"""
Stops the current presentation and hides the output
"""
self.process.Stop(self.pptid)
def is_active(self):
"""
Returns true if a presentation is currently active
"""
return self.is_loaded()
def start_presentation(self):
"""
Starts a presentation from the beginning
"""
self.process.RestartShow(self.pptid)
def blank_screen(self):
"""
Blanks the screen
"""
self.controller.process.Blank(self.pptid)
self.blanked = True
def get_slide_number(self):
"""
Returns the current slide number
"""
return self.process.GetCurrentSlide(self.pptid)
def unblank_screen(self):
"""
Unblanks (restores) the presentationn
"""
self.controller.process.Unblank(self.pptid)
self.blanked = False
def get_slide_count(self):
"""
Returns total number of slides
"""
return self.process.GetSlideCount(self.pptid)
def is_blank(self):
"""
Returns true if screen is blank
"""
log.debug(u'is blank OpenOffice')
return self.blanked
def goto_slide(self, slideno):
"""
Moves to a specific slide in the presentation
"""
self.process.GotoSlide(self.pptid, slideno)
def stop_presentation(self):
"""
Stops the current presentation and hides the output
"""
self.controller.process.Stop(self.pptid)
def next_step(self):
"""
Triggers the next effect of slide on the running presentation
"""
self.process.NextStep(self.pptid)
def start_presentation(self):
"""
Starts a presentation from the beginning
"""
self.controller.process.RestartShow(self.pptid)
def previous_step(self):
"""
Triggers the previous slide on the running presentation
"""
self.process.PrevStep(self.pptid)
def get_slide_number(self):
"""
Returns the current slide number
"""
return self.controller.process.GetCurrentSlide(self.pptid)
def get_slide_preview_file(self, slide_no):
"""
Returns an image path containing a preview for the requested slide
def get_slide_count(self):
"""
Returns total number of slides
"""
return self.controller.process.GetSlideCount(self.pptid)
``slide_no``
The slide an image is required for, starting at 1
"""
path = os.path.join(self.thumbnailpath,
self.thumbnailprefix + unicode(slide_no) + u'.bmp')
if os.path.isfile(path):
return path
else:
return None
def goto_slide(self, slideno):
"""
Moves to a specific slide in the presentation
"""
self.controller.process.GotoSlide(self.pptid, slideno)
def next_step(self):
"""
Triggers the next effect of slide on the running presentation
"""
self.controller.process.NextStep(self.pptid)
def previous_step(self):
"""
Triggers the previous slide on the running presentation
"""
self.controller.process.PrevStep(self.pptid)
def get_slide_preview_file(self, slide_no):
"""
Returns an image path containing a preview for the requested slide
``slide_no``
The slide an image is required for, starting at 1
"""
path = os.path.join(self.thumbnailpath,
self.controller.thumbnailprefix + unicode(slide_no) + u'.bmp')
if os.path.isfile(path):
return path
else:
return None

View File

@ -31,16 +31,16 @@ from PyQt4 import QtCore
from openlp.core.lib import Receiver
log = logging.getLogger(__name__)
class PresentationController(object):
"""
Base class for presentation controllers to inherit from
Class to control interactions with presentations.
It creates the runtime environment, loads and closes the presentation as
well as triggering the correct activities based on the users input
It creates the runtime environment
To create a new controller, take a copy of this file and name it
so it ends in controller.py, i.e. foobarcontroller.py
Make sure it inhetits PresentationController
Make sure it inherits PresentationController
Then fill in the blanks. If possible try and make sure it loads
on all platforms, using for example os.name checks, although
__init__, check_available and presentation_deleted should always work.
@ -73,6 +73,87 @@ class PresentationController(object):
``presentation_deleted()``
Deletes presentation specific files, e.g. thumbnails
"""
log.info(u'PresentationController loaded')
def __init__(self, plugin=None, name=u'PresentationController'):
"""
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):
PresentationController.__init(self, plugin, u'My Presenter App')
``plugin``
Defaults to *None*. The presentationplugin object
``name``
Name of the application, to appear in the application
"""
self.supports = []
self.alsosupports = []
self.docs = []
self.plugin = plugin
self.name = name
self.available = self.check_available()
if self.available:
self.enabled = int(plugin.config.get_config(
name, QtCore.Qt.Unchecked)) == QtCore.Qt.Checked
else:
self.enabled = False
self.thumbnailroot = os.path.join(plugin.config.get_data_path(),
name, u'thumbnails')
self.thumbnailprefix = u'slide'
if not os.path.isdir(self.thumbnailroot):
os.makedirs(self.thumbnailroot)
def check_available(self):
"""
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.
"""
pass
def kill(self):
"""
Called at system exit to clean up any running presentations and
close the application
"""
log.debug(u'Kill')
self.close_presentation()
def add_doc(self, name):
"""
Called when a new presentation document is opened
"""
doc = PresentationDocument(self, name)
self.docs.append(doc)
return doc
def remove_doc(self, doc):
"""
Called to remove an open document from the collection
"""
log.debug(u'remove_doc Presentation')
self.docs.remove(doc)
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
**Hook Functions**
``load_presentation(presentation)``
Load a presentation file
@ -91,6 +172,9 @@ class PresentationController(object):
``unblank_screen()``
Unblanks the screen, restoring the output
``is_blank``
Returns true if screen is blank
``stop_presentation()``
Stops the presentation, removing it from the output display
@ -116,70 +200,12 @@ class PresentationController(object):
Returns a path to an image containing a preview for the requested slide
"""
global log
log = logging.getLogger(u'PresentationController')
log.info(u'loaded')
def __init__(self, plugin=None, name=u'PresentationController'):
"""
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):
PresentationController.__init(self, plugin, u'My Presenter App')
``plugin``
Defaults to *None*. The presentationplugin object
``name``
Name of the application, to appear in the application
"""
self.plugin = plugin
self.name = name
self.available = self.check_available()
def __init__(self, controller, name):
self.slidenumber = 0
if self.available:
self.enabled = int(plugin.config.get_config(
name, QtCore.Qt.Unchecked)) == QtCore.Qt.Checked
else:
self.enabled = False
self.thumbnailroot = os.path.join(plugin.config.get_data_path(),
name, u'thumbnails')
self.thumbnailprefix = u'slide'
if not os.path.isdir(self.thumbnailroot):
os.makedirs(self.thumbnailroot)
self.controller = controller
self.store_filename(name)
def check_available(self):
"""
Presentation app is able to run on this machine
"""
return False
def presentation_deleted(self, presentation):
"""
Cleans up/deletes any controller specific files created for
a file, e.g. thumbnails
"""
self.store_filename(presentation)
shutil.rmtree(self.thumbnailpath)
def start_process(self):
"""
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
"""
log.debug(u'Kill')
self.close_presentation()
def load_presentation(self, presentation):
def load_presentation(self):
"""
Called when a presentation is added to the SlideController.
Loads the presentation and starts it
@ -190,16 +216,29 @@ class PresentationController(object):
"""
pass
def presentation_deleted(self):
"""
Cleans up/deletes any controller specific files created for
a file, e.g. thumbnails
"""
shutil.rmtree(self.thumbnailpath)
def store_filename(self, presentation):
"""
Set properties for the filename and thumbnail paths
"""
self.filepath = presentation
self.filename = os.path.split(presentation)[1]
self.thumbnailpath = os.path.join(self.thumbnailroot, self.filename)
self.filename = self.get_file_name(presentation)
self.thumbnailpath = self.get_thumbnail_path(presentation)
if not os.path.isdir(self.thumbnailpath):
os.mkdir(self.thumbnailpath)
def get_file_name(self, presentation):
return os.path.split(presentation)[1]
def get_thumbnail_path(self, presentation):
return os.path.join(self.controller.thumbnailroot, self.get_file_name(presentation))
def check_thumbnails(self):
"""
Returns true if the thumbnail images look to exist and are more
@ -217,10 +256,10 @@ class PresentationController(object):
Close presentation and clean up objects
Triggered by new object being added to SlideController
"""
pass
self.controller.delete_doc(self)
def is_active(self):
"""
"""
Returns True if a presentation is currently running
"""
return False
@ -243,6 +282,12 @@ class PresentationController(object):
"""
pass
def is_blank(self):
"""
Returns true if screen is blank
"""
return False
def stop_presentation(self):
"""
Stops the presentation, removing it from the output display
@ -313,4 +358,22 @@ class PresentationController(object):
else:
prefix = u'preview'
Receiver.send_message(u'%s_slidecontroller_change' % prefix,
self.slidenumber - 1)
self.slidenumber - 1)
def get_slide_text(self, slide_no):
"""
Returns the text on the slide
``slide_no``
The slide the text is required for, starting at 1
"""
return ''
def get_slide_notes(self, slide_no):
"""
Returns the text on the slide
``slide_no``
The slide the notes are required for, starting at 1
"""
return ''

View File

@ -51,17 +51,10 @@ class PresentationTab(SettingsTab):
self.PresentationLeftLayout.setMargin(0)
self.VerseDisplayGroupBox = QtGui.QGroupBox(self)
self.VerseDisplayGroupBox.setObjectName(u'VerseDisplayGroupBox')
self.VerseDisplayLayout = QtGui.QGridLayout(self.VerseDisplayGroupBox)
self.VerseDisplayLayout = QtGui.QVBoxLayout(self.VerseDisplayGroupBox)
self.VerseDisplayLayout.setMargin(8)
self.VerseDisplayLayout.setObjectName(u'VerseDisplayLayout')
self.VerseTypeWidget = QtGui.QWidget(self.VerseDisplayGroupBox)
self.VerseTypeWidget.setObjectName(u'VerseTypeWidget')
self.VerseTypeLayout = QtGui.QHBoxLayout(self.VerseTypeWidget)
self.VerseTypeLayout.setSpacing(8)
self.VerseTypeLayout.setMargin(0)
self.VerseTypeLayout.setObjectName(u'VerseTypeLayout')
self.PresenterCheckboxes = {}
index = 0
for key in self.controllers:
controller = self.controllers[key]
checkbox = QtGui.QCheckBox(self.VerseDisplayGroupBox)
@ -69,8 +62,7 @@ class PresentationTab(SettingsTab):
checkbox.setEnabled(controller.available)
checkbox.setObjectName(controller.name + u'CheckBox')
self.PresenterCheckboxes[controller.name] = checkbox
index = index + 1
self.VerseDisplayLayout.addWidget(checkbox, index, 0, 1, 1)
self.VerseDisplayLayout.addWidget(checkbox)
self.PresentationThemeWidget = QtGui.QWidget(self.VerseDisplayGroupBox)
self.PresentationThemeWidget.setObjectName(u'PresentationThemeWidget')
self.PresentationThemeLayout = QtGui.QHBoxLayout(
@ -96,6 +88,7 @@ class PresentationTab(SettingsTab):
self.PresentationLayout.addWidget(self.PresentationRightWidget)
def retranslateUi(self):
self.VerseDisplayGroupBox.setTitle(self.trUtf8('Available Controllers'))
for key in self.controllers:
controller = self.controllers[key]
checkbox = self.PresenterCheckboxes[controller.name]
@ -115,4 +108,4 @@ class PresentationTab(SettingsTab):
controller = self.controllers[key]
checkbox = self.PresenterCheckboxes[controller.name]
self.config.set_config(
controller.name, unicode(checkbox.checkState()))
controller.name, unicode(checkbox.checkState()))

View File

@ -26,20 +26,21 @@
import os
import logging
from openlp.core.lib import Plugin, build_icon
from openlp.core.lib import Plugin, build_icon, Receiver, PluginStatus
from openlp.plugins.presentations.lib import *
class PresentationPlugin(Plugin):
log = logging.getLogger(__name__)
global log
class PresentationPlugin(Plugin):
log = logging.getLogger(u'PresentationPlugin')
def __init__(self, plugin_helpers):
log.debug(u'Initialised')
self.controllers = {}
Plugin.__init__(self, u'Presentations', u'1.9.0', plugin_helpers)
Plugin.__init__(self, u'Presentations', u'1.9.1', plugin_helpers)
self.weight = -8
self.icon = build_icon(u':/media/media_presentation.png')
self.status = PluginStatus.Active
def get_settings_tab(self):
"""
@ -51,6 +52,12 @@ class PresentationPlugin(Plugin):
log.info(u'Presentations Initialising')
Plugin.initialise(self)
self.insert_toolbox_item()
presentation_types = []
for controller in self.controllers:
if self.controllers[controller].enabled:
presentation_types.append({u'%s' % controller : self.controllers[controller].supports})
Receiver.send_message(
u'presentation types', presentation_types)
def finalise(self):
log.info(u'Plugin Finalise')
@ -96,7 +103,7 @@ class PresentationPlugin(Plugin):
self.registerControllers(controller)
if controller.enabled:
controller.start_process()
if len(self.controllers) > 0:
if self.controllers:
return True
else:
return False
@ -106,4 +113,4 @@ class PresentationPlugin(Plugin):
'the ability to show presentations using a number of different '
'programs. The choice of available presentation programs is '
'available to the user in a drop down box.')
return about_text
return about_text

View File

@ -28,7 +28,6 @@ import socket
import sys
from optparse import OptionParser
def sendData(options, message):
addr = (options.address, options.port)
try:
@ -47,34 +46,23 @@ def main():
parser.add_option("-v", "--verbose",
action="store_true", dest="verbose", default=True,
help="make lots of noise [%default]")
parser.add_option("-p", "--port",
default=4316,
parser.add_option("-p", "--port", default=4316,
help="IP Port number %default ")
parser.add_option("-a", "--address",
help="Recipient address ")
parser.add_option("-e", "--event",
default=u'Alert',
help="Action to be undertaken")
parser.add_option("-m", "--message",
help="Message to be passed for the action")
parser.add_option("-n", "--slidenext",
help="Trigger the next slide")
(options, args) = parser.parse_args()
if len(args) > 0:
if args:
parser.print_help()
parser.error("incorrect number of arguments")
elif options.message is None:
parser.print_help()
parser.error("No message passed")
elif options.address is None:
parser.print_help()
parser.error("IP address missing")
elif options.slidenext:
options.event = u'next_slide'
options.message = u''
text = format_message(options)
sendData(options, text)
elif options.message is None:
parser.print_help()
parser.error("No message passed")
else:
text = format_message(options)
sendData(options, text)

View File

@ -30,14 +30,13 @@ from PyQt4 import QtNetwork, QtCore
from openlp.core.lib import Plugin, Receiver
from openlp.plugins.remotes.lib import RemoteTab
class RemotesPlugin(Plugin):
log = logging.getLogger(__name__)
global log
log = logging.getLogger(u'RemotesPlugin')
class RemotesPlugin(Plugin):
log.info(u'Remote Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Remotes', u'1.9.0', plugin_helpers)
Plugin.__init__(self, u'Remotes', u'1.9.1', plugin_helpers)
self.weight = -1
self.server = None
@ -83,4 +82,4 @@ class RemotesPlugin(Plugin):
'provides the ability to send messages to a running version of '
'openlp on a different computer.<br>The Primary use for this '
'would be to send alerts from a creche')
return about_text
return about_text

View File

@ -33,13 +33,13 @@ from openlp.plugins.songs.forms import EditVerseForm
from openlp.plugins.songs.lib.models import Song
from editsongdialog import Ui_EditSongDialog
log = logging.getLogger(__name__)
class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
"""
Class to manage the editing of a song
"""
global log
log = logging.getLogger(u'EditSongForm')
log.info(u'Song Editor loaded')
log.info(u'%s EditSongForm loaded', __name__)
def __init__(self, songmanager, parent=None):
"""
@ -169,6 +169,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.loadAuthors()
self.loadTopics()
self.loadBooks()
#it's a new song to preview is not possible
self.previewButton.setVisible(False)
def loadSong(self, id, preview):
log.debug(u'Load Song')
@ -316,13 +318,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def onVerseAddButtonClicked(self):
self.verse_form.setVerse(u'', self.VerseListWidget.count() + 1, True)
self.verse_form.exec_()
afterText, verse, subVerse = self.verse_form.getVerse()
data = u'%s:%s' %(verse, subVerse)
item = QtGui.QListWidgetItem(afterText)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
item.setText(afterText)
self.VerseListWidget.addItem(item)
if self.verse_form.exec_():
afterText, verse, subVerse = self.verse_form.getVerse()
data = u'%s:%s' %(verse, subVerse)
item = QtGui.QListWidgetItem(afterText)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
item.setText(afterText)
self.VerseListWidget.addItem(item)
def onVerseEditButtonClicked(self):
item = self.VerseListWidget.currentItem()
@ -331,25 +333,25 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
verseId = unicode((item.data(QtCore.Qt.UserRole)).toString())
self.verse_form.setVerse(tempText, \
self.VerseListWidget.count(), True, verseId)
self.verse_form.exec_()
afterText, verse, subVerse = self.verse_form.getVerse()
data = u'%s:%s' %(verse, subVerse)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
item.setText(afterText)
#number of lines has change so repaint the list moving the data
if len(tempText.split(u'\n')) != len(afterText.split(u'\n')):
tempList = {}
tempId = {}
for row in range(0, self.VerseListWidget.count()):
tempList[row] = self.VerseListWidget.item(row).text()
tempId[row] = self.VerseListWidget.item(row).\
data(QtCore.Qt.UserRole)
self.VerseListWidget.clear()
for row in range (0, len(tempList)):
item = QtGui.QListWidgetItem(tempList[row])
item.setData(QtCore.Qt.UserRole, tempId[row])
self.VerseListWidget.addItem(item)
self.VerseListWidget.repaint()
if self.verse_form.exec_():
afterText, verse, subVerse = self.verse_form.getVerse()
data = u'%s:%s' %(verse, subVerse)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
item.setText(afterText)
#number of lines has change so repaint the list moving the data
if len(tempText.split(u'\n')) != len(afterText.split(u'\n')):
tempList = {}
tempId = {}
for row in range(0, self.VerseListWidget.count()):
tempList[row] = self.VerseListWidget.item(row).text()
tempId[row] = self.VerseListWidget.item(row).\
data(QtCore.Qt.UserRole)
self.VerseListWidget.clear()
for row in range (0, len(tempList)):
item = QtGui.QListWidgetItem(tempList[row])
item.setData(QtCore.Qt.UserRole, tempId[row])
self.VerseListWidget.addItem(item)
self.VerseListWidget.repaint()
self.VerseEditButton.setEnabled(False)
self.VerseDeleteButton.setEnabled(False)
@ -410,7 +412,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.AuthorsListView.setFocus()
#split the verse list by space and mark lower case for testing
for verse in unicode(self.VerseOrderEdit.text()).lower().split(u' '):
if len(verse) == 2:
if len(verse) > 1:
if verse[0:1] == u'v' and verse[1:].isdigit():
pass
else:
@ -533,4 +535,4 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.song.search_title = self.song.search_title.replace(u'{', u'')
self.song.search_title = self.song.search_title.replace(u'}', u'')
self.song.search_title = self.song.search_title.replace(u'?', u'')
self.song.search_title = unicode(self.song.search_title)
self.song.search_title = unicode(self.song.search_title)

View File

@ -1,113 +1,129 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
# Form implementation generated from reading ui file 'editversedialog.ui'
#
# Created: Wed Dec 2 08:14:47 2009
# by: PyQt4 UI code generator 4.6.2
#
# WARNING! All changes made in this file will be lost!
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from PyQt4 import QtCore, QtGui
class Ui_EditVerseDialog(object):
def setupUi(self, EditVerseDialog):
EditVerseDialog.setObjectName("EditVerseDialog")
EditVerseDialog.setObjectName(u'EditVerseDialog')
EditVerseDialog.resize(500, 521)
EditVerseDialog.setModal(True)
self.layoutWidget = QtGui.QWidget(EditVerseDialog)
self.layoutWidget.setGeometry(QtCore.QRect(11, 1, 471, 491))
self.layoutWidget.setObjectName("layoutWidget")
self.layoutWidget.setObjectName(u'layoutWidget')
self.verticalLayout_3 = QtGui.QVBoxLayout(self.layoutWidget)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.verticalLayout_3.setObjectName(u'verticalLayout_3')
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.horizontalLayout.setObjectName(u'horizontalLayout')
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.verticalLayout.setObjectName(u'verticalLayout')
self.VerseTypeLabel = QtGui.QLabel(self.layoutWidget)
self.VerseTypeLabel.setTextFormat(QtCore.Qt.PlainText)
self.VerseTypeLabel.setAlignment(QtCore.Qt.AlignCenter)
self.VerseTypeLabel.setObjectName("VerseTypeLabel")
self.VerseTypeLabel.setObjectName(u'VerseTypeLabel')
self.verticalLayout.addWidget(self.VerseTypeLabel)
self.VerseListComboBox = QtGui.QComboBox(self.layoutWidget)
self.VerseListComboBox.setObjectName("VerseListComboBox")
self.VerseListComboBox.addItem("")
self.VerseListComboBox.addItem("")
self.VerseListComboBox.addItem("")
self.VerseListComboBox.addItem("")
self.VerseListComboBox.addItem("")
self.VerseListComboBox.addItem("")
self.VerseListComboBox.addItem("")
self.VerseListComboBox.setObjectName(u'VerseListComboBox')
self.VerseListComboBox.addItem(u'')
self.VerseListComboBox.addItem(u'')
self.VerseListComboBox.addItem(u'')
self.VerseListComboBox.addItem(u'')
self.VerseListComboBox.addItem(u'')
self.VerseListComboBox.addItem(u'')
self.VerseListComboBox.addItem(u'')
self.verticalLayout.addWidget(self.VerseListComboBox)
self.horizontalLayout.addLayout(self.verticalLayout)
self.verticalLayout_2 = QtGui.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.verticalLayout_2.setObjectName(u'verticalLayout_2')
self.VerseNumberLabel = QtGui.QLabel(self.layoutWidget)
self.VerseNumberLabel.setAlignment(QtCore.Qt.AlignCenter)
self.VerseNumberLabel.setObjectName("VerseNumberLabel")
self.VerseNumberLabel.setObjectName(u'VerseNumberLabel')
self.verticalLayout_2.addWidget(self.VerseNumberLabel)
self.SubVerseListComboBox = QtGui.QComboBox(self.layoutWidget)
self.SubVerseListComboBox.setObjectName("SubVerseListComboBox")
self.SubVerseListComboBox.setObjectName(u'SubVerseListComboBox')
self.verticalLayout_2.addWidget(self.SubVerseListComboBox)
self.horizontalLayout.addLayout(self.verticalLayout_2)
self.verticalLayout_3.addLayout(self.horizontalLayout)
self.VerseTextEdit = QtGui.QTextEdit(self.layoutWidget)
self.VerseTextEdit.setAcceptRichText(False)
self.VerseTextEdit.setObjectName("VerseTextEdit")
self.VerseTextEdit.setObjectName(u'VerseTextEdit')
self.verticalLayout_3.addWidget(self.VerseTextEdit)
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.horizontalLayout_2.setObjectName(u'horizontalLayout_2')
self.addBridge = QtGui.QPushButton(self.layoutWidget)
self.addBridge.setObjectName("addBridge")
self.addBridge.setObjectName(u'addBridge')
self.horizontalLayout_2.addWidget(self.addBridge)
self.addVerse = QtGui.QPushButton(self.layoutWidget)
self.addVerse.setObjectName("addVerse")
self.addVerse.setObjectName(u'addVerse')
self.horizontalLayout_2.addWidget(self.addVerse)
self.addChorus = QtGui.QPushButton(self.layoutWidget)
self.addChorus.setObjectName("addChorus")
self.addChorus.setObjectName(u'addChorus')
self.horizontalLayout_2.addWidget(self.addChorus)
self.verticalLayout_3.addLayout(self.horizontalLayout_2)
self.horizontalLayout_3 = QtGui.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.horizontalLayout_3.setObjectName(u'horizontalLayout_3')
self.addPreChorus = QtGui.QPushButton(self.layoutWidget)
self.addPreChorus.setObjectName("addPreChorus")
self.addPreChorus.setObjectName(u'addPreChorus')
self.horizontalLayout_3.addWidget(self.addPreChorus)
self.addIntro = QtGui.QPushButton(self.layoutWidget)
self.addIntro.setObjectName("addIntro")
self.addIntro.setObjectName(u'addIntro')
self.horizontalLayout_3.addWidget(self.addIntro)
self.addOther = QtGui.QPushButton(self.layoutWidget)
self.addOther.setObjectName("addOther")
self.addOther.setObjectName(u'addOther')
self.horizontalLayout_3.addWidget(self.addOther)
self.addEnding = QtGui.QPushButton(self.layoutWidget)
self.addEnding.setObjectName("addEnding")
self.addEnding.setObjectName(u'addEnding')
self.horizontalLayout_3.addWidget(self.addEnding)
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
self.ButtonBox = QtGui.QDialogButtonBox(self.layoutWidget)
self.ButtonBox.setOrientation(QtCore.Qt.Horizontal)
self.ButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Save)
self.ButtonBox.setObjectName("ButtonBox")
self.ButtonBox.setObjectName(u'ButtonBox')
self.verticalLayout_3.addWidget(self.ButtonBox)
self.retranslateUi(EditVerseDialog)
QtCore.QObject.connect(self.ButtonBox, QtCore.SIGNAL("accepted()"), EditVerseDialog.accept)
QtCore.QObject.connect(self.ButtonBox, QtCore.SIGNAL("rejected()"), EditVerseDialog.reject)
QtCore.QObject.connect(self.ButtonBox, QtCore.SIGNAL(u'accepted()'), EditVerseDialog.accept)
QtCore.QObject.connect(self.ButtonBox, QtCore.SIGNAL(u'rejected()'), EditVerseDialog.reject)
QtCore.QMetaObject.connectSlotsByName(EditVerseDialog)
def retranslateUi(self, EditVerseDialog):
EditVerseDialog.setWindowTitle(QtGui.QApplication.translate("EditVerseDialog", "Edit Verse", None, QtGui.QApplication.UnicodeUTF8))
self.VerseTypeLabel.setText(QtGui.QApplication.translate("EditVerseDialog", "Verse Type", None, QtGui.QApplication.UnicodeUTF8))
self.VerseListComboBox.setItemText(0, QtGui.QApplication.translate("EditVerseDialog", "Intro", None, QtGui.QApplication.UnicodeUTF8))
self.VerseListComboBox.setItemText(1, QtGui.QApplication.translate("EditVerseDialog", "Verse", None, QtGui.QApplication.UnicodeUTF8))
self.VerseListComboBox.setItemText(2, QtGui.QApplication.translate("EditVerseDialog", "Pre-Chorus", None, QtGui.QApplication.UnicodeUTF8))
self.VerseListComboBox.setItemText(3, QtGui.QApplication.translate("EditVerseDialog", "Chorus", None, QtGui.QApplication.UnicodeUTF8))
self.VerseListComboBox.setItemText(4, QtGui.QApplication.translate("EditVerseDialog", "Bridge", None, QtGui.QApplication.UnicodeUTF8))
self.VerseListComboBox.setItemText(5, QtGui.QApplication.translate("EditVerseDialog", "Ending", None, QtGui.QApplication.UnicodeUTF8))
self.VerseListComboBox.setItemText(6, QtGui.QApplication.translate("EditVerseDialog", "Other", None, QtGui.QApplication.UnicodeUTF8))
self.VerseNumberLabel.setText(QtGui.QApplication.translate("EditVerseDialog", "Number", None, QtGui.QApplication.UnicodeUTF8))
self.addBridge.setText(QtGui.QApplication.translate("EditVerseDialog", "Bridge", None, QtGui.QApplication.UnicodeUTF8))
self.addVerse.setText(QtGui.QApplication.translate("EditVerseDialog", "Verse", None, QtGui.QApplication.UnicodeUTF8))
self.addChorus.setText(QtGui.QApplication.translate("EditVerseDialog", "Chorus", None, QtGui.QApplication.UnicodeUTF8))
self.addPreChorus.setText(QtGui.QApplication.translate("EditVerseDialog", "Pre-Chorus", None, QtGui.QApplication.UnicodeUTF8))
self.addIntro.setText(QtGui.QApplication.translate("EditVerseDialog", "Intro", None, QtGui.QApplication.UnicodeUTF8))
self.addOther.setText(QtGui.QApplication.translate("EditVerseDialog", "Other", None, QtGui.QApplication.UnicodeUTF8))
self.addEnding.setText(QtGui.QApplication.translate("EditVerseDialog", "Ending", None, QtGui.QApplication.UnicodeUTF8))
EditVerseDialog.setWindowTitle(self.trUtf8('Edit Verse'))
self.VerseTypeLabel.setText(self.trUtf8('Verse Type'))
self.VerseListComboBox.setItemText(0, self.trUtf8('Intro'))
self.VerseListComboBox.setItemText(1, self.trUtf8('Verse'))
self.VerseListComboBox.setItemText(2, self.trUtf8('Pre-Chorus'))
self.VerseListComboBox.setItemText(3, self.trUtf8('Chorus'))
self.VerseListComboBox.setItemText(4, self.trUtf8('Bridge'))
self.VerseListComboBox.setItemText(5, self.trUtf8('Ending'))
self.VerseListComboBox.setItemText(6, self.trUtf8('Other'))
self.VerseNumberLabel.setText(self.trUtf8('Number'))
self.addBridge.setText(self.trUtf8('Bridge'))
self.addVerse.setText(self.trUtf8('Verse'))
self.addChorus.setText(self.trUtf8('Chorus'))
self.addPreChorus.setText(self.trUtf8('Pre-Chorus'))
self.addIntro.setText(self.trUtf8('Intro'))
self.addOther.setText(self.trUtf8('Other'))
self.addEnding.setText(self.trUtf8('Ending'))

View File

@ -77,6 +77,8 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
def setVerse(self, text, verseCount=0, single=False, tag=u'Verse:1'):
posVerse = 0
posSub = 0
if len(text) == 0 and not single:
text = u'---[Verse:1]---\n'
if single:
id = tag.split(u':')
posVerse = self.VerseListComboBox.findText(id[0], QtCore.Qt.MatchExactly)
@ -112,6 +114,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
self.VerseTextEdit.setPlainText(text)
self.VerseTextEdit.setFocus(QtCore.Qt.OtherFocusReason)
self.onVerseComboChanged(0)
self.VerseTextEdit.moveCursor(QtGui.QTextCursor.Down)
def getVerse(self):
return self.VerseTextEdit.toPlainText(), \
@ -119,11 +122,14 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
unicode(self.SubVerseListComboBox.currentText())
def getVerseAll(self):
return self.VerseTextEdit.toPlainText()
text = self.VerseTextEdit.toPlainText()
if not text.startsWith(u'---['):
text = u'---[Verse:1]---\n%s' % text
return text
def onVerseComboChanged(self, id):
if unicode(self.VerseListComboBox.currentText()) == u'Verse':
self.SubVerseListComboBox.setEnabled(True)
else:
self.SubVerseListComboBox.setEnabled(False)
self.SubVerseListComboBox.setCurrentIndex(0)
self.SubVerseListComboBox.setCurrentIndex(0)

View File

@ -51,10 +51,10 @@ class Ui_SongMaintenanceDialog(object):
self.TypeListWidget.sizePolicy().hasHeightForWidth())
self.TypeListWidget.setSizePolicy(sizePolicy)
self.TypeListWidget.setViewMode(QtGui.QListView.IconMode)
self.TypeListWidget.setIconSize(QtCore.QSize(112, 100));
self.TypeListWidget.setMovement(QtGui.QListView.Static);
self.TypeListWidget.setMaximumWidth(118);
self.TypeListWidget.setSpacing(0);
self.TypeListWidget.setIconSize(QtCore.QSize(112, 100))
self.TypeListWidget.setMovement(QtGui.QListView.Static)
self.TypeListWidget.setMaximumWidth(118)
self.TypeListWidget.setSpacing(0)
self.TypeListWidget.setSortingEnabled(False)
self.TypeListWidget.setUniformItemSizes(True)
self.TypeListWidget.setObjectName(u'TypeListWidget')

View File

@ -139,7 +139,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
else:
QtGui.QMessageBox.critical(
self, self.trUtf8('Error'),
self.trUtf8('Couldn\'t add your author!'),
self.trUtf8('Couldn\'t add your author.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
def onTopicAddButtonClick(self):
@ -150,7 +150,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
else:
QtGui.QMessageBox.critical(
self, self.trUtf8('Error'),
self.trUtf8('Couldn\'t add your topic!'),
self.trUtf8('Couldn\'t add your topic.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
def onBookAddButtonClick(self):
@ -162,7 +162,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
else:
QtGui.QMessageBox.critical(
self, self.trUtf8('Error'),
self.trUtf8('Couldn\'t add your book!'),
self.trUtf8('Couldn\'t add your book.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
def onAuthorEditButtonClick(self):
@ -182,7 +182,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
else:
QtGui.QMessageBox.critical(
self, self.trUtf8('Error'),
self.trUtf8('Couldn\'t save your author!'),
self.trUtf8('Couldn\'t save your author.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
def onTopicEditButtonClick(self):
@ -197,7 +197,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
else:
QtGui.QMessageBox.critical(
self, self.trUtf8('Error'),
self.trUtf8('Couldn\'t save your topic!'),
self.trUtf8('Couldn\'t save your topic.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
def onBookEditButtonClick(self):
@ -214,7 +214,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
else:
QtGui.QMessageBox.critical(
self, self.trUtf8('Error'),
self.trUtf8('Couldn\'t save your book!'),
self.trUtf8('Couldn\'t save your book.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
def onAuthorDeleteButtonClick(self):
@ -227,7 +227,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
self.trUtf8('Delete Author'),
self.trUtf8('Are you sure you want to delete the selected author?'),
self.trUtf8('This author can\'t be deleted, they are currently '
'assigned to at least one song!'),
'assigned to at least one song.'),
self.trUtf8('No author selected!'))
def onTopicDeleteButtonClick(self):
@ -240,7 +240,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
self.trUtf8('Delete Topic'),
self.trUtf8('Are you sure you want to delete the selected topic?'),
self.trUtf8('This topic can\'t be deleted, it is currently '
'assigned to at least one song!'),
'assigned to at least one song.'),
self.trUtf8('No topic selected!'))
def onBookDeleteButtonClick(self):
@ -253,5 +253,5 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
self.trUtf8('Delete Book'),
self.trUtf8('Are you sure you want to delete the selected book?'),
self.trUtf8('This book can\'t be deleted, it is currently '
'assigned to at least one song!'),
self.trUtf8('No book selected!'))
'assigned to at least one song.'),
self.trUtf8('No book selected!'))

View File

@ -28,14 +28,13 @@ import logging
from openlp.plugins.songs.lib.models import init_models, metadata, Song, \
Author, Topic, Book
log = logging.getLogger(__name__)
class SongManager():
"""
The Song Manager provides a central location for all database code. This
class takes care of connecting to the database and running all the queries.
"""
global log
log = logging.getLogger(u'SongManager')
log.info(u'Song manager loaded')
def __init__(self, config):
@ -238,3 +237,6 @@ class SongManager():
self.session.rollback()
log.exception(u'Could not delete book from song database')
return False
def get_songs_for_theme(self, theme):
return self.session.query(Song).filter(Song.theme_name == theme).all()

View File

@ -31,6 +31,8 @@ from openlp.core.lib import MediaManagerItem, SongXMLParser, \
BaseListWithDnD, Receiver, str_to_bool
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm
log = logging.getLogger(__name__)
class SongListView(BaseListWithDnD):
def __init__(self, parent=None):
self.PluginName = u'Songs'
@ -40,8 +42,6 @@ class SongMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Songs.
"""
global log
log = logging.getLogger(u'SongMediaItem')
log.info(u'Song Media Item loaded')
def __init__(self, parent, icon, title):
@ -185,8 +185,13 @@ class SongMediaItem(MediaManagerItem):
if author_list != u'':
author_list = author_list + u', '
author_list = author_list + author.display_name
song_detail = unicode(self.trUtf8('%s (%s)' % \
(unicode(song.title), unicode(author_list))))
if not isinstance(author_list, unicode):
author_list = unicode(author_list, u'utf8')
if isinstance(song.title, unicode):
song_title = song.title
else:
song_title = unicode(song.title, u'utf8')
song_detail = u'%s (%s)' % (song_title, author_list)
song_name = QtGui.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id))
self.ListView.addItem(song_name)
@ -285,6 +290,7 @@ class SongMediaItem(MediaManagerItem):
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
else:
item_id = self.remoteSong
service_item.autoPreviewAllowed = True
song = self.parent.songmanager.get_song(item_id)
service_item.theme = song.theme_name
service_item.edit_enabled = True
@ -305,7 +311,7 @@ class SongMediaItem(MediaManagerItem):
for verse in verseList:
if verse[1]:
if verse[0][u'type'] == "Verse":
if verse[0][u'label'][0] == order[1:]:
if verse[0][u'label'] == order[1:]:
verseTag = u'%s:%s' % \
(verse[0][u'type'], verse[0][u'label'])
service_item.add_from_text\
@ -339,4 +345,4 @@ class SongMediaItem(MediaManagerItem):
service_item.audit = [
song.title, author_audit, song.copyright, song.ccli_number
]
return True
return True

Some files were not shown because too many files have changed in this diff Show More