This commit is contained in:
Andreas Preikschat 2011-08-20 20:50:29 +02:00
commit 418802b735
97 changed files with 1998 additions and 1294 deletions

View File

@ -20,3 +20,4 @@ _eric4project
openlp/core/resources.py.old
*.qm
resources/windows/warnOpenLP.txt
openlp.cfg

View File

@ -79,6 +79,8 @@ class OpenLP(QtGui.QApplication):
class in order to provide the core of the application.
"""
args = []
def exec_(self):
"""
Override exec method to allow the shared memory to be released on exit
@ -92,7 +94,7 @@ class OpenLP(QtGui.QApplication):
"""
# On Windows, the args passed into the constructor are
# ignored. Not very handy, so set the ones we want to use.
self.args = args
self.args.extend(args)
# provide a listener for widgets to reqest a screen update.
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_process_events'), self.processEvents)
@ -125,6 +127,8 @@ class OpenLP(QtGui.QApplication):
# now kill the splashscreen
self.splash.finish(self.mainWindow)
log.debug(u'Splashscreen closed')
# make sure Qt really display the splash screen
self.processEvents()
self.mainWindow.repaint()
self.processEvents()
if not has_run_wizard:
@ -180,6 +184,18 @@ class OpenLP(QtGui.QApplication):
"""
self.restoreOverrideCursor()
def event(self, event):
"""
Enables direct file opening on OS X
"""
if event.type() == QtCore.QEvent.FileOpen:
file_name = event.file()
log.debug(u'Got open file event for %s!', file_name)
self.args.insert(0, unicode(file_name))
return True
else:
return QtGui.QApplication.event(self, event)
def main():
"""
The main function which parses command line options and then runs

View File

@ -205,7 +205,7 @@ def clean_tags(text):
text = text.replace(u'<br>', u'\n')
text = text.replace(u'{br}', u'\n')
text = text.replace(u'&nbsp;', u' ')
for tag in DisplayTags.get_html_tags():
for tag in FormattingTags.get_html_tags():
text = text.replace(tag[u'start tag'], u'')
text = text.replace(tag[u'end tag'], u'')
return text
@ -214,7 +214,7 @@ def expand_tags(text):
"""
Expand tags HTML for display
"""
for tag in DisplayTags.get_html_tags():
for tag in FormattingTags.get_html_tags():
text = text.replace(tag[u'start tag'], tag[u'start html'])
text = text.replace(tag[u'end tag'], tag[u'end html'])
return text
@ -233,9 +233,9 @@ def check_directory_exists(dir):
except IOError:
pass
from listwidgetwithdnd import ListWidgetWithDnD
from displaytags import DisplayTags
from eventreceiver import Receiver
from listwidgetwithdnd import ListWidgetWithDnD
from formattingtags import FormattingTags
from spelltextedit import SpellTextEdit
from settingsmanager import SettingsManager
from plugin import PluginStatus, StringContent, Plugin

View File

@ -25,12 +25,12 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Provide Html Tag management and Display Tag access class
Provide HTML Tag management and Formatting Tag access class
"""
from openlp.core.lib import translate
class DisplayTags(object):
class FormattingTags(object):
"""
Static Class to HTML Tags to be access around the code the list is managed
by the Options Tab.
@ -42,89 +42,93 @@ class DisplayTags(object):
"""
Provide access to the html_expands list.
"""
return DisplayTags.html_expands
return FormattingTags.html_expands
@staticmethod
def reset_html_tags():
"""
Resets the html_expands list.
"""
DisplayTags.html_expands = []
FormattingTags.html_expands = []
base_tags = []
# Append the base tags.
# Hex Color tags from http://www.w3schools.com/html/html_colornames.asp
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Red'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Red'),
u'start tag': u'{r}',
u'start html': u'<span style="-webkit-text-fill-color:red">',
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Black'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Black'),
u'start tag': u'{b}',
u'start html': u'<span style="-webkit-text-fill-color:black">',
u'end tag': u'{/b}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Blue'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Blue'),
u'start tag': u'{bl}',
u'start html': u'<span style="-webkit-text-fill-color:blue">',
u'end tag': u'{/bl}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Yellow'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Yellow'),
u'start tag': u'{y}',
u'start html': u'<span style="-webkit-text-fill-color:yellow">',
u'end tag': u'{/y}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Green'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Green'),
u'start tag': u'{g}',
u'start html': u'<span style="-webkit-text-fill-color:green">',
u'end tag': u'{/g}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Pink'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Pink'),
u'start tag': u'{pk}',
u'start html': u'<span style="-webkit-text-fill-color:#FFC0CB">',
u'end tag': u'{/pk}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Orange'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Orange'),
u'start tag': u'{o}',
u'start html': u'<span style="-webkit-text-fill-color:#FFA500">',
u'end tag': u'{/o}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Purple'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Purple'),
u'start tag': u'{pp}',
u'start html': u'<span style="-webkit-text-fill-color:#800080">',
u'end tag': u'{/pp}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'White'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'White'),
u'start tag': u'{w}',
u'start html': u'<span style="-webkit-text-fill-color:white">',
u'end tag': u'{/w}', u'end html': u'</span>', u'protected': True})
base_tags.append({
u'desc': translate('OpenLP.DisplayTags', 'Superscript'),
u'desc': translate('OpenLP.FormattingTags', 'Superscript'),
u'start tag': u'{su}', u'start html': u'<sup>',
u'end tag': u'{/su}', u'end html': u'</sup>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Subscript'),
base_tags.append({
u'desc': translate('OpenLP.FormattingTags', 'Subscript'),
u'start tag': u'{sb}', u'start html': u'<sub>',
u'end tag': u'{/sb}', u'end html': u'</sub>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Paragraph'),
base_tags.append({
u'desc': translate('OpenLP.FormattingTags', 'Paragraph'),
u'start tag': u'{p}', u'start html': u'<p>', u'end tag': u'{/p}',
u'end html': u'</p>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Bold'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Bold'),
u'start tag': u'{st}', u'start html': u'<strong>',
u'end tag': u'{/st}', u'end html': u'</strong>',
u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Italics'),
base_tags.append({
u'desc': translate('OpenLP.FormattingTags', 'Italics'),
u'start tag': u'{it}', u'start html': u'<em>', u'end tag': u'{/it}',
u'end html': u'</em>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Underline'),
base_tags.append({
u'desc': translate('OpenLP.FormattingTags', 'Underline'),
u'start tag': u'{u}',
u'start html': u'<span style="text-decoration: underline;">',
u'end tag': u'{/u}', u'end html': u'</span>', u'protected': True})
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Break'),
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Break'),
u'start tag': u'{br}', u'start html': u'<br>', u'end tag': u'',
u'end html': u'', u'protected': True})
DisplayTags.add_html_tags(base_tags)
FormattingTags.add_html_tags(base_tags)
@staticmethod
def add_html_tags(tags):
"""
Add a list of tags to the list
"""
DisplayTags.html_expands.extend(tags)
FormattingTags.html_expands.extend(tags)
@staticmethod
def remove_html_tag(tag_id):
"""
Removes an individual html_expands tag.
"""
DisplayTags.html_expands.pop(tag_id)
FormattingTags.html_expands.pop(tag_id)

View File

@ -34,6 +34,7 @@ from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, \
log = logging.getLogger(__name__)
# FIXME: Add html5 doctype. However, do not break theme gradients.
HTMLSRC = u"""
<html>
<head>
@ -56,44 +57,44 @@ body {
height: %spx;
}
#black {
z-index:8;
z-index: 8;
background-color: black;
display: none;
}
#bgimage {
z-index:1;
z-index: 1;
}
#image {
z-index:2;
z-index: 2;
}
#video1 {
z-index:3;
z-index: 3;
}
#video2 {
z-index:3;
z-index: 3;
}
#alert {
position: absolute;
left: 0px;
top: 0px;
z-index:10;
z-index: 10;
%s
}
#footer {
position: absolute;
z-index:6;
z-index: 6;
%s
}
/* lyric css */
%s
sup {
font-size:0.6em;
vertical-align:top;
position:relative;
top:-0.3em;
font-size: 0.6em;
vertical-align: top;
position: relative;
top: -0.3em;
}
</style>
<script language="javascript">
<script>
var timer = null;
var video_timer = null;
var current_video = '1';
@ -317,10 +318,10 @@ sup {
%s
<div id="footer" class="footer"></div>
<div id="black" class="size"></div>
<div id="alert" style="visibility:hidden;"></div>
<div id="alert" style="visibility:hidden"></div>
</body>
</html>
"""
"""
def build_html(item, screen, alert, islive, background, image=None):
"""
@ -446,15 +447,15 @@ def build_lyrics_css(item, webkitvers):
The version of qtwebkit we're using
"""
style = """
style = u"""
.lyricstable {
z-index:5;
z-index: 5;
position: absolute;
display: table;
%s
}
.lyricscell {
display:table-cell;
display: table-cell;
word-wrap: break-word;
%s
}
@ -558,8 +559,8 @@ def build_lyrics_format_css(theme, width, height):
left_margin = 0
lyrics = u'white-space:pre-wrap; word-wrap: break-word; ' \
'text-align: %s; vertical-align: %s; font-family: %s; ' \
'font-size: %spt; color: %s; line-height: %d%%; margin:0;' \
'padding:0; padding-left:%spx; width: %spx; height: %spx; ' % \
'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \
'padding: 0; padding-left: %spx; width: %spx; height: %spx; ' % \
(align, valign, theme.font_main_name, theme.font_main_size,
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
left_margin, width, height)
@ -608,7 +609,7 @@ def build_footer_css(item, height):
``item``
Service Item to be processed.
"""
style = """
style = u"""
left: %spx;
bottom: %spx;
width: %spx;
@ -616,7 +617,7 @@ def build_footer_css(item, height):
font-size: %spt;
color: %s;
text-align: left;
white-space:nowrap;
white-space: nowrap;
"""
theme = item.themedata
if not theme or not item.footer:
@ -634,7 +635,7 @@ def build_alert_css(alertTab, width):
``alertTab``
Details from the Alert tab for fonts etc
"""
style = """
style = u"""
width: %spx;
vertical-align: %s;
font-family: %s;

View File

@ -32,6 +32,7 @@ to wait for the conversion to happen.
"""
import logging
import time
import Queue
from PyQt4 import QtCore
@ -42,8 +43,8 @@ log = logging.getLogger(__name__)
class ImageThread(QtCore.QThread):
"""
A special Qt thread class to speed up the display of text based frames.
This is threaded so it loads the frames in background
A special Qt thread class to speed up the display of images. This is
threaded so it loads the frames and generates byte stream in background.
"""
def __init__(self, manager):
QtCore.QThread.__init__(self, None)
@ -53,15 +54,87 @@ class ImageThread(QtCore.QThread):
"""
Run the thread.
"""
self.imageManager.process()
self.imageManager._process()
class Priority(object):
"""
Enumeration class for different priorities.
``Lowest``
Only the image's byte stream has to be generated. But neither the
``QImage`` nor the byte stream has been requested yet.
``Low``
Only the image's byte stream has to be generated. Because the image's
``QImage`` has been requested previously it is reasonable to assume that
the byte stream will be needed before the byte stream of other images
whose ``QImage`` were not generated due to a request.
``Normal``
The image's byte stream as well as the image has to be generated.
Neither the ``QImage`` nor the byte stream has been requested yet.
``High``
The image's byte stream as well as the image has to be generated. The
``QImage`` for this image has been requested.
**Note**, this priority is only set when the ``QImage`` has not been
generated yet.
``Urgent``
The image's byte stream as well as the image has to be generated. The
byte stream for this image has been requested.
**Note**, this priority is only set when the byte stream has not been
generated yet.
"""
Lowest = 4
Low = 3
Normal = 2
High = 1
Urgent = 0
class Image(object):
name = ''
path = ''
dirty = True
image = None
image_bytes = None
"""
This class represents an image. To mark an image as *dirty* set the instance
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
to the queue of images to process.
"""
def __init__(self, name='', path=''):
self.name = name
self.path = path
self.image = None
self.image_bytes = None
self.priority = Priority.Normal
class PriorityQueue(Queue.PriorityQueue):
"""
Customised ``Queue.PriorityQueue``.
"""
def modify_priority(self, image, new_priority):
"""
Modifies the priority of the given ``image``.
``image``
The image to remove. This should be an ``Image`` instance.
``new_priority``
The image's new priority.
"""
self.remove(image)
image.priority = new_priority
self.put((image.priority, image))
def remove(self, image):
"""
Removes the given ``image`` from the queue.
``image``
The image to remove. This should be an ``Image`` instance.
"""
if (image.priority, image) in self.queue:
self.queue.remove((image.priority, image))
class ImageManager(QtCore.QObject):
@ -76,96 +149,117 @@ class ImageManager(QtCore.QObject):
self.width = current_screen[u'size'].width()
self.height = current_screen[u'size'].height()
self._cache = {}
self._thread_running = False
self._cache_dirty = False
self.image_thread = ImageThread(self)
self._imageThread = ImageThread(self)
self._conversion_queue = PriorityQueue()
def update_display(self):
"""
Screen has changed size so rebuild the cache to new size
Screen has changed size so rebuild the cache to new size.
"""
log.debug(u'update_display')
current_screen = ScreenList.get_instance().current
self.width = current_screen[u'size'].width()
self.height = current_screen[u'size'].height()
# mark the images as dirty for a rebuild
for key in self._cache.keys():
image = self._cache[key]
image.dirty = True
image.image = resize_image(image.path, self.width, self.height)
self._cache_dirty = True
# only one thread please
if not self._thread_running:
self.image_thread.start()
# Mark the images as dirty for a rebuild by setting the image and byte
# stream to None.
self._conversion_queue = PriorityQueue()
for key, image in self._cache.iteritems():
image.priority = Priority.Normal
image.image = None
image.image_bytes = None
self._conversion_queue.put((image.priority, image))
# We want only one thread.
if not self._imageThread.isRunning():
self._imageThread.start()
def get_image(self, name):
"""
Return the Qimage from the cache
Return the ``QImage`` from the cache. If not present wait for the
background thread to process it.
"""
log.debug(u'get_image %s' % name)
return self._cache[name].image
image = self._cache[name]
if image.image is None:
self._conversion_queue.modify_priority(image, Priority.High)
while image.image is None:
log.debug(u'get_image - waiting')
time.sleep(0.1)
elif image.image_bytes is None:
# Set the priority to Low, because the image was requested but the
# byte stream was not generated yet. However, we only need to do
# this, when the image was generated before it was requested
# (otherwise this is already taken care of).
self._conversion_queue.modify_priority(image, Priority.Low)
return image.image
def get_image_bytes(self, name):
"""
Returns the byte string for an image
If not present wait for the background thread to process it.
Returns the byte string for an image. If not present wait for the
background thread to process it.
"""
log.debug(u'get_image_bytes %s' % name)
if not self._cache[name].image_bytes:
while self._cache[name].dirty:
image = self._cache[name]
if image.image_bytes is None:
self._conversion_queue.modify_priority(image, Priority.Urgent)
while image.image_bytes is None:
log.debug(u'get_image_bytes - waiting')
time.sleep(0.1)
return self._cache[name].image_bytes
return image.image_bytes
def del_image(self, name):
"""
Delete the Image from the Cache
Delete the Image from the cache.
"""
log.debug(u'del_image %s' % name)
if name in self._cache:
self._conversion_queue.remove(self._cache[name])
del self._cache[name]
def add_image(self, name, path):
"""
Add image to cache if it is not already there
Add image to cache if it is not already there.
"""
log.debug(u'add_image %s:%s' % (name, path))
if not name in self._cache:
image = Image()
image.name = name
image.path = path
image.image = resize_image(path, self.width, self.height)
image = Image(name, path)
self._cache[name] = image
self._conversion_queue.put((image.priority, image))
else:
log.debug(u'Image in cache %s:%s' % (name, path))
self._cache_dirty = True
# only one thread please
if not self._thread_running:
self.image_thread.start()
# We want only one thread.
if not self._imageThread.isRunning():
self._imageThread.start()
def process(self):
def _process(self):
"""
Controls the processing called from a QThread
Controls the processing called from a ``QtCore.QThread``.
"""
log.debug(u'process - started')
self._thread_running = True
self.clean_cache()
# data loaded since we started ?
while self._cache_dirty:
log.debug(u'process - recycle')
self.clean_cache()
self._thread_running = False
log.debug(u'process - ended')
log.debug(u'_process - started')
while not self._conversion_queue.empty():
self._process_cache()
log.debug(u'_process - ended')
def clean_cache(self):
def _process_cache(self):
"""
Actually does the work.
"""
log.debug(u'clean_cache')
# we will clean the cache now
self._cache_dirty = False
for key in self._cache.keys():
image = self._cache[key]
if image.dirty:
image.image_bytes = image_to_byte(image.image)
image.dirty = False
log.debug(u'_process_cache')
image = self._conversion_queue.get()[1]
# Generate the QImage for the image.
if image.image is None:
image.image = resize_image(image.path, self.width, self.height)
# Set the priority to Lowest and stop here as we need to process
# more important images first.
if image.priority == Priority.Normal:
self._conversion_queue.modify_priority(image, Priority.Lowest)
return
# For image with high priority we set the priority to Low, as the
# byte stream might be needed earlier the byte stream of image with
# Normal priority. We stop here as we need to process more important
# images first.
elif image.priority == Priority.High:
self._conversion_queue.modify_priority(image, Priority.Low)
return
# Generate the byte stream for the image.
if image.image_bytes is None:
image.image_bytes = image_to_byte(image.image)

View File

@ -27,8 +27,12 @@
"""
Extend QListWidget to handle drag and drop functionality
"""
import os.path
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver
class ListWidgetWithDnD(QtGui.QListWidget):
"""
Provide a list widget to store objects and handle drag and drop events
@ -41,6 +45,16 @@ class ListWidgetWithDnD(QtGui.QListWidget):
self.mimeDataText = name
assert(self.mimeDataText)
def activateDnD(self):
"""
Activate DnD of widget
"""
self.setAcceptDrops(True)
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText),
self.parent().loadFile)
def mouseMoveEvent(self, event):
"""
Drag and drop event does not care what data is selected
@ -58,3 +72,39 @@ class ListWidgetWithDnD(QtGui.QListWidget):
drag.setMimeData(mimeData)
mimeData.setText(self.mimeDataText)
drag.start(QtCore.Qt.CopyAction)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
event.ignore()
def dropEvent(self, event):
"""
Receive drop event check if it is a file and process it if it is.
``event``
Handle of the event pint passed
"""
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
files = []
for url in event.mimeData().urls():
localFile = unicode(url.toLocalFile())
if os.path.isfile(localFile):
files.append(localFile)
elif os.path.isdir(localFile):
listing = os.listdir(localFile)
for file in listing:
files.append(os.path.join(localFile,file))
Receiver.send_message(u'%s_dnd' % self.mimeDataText,files)
else:
event.ignore()

View File

@ -96,7 +96,7 @@ class MediaManagerItem(QtGui.QWidget):
self.plugin = plugin
visible_title = self.plugin.getString(StringContent.VisibleName)
self.title = unicode(visible_title[u'title'])
self.settingsSection = self.plugin.name.lower()
self.settingsSection = self.plugin.name
self.icon = None
if icon:
self.icon = build_icon(icon)
@ -113,7 +113,7 @@ class MediaManagerItem(QtGui.QWidget):
self.retranslateUi()
self.auto_select_id = -1
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name.lower()),
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name),
self.serviceLoad)
def requiredIcons(self):
@ -252,7 +252,6 @@ class MediaManagerItem(QtGui.QWidget):
self.listView.setSelectionMode(
QtGui.QAbstractItemView.ExtendedSelection)
self.listView.setAlternatingRowColors(True)
self.listView.setDragEnabled(True)
self.listView.setObjectName(u'%sListView' % self.plugin.name)
# Add to pageLayout
self.pageLayout.addWidget(self.listView)
@ -288,6 +287,7 @@ class MediaManagerItem(QtGui.QWidget):
self.listView, u':/general/general_add.png',
translate('OpenLP.MediaManagerItem',
'&Add to selected Service Item'), self.onAddEditClick)
self.addCustomContextActions()
# Create the context menu and add all actions from the listView.
self.menu = QtGui.QMenu()
self.menu.addActions(self.listView.actions())
@ -301,6 +301,13 @@ class MediaManagerItem(QtGui.QWidget):
QtCore.SIGNAL('customContextMenuRequested(QPoint)'),
self.contextMenu)
def addCustomContextActions(self):
"""
Implement this method in your descendent media manager item to
add any context menu items. This method is called automatically.
"""
pass
def initialise(self):
"""
Implement this method in your descendent media manager item to
@ -331,26 +338,65 @@ class MediaManagerItem(QtGui.QWidget):
log.info(u'New files(s) %s', unicode(files))
if files:
Receiver.send_message(u'cursor_busy')
names = []
for count in range(0, self.listView.count()):
names.append(self.listView.item(count).text())
newFiles = []
for file in files:
filename = os.path.split(unicode(file))[1]
if filename in names:
self.validateAndLoad(files)
Receiver.send_message(u'cursor_normal')
def loadFile(self, files):
"""
Turn file from Drag and Drop into an array so the Validate code
can run it.
``files``
The list of files to be loaded
"""
newFiles = []
errorShown = False
for file in files:
type = file.split(u'.')[-1]
if type.lower() not in self.onNewFileMasks:
if not errorShown:
critical_error_message_box(
UiStrings().Duplicate,
translate('OpenLP.MediaManagerItem',
'Invalid File Type'),
unicode(translate('OpenLP.MediaManagerItem',
'Duplicate filename %s.\nThis filename is already in '
'the list')) % filename)
else:
newFiles.append(file)
'Invalid File %s.\nSuffix not supported'))
% file)
errorShown = True
else:
newFiles.append(file)
if file:
self.validateAndLoad(newFiles)
def validateAndLoad(self, files):
"""
Process a list for files either from the File Dialog or from Drag and
Drop
``files``
The files to be loaded
"""
names = []
for count in range(0, self.listView.count()):
names.append(self.listView.item(count).text())
newFiles = []
duplicatesFound = False
for file in files:
filename = os.path.split(unicode(file))[1]
if filename in names:
duplicatesFound = True
else:
newFiles.append(file)
if newFiles:
self.loadList(newFiles)
lastDir = os.path.split(unicode(files[0]))[0]
SettingsManager.set_last_dir(self.settingsSection, lastDir)
SettingsManager.set_list(self.settingsSection,
self.settingsSection, self.getFileList())
Receiver.send_message(u'cursor_normal')
if duplicatesFound:
critical_error_message_box(
UiStrings().Duplicate,
unicode(translate('OpenLP.MediaManagerItem',
'Duplicate files found on import and ignored.')))
def contextMenu(self, point):
item = self.listView.itemAt(point)
@ -550,7 +596,7 @@ class MediaManagerItem(QtGui.QWidget):
QtGui.QMessageBox.information(self, UiStrings().NISs,
translate('OpenLP.MediaManagerItem',
'You must select an existing service item to add to.'))
elif self.plugin.name.lower() == serviceItem.name.lower():
elif self.plugin.name == serviceItem.name:
self.generateSlideData(serviceItem)
self.plugin.serviceManager.addServiceItem(serviceItem,
replace=True)

View File

@ -152,7 +152,7 @@ class Plugin(QtCore.QObject):
self.version = version
else:
self.version = get_application_version()[u'version']
self.settingsSection = self.name.lower()
self.settingsSection = self.name
self.icon = None
self.media_item_class = media_item_class
self.settings_tab_class = settings_tab_class

View File

@ -31,7 +31,7 @@ import os
import sys
import logging
from openlp.core.lib import Plugin, StringContent, PluginStatus
from openlp.core.lib import Plugin, PluginStatus
log = logging.getLogger(__name__)

View File

@ -46,8 +46,6 @@ VERSE = u'The Lord said to {r}Noah{/r}: \n' \
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
HTML_END = u'</div></body></html>'
class Renderer(object):
"""
Class to pull all Renderer interactions into one place. The plugins will
@ -56,20 +54,20 @@ class Renderer(object):
"""
log.info(u'Renderer Loaded')
def __init__(self, image_manager, theme_manager):
def __init__(self, imageManager, themeManager):
"""
Initialise the render manager.
``image_manager``
``imageManager``
A ImageManager instance which takes care of e. g. caching and resizing
images.
``theme_manager``
``themeManager``
The ThemeManager instance, used to get the current theme details.
"""
log.debug(u'Initialisation started')
self.theme_manager = theme_manager
self.image_manager = image_manager
self.themeManager = themeManager
self.imageManager = imageManager
self.screens = ScreenList.get_instance()
self.service_theme = u''
self.theme_level = u''
@ -77,7 +75,7 @@ class Renderer(object):
self.theme_data = None
self.bg_frame = None
self.force_page = False
self.display = MainDisplay(None, self.image_manager, False)
self.display = MainDisplay(None, self.imageManager, False)
self.display.setup()
def update_display(self):
@ -85,10 +83,10 @@ class Renderer(object):
Updates the render manager's information about the current screen.
"""
log.debug(u'Update Display')
self._calculate_default(self.screens.current[u'size'])
self._calculate_default()
if self.display:
self.display.close()
self.display = MainDisplay(None, self.image_manager, False)
self.display = MainDisplay(None, self.imageManager, False)
self.display.setup()
self.bg_frame = None
self.theme_data = None
@ -101,14 +99,14 @@ class Renderer(object):
The global-level theme to be set.
``theme_level``
Defaults to *``ThemeLevel.Global``*. The theme level, can be
Defaults to ``ThemeLevel.Global``. The theme level, can be
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
``ThemeLevel.Song``.
"""
self.global_theme = global_theme
self.theme_level = theme_level
self.global_theme_data = \
self.theme_manager.getThemeData(self.global_theme)
self.themeManager.getThemeData(self.global_theme)
self.theme_data = None
def set_service_theme(self, service_theme):
@ -162,12 +160,12 @@ class Renderer(object):
if override_levels:
self.theme_data = override_theme
else:
self.theme_data = self.theme_manager.getThemeData(theme)
self._calculate_default(self.screens.current[u'size'])
self.theme_data = self.themeManager.getThemeData(theme)
self._calculate_default()
self._build_text_rectangle(self.theme_data)
# if No file do not update cache
if self.theme_data.background_filename:
self.image_manager.add_image(self.theme_data.theme_name,
self.imageManager.add_image(self.theme_data.theme_name,
self.theme_data.background_filename)
return self._rect, self._rect_footer
@ -185,7 +183,7 @@ class Renderer(object):
# save value for use in format_slide
self.force_page = force_page
# set the default image size for previews
self._calculate_default(self.screens.preview[u'size'])
self._calculate_default()
# build a service item to generate preview
serviceItem = ServiceItem()
serviceItem.theme = theme_data
@ -193,7 +191,7 @@ class Renderer(object):
# make big page for theme edit dialog to get line count
serviceItem.add_from_text(u'', VERSE + VERSE + VERSE)
else:
self.image_manager.del_image(theme_data.theme_name)
self.imageManager.del_image(theme_data.theme_name)
serviceItem.add_from_text(u'', VERSE)
serviceItem.renderer = self
serviceItem.raw_footer = FOOTER
@ -203,52 +201,58 @@ class Renderer(object):
raw_html = serviceItem.get_rendered_frame(0)
preview = self.display.text(raw_html)
# Reset the real screen size for subsequent render requests
self._calculate_default(self.screens.current[u'size'])
self._calculate_default()
return preview
self.force_page = False
def format_slide(self, text, line_break, item):
def format_slide(self, text, item):
"""
Calculate how much text can fit on a slide.
``text``
The words to go on the slides.
``line_break``
Add line endings after each line of text used for bibles.
``item``
The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
"""
log.debug(u'format slide')
# clean up line endings
lines = self._lines_split(text)
pages = self._paginate_slide(lines, line_break, self.force_page)
if len(pages) > 1:
# Songs and Custom
if item.is_capable(ItemCapabilities.AllowsVirtualSplit):
# Do not forget the line breaks !
slides = text.split(u'[---]')
pages = []
for slide in slides:
lines = slide.strip(u'\n').split(u'\n')
new_pages = self._paginate_slide(lines, line_break,
self.force_page)
pages.extend(new_pages)
# Bibles
elif item.is_capable(ItemCapabilities.AllowsWordSplit):
pages = self._paginate_slide_words(text, line_break)
return pages
# Add line endings after each line of text used for bibles.
line_end = u'<br>'
if item.is_capable(ItemCapabilities.NoLineBreaks):
line_end = u' '
# Bibles
if item.is_capable(ItemCapabilities.AllowsWordSplit):
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
else:
# Clean up line endings.
lines = self._lines_split(text)
pages = self._paginate_slide(lines, line_end)
if len(pages) > 1:
# Songs and Custom
if item.is_capable(ItemCapabilities.AllowsVirtualSplit):
# Do not forget the line breaks!
slides = text.split(u'[---]')
pages = []
for slide in slides:
lines = slide.strip(u'\n').split(u'\n')
pages.extend(self._paginate_slide(lines, line_end))
new_pages = []
for page in pages:
while page.endswith(u'<br>'):
page = page[:-4]
new_pages.append(page)
return new_pages
def _calculate_default(self, screen):
def _calculate_default(self):
"""
Calculate the default dimentions of the screen.
``screen``
The QSize of the screen.
"""
log.debug(u'_calculate default %s', screen)
self.width = screen.width()
self.height = screen.height()
screen_size = self.screens.current[u'size']
self.width = screen_size.width()
self.height = screen_size.height()
self.screen_ratio = float(self.height) / float(self.width)
log.debug(u'calculate default %d, %d, %f',
self.width, self.height, self.screen_ratio)
log.debug(u'_calculate default %s, %f' % (screen_size,
self.screen_ratio))
# 90% is start of footer
self.footer_start = int(self.height * 0.90)
@ -301,177 +305,202 @@ class Renderer(object):
self.web.resize(self.page_width, self.page_height)
self.web_frame = self.web.page().mainFrame()
# Adjust width and height to account for shadow. outline done in css
self.page_shell = u'<html><head><style>' \
u'*{margin: 0; padding: 0; border: 0;} '\
u'#main {position:absolute; top:0px; %s %s}</style></head><body>' \
u'<div id="main">' % \
html = u"""<!DOCTYPE html><html><head><script>
function show_text(newtext) {
var main = document.getElementById('main');
main.innerHTML = newtext;
// We need to be sure that the page is loaded, that is why we
// return the element's height (even though we do not use the
// returned value).
return main.offsetHeight;
}
</script><style>*{margin: 0; padding: 0; border: 0;}
#main {position: absolute; top: 0px; %s %s}</style></head><body>
<div id="main"></div></body></html>""" % \
(build_lyrics_format_css(self.theme_data, self.page_width,
self.page_height), build_lyrics_outline_css(self.theme_data))
self.web.setHtml(html)
def _paginate_slide(self, lines, line_break, force_page=False):
def _paginate_slide(self, lines, line_end):
"""
Figure out how much text can appear on a slide, using the current
theme settings.
**Note:** The smallest possible "unit" of text for a slide is one line.
If the line is too long it will be cut off when displayed.
``lines``
The words to be fitted on the slide split into lines.
``line_break``
Add line endings after each line of text (used for bibles).
``force_page``
Flag to tell message lines in page.
The text to be fitted on the slide split into lines.
``line_end``
The text added after each line. Either ``u' '`` or ``u'<br>``.
"""
log.debug(u'_paginate_slide - Start')
line_end = u''
if line_break:
line_end = u'<br>'
formatted = []
html_text = u''
styled_text = u''
line_count = 0
for line in lines:
if line_count != -1:
line_count += 1
styled_line = expand_tags(line) + line_end
styled_text += styled_line
html = self.page_shell + styled_text + HTML_END
self.web.setHtml(html)
# Text too long so go to next page.
if self.web_frame.contentsSize().height() > self.page_height:
if force_page and line_count > 0:
Receiver.send_message(u'theme_line_count', line_count - 1)
line_count = -1
while html_text.endswith(u'<br>'):
html_text = html_text[:-4]
formatted.append(html_text)
html_text = u''
styled_text = styled_line
html_text += line + line_end
while html_text.endswith(u'<br>'):
html_text = html_text[:-4]
formatted.append(html_text)
log.debug(u'_paginate_slide - End')
return formatted
def _paginate_slide_words(self, text, line_break):
"""
Figure out how much text can appear on a slide, using the current
theme settings. This version is to handle text which needs to be split
into words to get it to fit.
``text``
The words to be fitted on the slide split into lines.
``line_break``
Add line endings after each line of text used for bibles.
"""
log.debug(u'_paginate_slide_words - Start')
line_end = u' '
if line_break:
line_end = u'<br>'
formatted = []
previous_html = u''
previous_raw = u''
lines = text.split(u'\n')
separator = u'<br>'
html_lines = map(expand_tags, lines)
# Text too long so go to next page.
if self._text_fits_on_slide(separator.join(html_lines)):
html_text, previous_raw = self._binary_chop(formatted,
previous_html, previous_raw, html_lines, lines, separator, u'')
else:
previous_raw = separator.join(lines)
if previous_raw:
formatted.append(previous_raw)
log.debug(u'_paginate_slide - End')
return formatted
def _paginate_slide_words(self, lines, line_end):
"""
Figure out how much text can appear on a slide, using the current
theme settings.
**Note:** The smallest possible "unit" of text for a slide is one word.
If one line is too long it will be processed word by word. This is
sometimes need for **bible** verses.
``lines``
The text to be fitted on the slide split into lines.
``line_end``
The text added after each line. Either ``u' '`` or ``u'<br>``.
This is needed for **bibles**.
"""
log.debug(u'_paginate_slide_words - Start')
formatted = []
previous_html = u''
previous_raw = u''
for line in lines:
line = line.strip()
styled_line = expand_tags(line)
html = self.page_shell + previous_html + styled_line + HTML_END
self.web.setHtml(html)
html_line = expand_tags(line)
# Text too long so go to next page.
if self.web_frame.contentsSize().height() > self.page_height:
if self._text_fits_on_slide(previous_html + html_line):
# Check if there was a verse before the current one and append
# it, when it fits on the page.
if previous_html:
html = self.page_shell + previous_html + HTML_END
self.web.setHtml(html)
if self.web_frame.contentsSize().height() <= \
self.page_height:
while previous_raw.endswith(u'<br>'):
previous_raw = previous_raw[:-4]
if not self._text_fits_on_slide(previous_html):
formatted.append(previous_raw)
previous_html = u''
previous_raw = u''
html = self.page_shell + styled_line + HTML_END
self.web.setHtml(html)
# Now check if the current verse will fit, if it does
# not we have to start to process the verse word by
# word.
if self.web_frame.contentsSize().height() <= \
self.page_height:
previous_html = styled_line + line_end
if not self._text_fits_on_slide(html_line):
previous_html = html_line + line_end
previous_raw = line + line_end
continue
# Figure out how many words of the line will fit on screen by
# using the algorithm known as "binary chop".
# Figure out how many words of the line will fit on screen as
# the line will not fit as a whole.
raw_words = self._words_split(line)
html_words = [expand_tags(word) for word in raw_words]
smallest_index = 0
highest_index = len(html_words) - 1
index = int(highest_index / 2)
while True:
html = self.page_shell + previous_html + \
u''.join(html_words[:index + 1]).strip() + HTML_END
self.web.setHtml(html)
if self.web_frame.contentsSize().height() > \
self.page_height:
# We know that it does not fit, so change/calculate the
# new index and highest_index accordingly.
highest_index = index
index = int(index - (index - smallest_index) / 2)
else:
smallest_index = index
index = int(index + (highest_index - index) / 2)
# We found the number of words which will fit.
if smallest_index == index or highest_index == index:
index = smallest_index
formatted.append(previous_raw.rstrip(u'<br>') +
u''.join(raw_words[:index + 1]))
previous_html = u''
previous_raw = u''
else:
continue
# Check if the rest of the line fits on the slide. If it
# does we do not have to do the much more intensive "word by
# word" checking.
html = self.page_shell + \
u''.join(html_words[index + 1:]).strip() + HTML_END
self.web.setHtml(html)
if self.web_frame.contentsSize().height() <= \
self.page_height:
previous_html = \
u''.join(html_words[index + 1:]).strip() + line_end
previous_raw = \
u''.join(raw_words[index + 1:]).strip() + line_end
break
else:
# The other words do not fit, thus reset the indexes,
# create a new list and continue with "word by word".
raw_words = raw_words[index + 1:]
html_words = html_words[index + 1:]
smallest_index = 0
highest_index = len(html_words) - 1
index = int(highest_index / 2)
html_words = map(expand_tags, raw_words)
previous_html, previous_raw = self._binary_chop(
formatted, previous_html, previous_raw, html_words,
raw_words, u' ', line_end)
else:
previous_html += styled_line + line_end
previous_html += html_line + line_end
previous_raw += line + line_end
while previous_raw.endswith(u'<br>'):
previous_raw = previous_raw[:-4]
formatted.append(previous_raw)
log.debug(u'_paginate_slide_words - End')
return formatted
def _binary_chop(self, formatted, previous_html, previous_raw, html_list,
raw_list, separator, line_end):
"""
This implements the binary chop algorithm for faster rendering. This
algorithm works line based (line by line) and word based (word by word).
It is assumed that this method is **only** called, when the lines/words
to be rendered do **not** fit as a whole.
``formatted``
The list to append any slides.
``previous_html``
The html text which is know to fit on a slide, but is not yet added
to the list of slides. (unicode string)
``previous_raw``
The raw text (with formatting tags) which is know to fit on a slide,
but is not yet added to the list of slides. (unicode string)
``html_list``
The elements which do not fit on a slide and needs to be processed
using the binary chop. The text contains html.
``raw_list``
The elements which do not fit on a slide and needs to be processed
using the binary chop. The elements can contain formatting tags.
``separator``
The separator for the elements. For lines this is ``u'<br>'`` and
for words this is ``u' '``.
``line_end``
The text added after each "element line". Either ``u' '`` or
``u'<br>``. This is needed for bibles.
"""
smallest_index = 0
highest_index = len(html_list) - 1
index = int(highest_index / 2)
while True:
if self._text_fits_on_slide(
previous_html + separator.join(html_list[:index + 1]).strip()):
# We know that it does not fit, so change/calculate the
# new index and highest_index accordingly.
highest_index = index
index = int(index - (index - smallest_index) / 2)
else:
smallest_index = index
index = int(index + (highest_index - index) / 2)
# We found the number of words which will fit.
if smallest_index == index or highest_index == index:
index = smallest_index
formatted.append(previous_raw.rstrip(u'<br>') +
separator.join(raw_list[:index + 1]))
previous_html = u''
previous_raw = u''
# Stop here as the theme line count was requested.
if self.force_page:
Receiver.send_message(u'theme_line_count', index + 1)
break
else:
continue
# Check if the remaining elements fit on the slide.
if not self._text_fits_on_slide(
separator.join(html_list[index + 1:]).strip()):
previous_html = separator.join(
html_list[index + 1:]).strip() + line_end
previous_raw = separator.join(
raw_list[index + 1:]).strip() + line_end
break
else:
# The remaining elements do not fit, thus reset the indexes,
# create a new list and continue.
raw_list = raw_list[index + 1:]
html_list = html_list[index + 1:]
smallest_index = 0
highest_index = len(html_list) - 1
index = int(highest_index / 2)
return previous_html, previous_raw
def _text_fits_on_slide(self, text):
"""
Checks if the given ``text`` fits on a slide. If it does ``True`` is
returned, otherwise ``False``.
``text``
The text to check. It can contain HTML tags.
"""
self.web_frame.evaluateJavaScript(u'show_text("%s")' %
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
return self.web_frame.contentsSize().height() > self.page_height
def _words_split(self, line):
"""
Split the slide up by word so can wrap better
"""
# this parse we are to be wordy
line = line.replace(u'\n', u' ')
words = line.split(u' ')
return [word + u' ' for word in words]
return line.split(u' ')
def _lines_split(self, text):
"""
@ -479,5 +508,5 @@ class Renderer(object):
"""
# this parse we do not want to use this so remove it
text = text.replace(u'\n[---]', u'')
lines = text.split(u'\n')
return [line.replace(u'[---]', u'') for line in lines]
text = text.replace(u'[---]', u'')
return text.split(u'\n')

View File

@ -35,8 +35,7 @@ import logging
import os
import uuid
from openlp.core.lib import build_icon, clean_tags, expand_tags
from openlp.core.lib.ui import UiStrings
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate
log = logging.getLogger(__name__)
@ -165,7 +164,6 @@ class ServiceItem(object):
log.debug(u'Render called')
self._display_frames = []
self.bg_image_bytes = None
line_break = not self.is_capable(ItemCapabilities.NoLineBreaks)
theme = self.theme if self.theme else None
self.main, self.footer = \
self.renderer.set_override_theme(theme, use_override)
@ -173,9 +171,8 @@ class ServiceItem(object):
if self.service_item_type == ServiceItemType.Text:
log.debug(u'Formatting slides')
for slide in self._raw_frames:
formatted = self.renderer \
.format_slide(slide[u'raw_slide'], line_break, self)
for page in formatted:
pages = self.renderer.format_slide(slide[u'raw_slide'], self)
for page in pages:
page = page.replace(u'<br>', u'{br}')
html = expand_tags(cgi.escape(page.rstrip()))
self._display_frames.append({
@ -210,7 +207,7 @@ class ServiceItem(object):
"""
self.service_item_type = ServiceItemType.Image
self._raw_frames.append({u'title': title, u'path': path})
self.renderer.image_manager.add_image(title, path)
self.renderer.imageManager.add_image(title, path)
self._new_item()
def add_from_text(self, title, raw_slide, verse_tag=None):
@ -352,6 +349,9 @@ class ServiceItem(object):
Updates the _uuid with the value from the original one
The _uuid is unique for a given service item but this allows one to
replace an original version.
``other``
The service item to be merged with
"""
self._uuid = other._uuid
self.notes = other.notes
@ -447,10 +447,12 @@ class ServiceItem(object):
start = None
end = None
if self.start_time != 0:
start = UiStrings().StartTimeCode % \
start = unicode(translate('OpenLP.ServiceItem',
'<strong>Start</strong>: %s')) % \
unicode(datetime.timedelta(seconds=self.start_time))
if self.media_length != 0:
end = UiStrings().LengthTime % \
end = unicode(translate('OpenLP.ServiceItem',
'<strong>Length</strong>: %s')) % \
unicode(datetime.timedelta(seconds=self.media_length))
if not start and not end:
return None
@ -459,5 +461,16 @@ class ServiceItem(object):
elif not start and end:
return end
else:
return u'%s : %s' % (start, end)
return u'%s <br>%s' % (start, end)
def update_theme(self, theme):
"""
updates the theme in the service item
``theme``
The new theme to be replaced in the service item
"""
self.theme = theme
self._new_item()
self.render()

View File

@ -39,7 +39,7 @@ except ImportError:
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate, DisplayTags
from openlp.core.lib import translate, FormattingTags
from openlp.core.lib.ui import checkable_action
log = logging.getLogger(__name__)
@ -48,16 +48,17 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
"""
Spell checking widget based on QPlanTextEdit.
"""
def __init__(self, *args):
def __init__(self, parent=None, formattingTagsAllowed=True):
global ENCHANT_AVAILABLE
QtGui.QPlainTextEdit.__init__(self, *args)
QtGui.QPlainTextEdit.__init__(self, parent)
self.formattingTagsAllowed = formattingTagsAllowed
# Default dictionary based on the current locale.
if ENCHANT_AVAILABLE:
try:
self.dictionary = enchant.Dict()
self.highlighter = Highlighter(self.document())
self.highlighter.spellingDictionary = self.dictionary
except Error, DictNotFoundError:
except (Error, DictNotFoundError):
ENCHANT_AVAILABLE = False
log.debug(u'Could not load default dictionary')
@ -110,16 +111,17 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
spell_menu.addAction(action)
# Only add the spelling suggests to the menu if there are
# suggestions.
if len(spell_menu.actions()):
if spell_menu.actions():
popupMenu.insertMenu(popupMenu.actions()[0], spell_menu)
tagMenu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
'Formatting Tags'))
for html in DisplayTags.get_html_tags():
action = SpellAction(html[u'desc'], tagMenu)
action.correct.connect(self.htmlTag)
tagMenu.addAction(action)
popupMenu.insertSeparator(popupMenu.actions()[0])
popupMenu.insertMenu(popupMenu.actions()[0], tagMenu)
if self.formattingTagsAllowed:
for html in FormattingTags.get_html_tags():
action = SpellAction(html[u'desc'], tagMenu)
action.correct.connect(self.htmlTag)
tagMenu.addAction(action)
popupMenu.insertSeparator(popupMenu.actions()[0])
popupMenu.insertMenu(popupMenu.actions()[0], tagMenu)
popupMenu.exec_(event.globalPos())
def setLanguage(self, action):
@ -148,7 +150,7 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
"""
Replaces the selected text with word.
"""
for html in DisplayTags.get_html_tags():
for html in FormattingTags.get_html_tags():
if tag == html[u'desc']:
cursor = self.textCursor()
if self.textCursor().hasSelection():

View File

@ -34,8 +34,7 @@ import logging
from xml.dom.minidom import Document
from lxml import etree, objectify
from openlp.core.lib import str_to_bool, translate
from openlp.core.lib.ui import UiStrings
from openlp.core.lib import str_to_bool
log = logging.getLogger(__name__)

View File

@ -64,6 +64,7 @@ class UiStrings(object):
self.Cancel = translate('OpenLP.Ui', 'Cancel')
self.CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:')
self.CreateService = translate('OpenLP.Ui', 'Create a new service.')
self.ConfirmDelete = translate('OpenLP.Ui', 'Confirm Delete')
self.Continuous = translate('OpenLP.Ui', 'Continuous')
self.Default = unicode(translate('OpenLP.Ui', 'Default'))
self.Delete = translate('OpenLP.Ui', '&Delete')
@ -82,7 +83,6 @@ class UiStrings(object):
self.Image = translate('OpenLP.Ui', 'Image')
self.Import = translate('OpenLP.Ui', 'Import')
self.LayoutStyle = translate('OpenLP.Ui', 'Layout style:')
self.LengthTime = unicode(translate('OpenLP.Ui', 'Length %s'))
self.Live = translate('OpenLP.Ui', 'Live')
self.LiveBGError = translate('OpenLP.Ui', 'Live Background Error')
self.LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar')
@ -102,6 +102,8 @@ class UiStrings(object):
self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. '
'Do you wish to continue?')
self.OpenService = translate('OpenLP.Ui', 'Open service.')
self.PlaySlidesInLoop = translate('OpenLP.Ui','Play Slides in Loop')
self.PlaySlidesToEnd = translate('OpenLP.Ui','Play Slides to End')
self.Preview = translate('OpenLP.Ui', 'Preview')
self.PrintService = translate('OpenLP.Ui', 'Print Service')
self.ReplaceBG = translate('OpenLP.Ui', 'Replace Background')
@ -123,6 +125,10 @@ class UiStrings(object):
self.SplitToolTip = translate('OpenLP.Ui', 'Split a slide into two '
'only if it does not fit on the screen as one slide.')
self.StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s'))
self.StopPlaySlidesInLoop = translate('OpenLP.Ui',
'Stop Play Slides in Loop')
self.StopPlaySlidesToEnd = translate('OpenLP.Ui',
'Stop Play Slides to End')
self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular')
self.Themes = translate('OpenLP.Ui', 'Themes', 'Plural')
self.Tools = translate('OpenLP.Ui', 'Tools')
@ -323,8 +329,9 @@ def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None,
if checked is not None:
action.setCheckable(True)
action.setChecked(checked)
action.setShortcuts(shortcuts)
action.setShortcutContext(context)
if shortcuts:
action.setShortcuts(shortcuts)
action.setShortcutContext(context)
action_list = ActionList.get_instance()
action_list.add_action(action, category)
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'), function)

View File

@ -69,7 +69,7 @@ from advancedtab import AdvancedTab
from aboutform import AboutForm
from pluginform import PluginForm
from settingsform import SettingsForm
from displaytagform import DisplayTagForm
from formattingtagform import FormattingTagForm
from shortcutlistform import ShortcutListForm
from mediadockmanager import MediaDockManager
from servicemanager import ServiceManager

View File

@ -61,6 +61,5 @@ class AboutForm(QtGui.QDialog, Ui_AboutDialog):
Launch a web browser and go to the contribute page on the site.
"""
import webbrowser
url = u'http://www.openlp.org/en/documentation/introduction/' \
+ u'contributing.html'
url = u'http://openlp.org/en/documentation/introduction/contributing'
webbrowser.open_new(url)

View File

@ -129,6 +129,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
os.path.join(gettempdir(), u'openlp', screenshot)))
item.setCheckState(QtCore.Qt.Unchecked)
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
Receiver.send_message(u'cursor_normal')
def nextId(self):
"""
@ -156,10 +157,27 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
item = self.themesListWidget.item(iter)
if item.checkState() == QtCore.Qt.Checked:
self.themeComboBox.addItem(item.text())
# Check if this is a re-run of the wizard.
self.has_run_wizard = QtCore.QSettings().value(
u'general/has run wizard', QtCore.QVariant(False)).toBool()
if self.has_run_wizard:
# Add any existing themes to list.
for theme in self.parent().themeManagerContents.getThemes():
index = self.themeComboBox.findText(theme)
if index == -1:
self.themeComboBox.addItem(theme)
default_theme = unicode(QtCore.QSettings().value(
u'themes/global theme',
QtCore.QVariant(u'')).toString())
# Pre-select the current default theme.
index = self.themeComboBox.findText(default_theme)
self.themeComboBox.setCurrentIndex(index)
elif pageId == FirstTimePage.Progress:
Receiver.send_message(u'cursor_busy')
self._preWizard()
self._performWizard()
self._postWizard()
Receiver.send_message(u'cursor_normal')
def updateScreenListCombo(self):
"""
@ -248,11 +266,21 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
"""
if self.max_progress:
self.progressBar.setValue(self.progressBar.maximum())
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
'Download complete. Click the finish button to start OpenLP.'))
if self.has_run_wizard:
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
'Download complete.'
' Click the finish button to return to OpenLP.'))
else:
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
'Download complete.'
' Click the finish button to start OpenLP.'))
else:
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
'Click the finish button to start OpenLP.'))
if self.has_run_wizard:
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
'Click the finish button to return to OpenLP.'))
else:
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
'Click the finish button to start OpenLP.'))
self.finishButton.setVisible(True)
self.finishButton.setEnabled(True)
self.cancelButton.setVisible(False)

View File

@ -49,7 +49,7 @@ class Ui_FirstTimeWizard(object):
FirstTimeWizard.resize(550, 386)
FirstTimeWizard.setModal(True)
FirstTimeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
FirstTimeWizard.setOptions(QtGui.QWizard.IndependentPages|
FirstTimeWizard.setOptions(QtGui.QWizard.IndependentPages |
QtGui.QWizard.NoBackButtonOnStartPage |
QtGui.QWizard.NoBackButtonOnLastPage)
self.finishButton = self.button(QtGui.QWizard.FinishButton)
@ -81,9 +81,9 @@ class Ui_FirstTimeWizard(object):
self.pluginLayout.addWidget(self.imageCheckBox)
self.presentationCheckBox = QtGui.QCheckBox(self.pluginPage)
if sys.platform == "darwin":
self.presentationCheckBox.setChecked(False)
self.presentationCheckBox.setChecked(False)
else:
self.presentationCheckBox.setChecked(True)
self.presentationCheckBox.setChecked(True)
self.presentationCheckBox.setObjectName(u'presentationCheckBox')
self.pluginLayout.addWidget(self.presentationCheckBox)
self.mediaCheckBox = QtGui.QCheckBox(self.pluginPage)
@ -209,7 +209,7 @@ class Ui_FirstTimeWizard(object):
'Select the Plugins you wish to use. '))
self.songsCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
self.customCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Custom Text'))
'Custom Slides'))
self.bibleCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
self.imageCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Images'))

View File

@ -28,17 +28,17 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate
from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box
from openlp.core.lib.ui import UiStrings
class Ui_DisplayTagDialog(object):
class Ui_FormattingTagDialog(object):
def setupUi(self, displayTagDialog):
displayTagDialog.setObjectName(u'displayTagDialog')
displayTagDialog.resize(725, 548)
self.listdataGridLayout = QtGui.QGridLayout(displayTagDialog)
def setupUi(self, formattingTagDialog):
formattingTagDialog.setObjectName(u'formattingTagDialog')
formattingTagDialog.resize(725, 548)
self.listdataGridLayout = QtGui.QGridLayout(formattingTagDialog)
self.listdataGridLayout.setMargin(8)
self.listdataGridLayout.setObjectName(u'listdataGridLayout')
self.tagTableWidget = QtGui.QTableWidget(displayTagDialog)
self.tagTableWidget = QtGui.QTableWidget(formattingTagDialog)
self.tagTableWidget.setHorizontalScrollBarPolicy(
QtCore.Qt.ScrollBarAlwaysOff)
self.tagTableWidget.setEditTriggers(
@ -67,11 +67,11 @@ class Ui_DisplayTagDialog(object):
spacerItem = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.deletePushButton = QtGui.QPushButton(displayTagDialog)
self.deletePushButton = QtGui.QPushButton(formattingTagDialog)
self.deletePushButton.setObjectName(u'deletePushButton')
self.horizontalLayout.addWidget(self.deletePushButton)
self.listdataGridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1)
self.editGroupBox = QtGui.QGroupBox(displayTagDialog)
self.editGroupBox = QtGui.QGroupBox(formattingTagDialog)
self.editGroupBox.setObjectName(u'editGroupBox')
self.dataGridLayout = QtGui.QGridLayout(self.editGroupBox)
self.dataGridLayout.setObjectName(u'dataGridLayout')
@ -112,38 +112,38 @@ class Ui_DisplayTagDialog(object):
self.savePushButton.setObjectName(u'savePushButton')
self.dataGridLayout.addWidget(self.savePushButton, 4, 2, 1, 1)
self.listdataGridLayout.addWidget(self.editGroupBox, 2, 0, 1, 1)
self.buttonBox = QtGui.QDialogButtonBox(displayTagDialog)
self.buttonBox.setObjectName('displayTagDialogButtonBox')
self.buttonBox = QtGui.QDialogButtonBox(formattingTagDialog)
self.buttonBox.setObjectName('formattingTagDialogButtonBox')
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Close)
self.listdataGridLayout.addWidget(self.buttonBox, 3, 0, 1, 1)
self.retranslateUi(displayTagDialog)
QtCore.QMetaObject.connectSlotsByName(displayTagDialog)
self.retranslateUi(formattingTagDialog)
QtCore.QMetaObject.connectSlotsByName(formattingTagDialog)
def retranslateUi(self, displayTagDialog):
displayTagDialog.setWindowTitle(translate('OpenLP.displayTagDialog',
'Configure Display Tags'))
def retranslateUi(self, formattingTagDialog):
formattingTagDialog.setWindowTitle(translate(
'OpenLP.FormattingTagDialog', 'Configure Formatting Tags'))
self.editGroupBox.setTitle(
translate('OpenLP.DisplayTagDialog', 'Edit Selection'))
translate('OpenLP.FormattingTagDialog', 'Edit Selection'))
self.savePushButton.setText(
translate('OpenLP.DisplayTagDialog', 'Save'))
translate('OpenLP.FormattingTagDialog', 'Save'))
self.descriptionLabel.setText(
translate('OpenLP.DisplayTagDialog', 'Description'))
self.tagLabel.setText(translate('OpenLP.DisplayTagDialog', 'Tag'))
translate('OpenLP.FormattingTagDialog', 'Description'))
self.tagLabel.setText(translate('OpenLP.FormattingTagDialog', 'Tag'))
self.startTagLabel.setText(
translate('OpenLP.DisplayTagDialog', 'Start tag'))
translate('OpenLP.FormattingTagDialog', 'Start tag'))
self.endTagLabel.setText(
translate('OpenLP.DisplayTagDialog', 'End tag'))
translate('OpenLP.FormattingTagDialog', 'End tag'))
self.deletePushButton.setText(UiStrings().Delete)
self.newPushButton.setText(UiStrings().New)
self.tagTableWidget.horizontalHeaderItem(0).setText(
translate('OpenLP.DisplayTagDialog', 'Description'))
translate('OpenLP.FormattingTagDialog', 'Description'))
self.tagTableWidget.horizontalHeaderItem(1).setText(
translate('OpenLP.DisplayTagDialog', 'Tag Id'))
translate('OpenLP.FormattingTagDialog', 'Tag Id'))
self.tagTableWidget.horizontalHeaderItem(2).setText(
translate('OpenLP.DisplayTagDialog', 'Start HTML'))
translate('OpenLP.FormattingTagDialog', 'Start HTML'))
self.tagTableWidget.horizontalHeaderItem(3).setText(
translate('OpenLP.DisplayTagDialog', 'End HTML'))
translate('OpenLP.FormattingTagDialog', 'End HTML'))
self.tagTableWidget.setColumnWidth(0, 120)
self.tagTableWidget.setColumnWidth(1, 80)
self.tagTableWidget.setColumnWidth(2, 330)

View File

@ -25,22 +25,22 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`DisplayTagTab` provides an Tag Edit facility. The Base set are
protected and included each time loaded. Custom tags can be defined and saved.
The Custom Tag arrays are saved in a pickle so QSettings works on them. Base
The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are
protected and included each time loaded. Custom tags can be defined and saved.
The Custom Tag arrays are saved in a pickle so QSettings works on them. Base
Tags cannot be changed.
"""
import cPickle
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate, DisplayTags
from openlp.core.lib import translate, FormattingTags
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.displaytagdialog import Ui_DisplayTagDialog
from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog
class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
"""
The :class:`DisplayTagTab` manages the settings tab .
The :class:`FormattingTagForm` manages the settings tab .
"""
def __init__(self, parent):
"""
@ -48,7 +48,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
"""
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
self._loadDisplayTags()
self._loadFormattingTags()
QtCore.QObject.connect(self.tagTableWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected)
QtCore.QObject.connect(self.newPushButton,
@ -65,19 +65,20 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
Load Display and set field state.
"""
# Create initial copy from master
self._loadDisplayTags()
self._loadFormattingTags()
self._resetTable()
self.selected = -1
return QtGui.QDialog.exec_(self)
def _loadDisplayTags(self):
def _loadFormattingTags(self):
"""
Load the Tags from store so can be used in the system or used to
update the display. If Cancel was selected this is needed to reset the
dsiplay to the correct version.
"""
# Initial Load of the Tags
DisplayTags.reset_html_tags()
FormattingTags.reset_html_tags()
# Formatting Tags were also known as display tags.
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
QtCore.QVariant(u'')).toString()
# cPickle only accepts str not unicode strings
@ -85,14 +86,14 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
if user_expands_string:
user_tags = cPickle.loads(user_expands_string)
# If we have some user ones added them as well
DisplayTags.add_html_tags(user_tags)
FormattingTags.add_html_tags(user_tags)
def onRowSelected(self):
"""
Table Row selected so display items and set field state.
"""
row = self.tagTableWidget.currentRow()
html = DisplayTags.get_html_tags()[row]
html = FormattingTags.get_html_tags()[row]
self.selected = row
self.descriptionLineEdit.setText(html[u'desc'])
self.tagLineEdit.setText(self._strip(html[u'start tag']))
@ -117,23 +118,23 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
"""
Add a new tag to list only if it is not a duplicate.
"""
for html in DisplayTags.get_html_tags():
for html in FormattingTags.get_html_tags():
if self._strip(html[u'start tag']) == u'n':
critical_error_message_box(
translate('OpenLP.DisplayTagTab', 'Update Error'),
translate('OpenLP.DisplayTagTab',
translate('OpenLP.FormattingTagForm', 'Update Error'),
translate('OpenLP.FormattingTagForm',
'Tag "n" already defined.'))
return
# Add new tag to list
tag = {
u'desc': translate('OpenLP.DisplayTagTab', 'New Tag'),
u'desc': translate('OpenLP.FormattingTagForm', 'New Tag'),
u'start tag': u'{n}',
u'start html': translate('OpenLP.DisplayTagTab', '<HTML here>'),
u'start html': translate('OpenLP.FormattingTagForm', '<HTML here>'),
u'end tag': u'{/n}',
u'end html': translate('OpenLP.DisplayTagTab', '</and here>'),
u'end html': translate('OpenLP.FormattingTagForm', '</and here>'),
u'protected': False
}
DisplayTags.add_html_tags([tag])
FormattingTags.add_html_tags([tag])
self._resetTable()
# Highlight new row
self.tagTableWidget.selectRow(self.tagTableWidget.rowCount() - 1)
@ -145,7 +146,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
Delete selected custom tag.
"""
if self.selected != -1:
DisplayTags.remove_html_tag(self.selected)
FormattingTags.remove_html_tag(self.selected)
self.selected = -1
self._resetTable()
self._saveTable()
@ -154,7 +155,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
"""
Update Custom Tag details if not duplicate and save the data.
"""
html_expands = DisplayTags.get_html_tags()
html_expands = FormattingTags.get_html_tags()
if self.selected != -1:
html = html_expands[self.selected]
tag = unicode(self.tagLineEdit.text())
@ -162,8 +163,8 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
if self._strip(html1[u'start tag']) == tag and \
linenumber != self.selected:
critical_error_message_box(
translate('OpenLP.DisplayTagTab', 'Update Error'),
unicode(translate('OpenLP.DisplayTagTab',
translate('OpenLP.FormattingTagForm', 'Update Error'),
unicode(translate('OpenLP.FormattingTagForm',
'Tag %s already defined.')) % tag)
return
html[u'desc'] = unicode(self.descriptionLineEdit.text())
@ -177,18 +178,15 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
def _saveTable(self):
"""
Saves all display tags except protected ones.
Saves all formatting tags except protected ones.
"""
tags = []
for tag in DisplayTags.get_html_tags():
for tag in FormattingTags.get_html_tags():
if not tag[u'protected']:
tags.append(tag)
if tags:
QtCore.QSettings().setValue(u'displayTags/html_tags',
QtCore.QVariant(cPickle.dumps(tags)))
else:
QtCore.QSettings().setValue(u'displayTags/html_tags',
QtCore.QVariant(u''))
# Formatting Tags were also known as display tags.
QtCore.QSettings().setValue(u'displayTags/html_tags',
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
def _resetTable(self):
"""
@ -199,7 +197,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
self.newPushButton.setEnabled(True)
self.savePushButton.setEnabled(False)
self.deletePushButton.setEnabled(False)
for linenumber, html in enumerate(DisplayTags.get_html_tags()):
for linenumber, html in enumerate(FormattingTags.get_html_tags()):
self.tagTableWidget.setRowCount(
self.tagTableWidget.rowCount() + 1)
self.tagTableWidget.setItem(linenumber, 0,

View File

@ -44,7 +44,7 @@ class GeneralTab(SettingsTab):
"""
self.screens = ScreenList.get_instance()
self.icon_path = u':/icon/openlp-logo-16x16.png'
generalTranslated = translate('GeneralTab', 'General')
generalTranslated = translate('OpenLP.GeneralTab', 'General')
SettingsTab.__init__(self, parent, u'General', generalTranslated)
def setupUi(self):

View File

@ -48,13 +48,13 @@ class MainDisplay(QtGui.QGraphicsView):
"""
This is the display screen.
"""
def __init__(self, parent, image_manager, live):
def __init__(self, parent, imageManager, live):
if live:
QtGui.QGraphicsView.__init__(self)
else:
QtGui.QGraphicsView.__init__(self, parent)
self.isLive = live
self.image_manager = image_manager
self.imageManager = imageManager
self.screens = ScreenList.get_instance()
self.alertTab = None
self.hideMode = None
@ -188,7 +188,7 @@ class MainDisplay(QtGui.QGraphicsView):
while not self.webLoaded:
Receiver.send_message(u'openlp_process_events')
self.setGeometry(self.screen[u'size'])
self.frame.evaluateJavaScript(u'show_text("%s")' % \
self.frame.evaluateJavaScript(u'show_text("%s")' %
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
return self.preview()
@ -232,11 +232,13 @@ class MainDisplay(QtGui.QGraphicsView):
"""
API for replacement backgrounds so Images are added directly to cache
"""
self.image_manager.add_image(name, path)
self.image(name)
self.imageManager.add_image(name, path)
if hasattr(self, u'serviceItem'):
self.override[u'image'] = name
self.override[u'theme'] = self.serviceItem.themedata.theme_name
self.image(name)
return True
return False
def image(self, name):
"""
@ -247,7 +249,7 @@ class MainDisplay(QtGui.QGraphicsView):
The name of the image to be displayed
"""
log.debug(u'image to display')
image = self.image_manager.get_image_bytes(name)
image = self.imageManager.get_image_bytes(name)
self.resetVideo()
self.displayImage(image)
return self.preview()
@ -349,6 +351,9 @@ class MainDisplay(QtGui.QGraphicsView):
"""
Loads and starts a video to run with the option of sound
"""
# We request a background video but have no service Item
if isBackground and not hasattr(self, u'serviceItem'):
return None
if not self.mediaObject:
self.createMediaObject()
log.debug(u'video')
@ -477,13 +482,13 @@ class MainDisplay(QtGui.QGraphicsView):
self.override = {}
else:
# replace the background
background = self.image_manager. \
background = self.imageManager. \
get_image_bytes(self.override[u'image'])
if self.serviceItem.themedata.background_filename:
self.serviceItem.bg_image_bytes = self.image_manager. \
self.serviceItem.bg_image_bytes = self.imageManager. \
get_image_bytes(self.serviceItem.themedata.theme_name)
if image:
image_bytes = self.image_manager.get_image_bytes(image)
image_bytes = self.imageManager.get_image_bytes(image)
else:
image_bytes = None
html = build_html(self.serviceItem, self.screen, self.alertTab,

View File

@ -28,20 +28,23 @@
import logging
import os
import sys
import shutil
from tempfile import gettempdir
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Renderer, build_icon, OpenLPDockWidget, \
PluginManager, Receiver, translate, ImageManager
PluginManager, Receiver, translate, ImageManager, PluginStatus
from openlp.core.lib.ui import UiStrings, base_action, checkable_action, \
icon_action, shortcut_action
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
ThemeManager, SlideController, PluginForm, MediaDockManager, \
ShortcutListForm, DisplayTagForm
ShortcutListForm, FormattingTagForm
from openlp.core.utils import AppLocation, add_actions, LanguageManager, \
get_application_version, delete_file
from openlp.core.utils.actions import ActionList, CategoryOrder
from openlp.core.ui.firsttimeform import FirstTimeForm
from openlp.core.ui import ScreenList
log = logging.getLogger(__name__)
@ -65,6 +68,12 @@ MEDIA_MANAGER_STYLE = """
}
"""
PROGRESSBAR_STYLE = """
QProgressBar{
height: 10px;
}
"""
class Ui_MainWindow(object):
def setupUi(self, mainWindow):
"""
@ -93,12 +102,16 @@ class Ui_MainWindow(object):
self.previewController.panel.setVisible(previewVisible)
liveVisible = QtCore.QSettings().value(u'user interface/live panel',
QtCore.QVariant(True)).toBool()
panelLocked = QtCore.QSettings().value(u'user interface/lock panel',
QtCore.QVariant(False)).toBool()
self.liveController.panel.setVisible(liveVisible)
# Create menu
self.menuBar = QtGui.QMenuBar(mainWindow)
self.menuBar.setObjectName(u'menuBar')
self.fileMenu = QtGui.QMenu(self.menuBar)
self.fileMenu.setObjectName(u'fileMenu')
self.recentFilesMenu = QtGui.QMenu(self.fileMenu)
self.recentFilesMenu.setObjectName(u'recentFilesMenu')
self.fileImportMenu = QtGui.QMenu(self.fileMenu)
self.fileImportMenu.setObjectName(u'fileImportMenu')
self.fileExportMenu = QtGui.QMenu(self.fileMenu)
@ -128,6 +141,7 @@ class Ui_MainWindow(object):
self.statusBar.addPermanentWidget(self.loadProgressBar)
self.loadProgressBar.hide()
self.loadProgressBar.setValue(0)
self.loadProgressBar.setStyleSheet(PROGRESSBAR_STYLE)
self.defaultThemeLabel = QtGui.QLabel(self.statusBar)
self.defaultThemeLabel.setObjectName(u'defaultThemeLabel')
self.statusBar.addPermanentWidget(self.defaultThemeLabel)
@ -213,7 +227,11 @@ class Ui_MainWindow(object):
self.viewLivePanel = shortcut_action(mainWindow, u'viewLivePanel',
[QtGui.QKeySequence(u'F12')], self.setLivePanelVisibility,
checked=liveVisible, category=UiStrings().View)
action_list.add_category(UiStrings().ViewMode, CategoryOrder.standardMenu)
self.lockPanel = shortcut_action(mainWindow, u'lockPanel',
None, self.setLockPanel,
checked=panelLocked, category=None)
action_list.add_category(UiStrings().ViewMode,
CategoryOrder.standardMenu)
self.modeDefaultItem = checkable_action(
mainWindow, u'modeDefaultItem', category=UiStrings().ViewMode)
self.modeSetupItem = checkable_action(
@ -231,9 +249,13 @@ class Ui_MainWindow(object):
self.toolsOpenDataFolder = icon_action(mainWindow,
u'toolsOpenDataFolder', u':/general/general_open.png',
category=UiStrings().Tools)
self.toolsFirstTimeWizard = icon_action(mainWindow,
u'toolsFirstTimeWizard', u':/general/general_revert.png',
category=UiStrings().Tools)
self.updateThemeImages = base_action(mainWindow,
u'updateThemeImages', category=UiStrings().Tools)
action_list.add_category(UiStrings().Settings, CategoryOrder.standardMenu)
action_list.add_category(UiStrings().Settings,
CategoryOrder.standardMenu)
self.settingsPluginListItem = shortcut_action(mainWindow,
u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')],
self.onPluginItemClicked, u':/system/settings_plugin_list.png',
@ -255,40 +277,45 @@ class Ui_MainWindow(object):
u'settingsShortcutsItem',
u':/system/system_configure_shortcuts.png',
category=UiStrings().Settings)
self.displayTagItem = icon_action(mainWindow,
# Formatting Tags were also known as display tags.
self.formattingTagItem = icon_action(mainWindow,
u'displayTagItem', u':/system/tag_editor.png',
category=UiStrings().Settings)
self.settingsConfigureItem = icon_action(mainWindow,
u'settingsConfigureItem', u':/system/system_settings.png',
category=UiStrings().Settings)
action_list.add_category(UiStrings().Help, CategoryOrder.standardMenu)
self.helpDocumentationItem = icon_action(mainWindow,
u'helpDocumentationItem', u':/system/system_help_contents.png',
category=None)#UiStrings().Help)
self.helpDocumentationItem.setEnabled(False)
self.helpAboutItem = shortcut_action(mainWindow, u'helpAboutItem',
[QtGui.QKeySequence(u'Ctrl+F1')], self.onHelpAboutItemClicked,
self.aboutItem = shortcut_action(mainWindow, u'aboutItem',
[QtGui.QKeySequence(u'Ctrl+F1')], self.onAboutItemClicked,
u':/system/system_about.png', category=UiStrings().Help)
self.helpOnlineHelpItem = shortcut_action(
mainWindow, u'helpOnlineHelpItem', [QtGui.QKeySequence(u'F1')],
self.onHelpOnlineHelpClicked, u':/system/system_online_help.png',
category=UiStrings().Help)
self.helpWebSiteItem = base_action(
mainWindow, u'helpWebSiteItem', category=UiStrings().Help)
if os.name == u'nt':
self.localHelpFile = os.path.join(
AppLocation.get_directory(AppLocation.AppDir), 'OpenLP.chm')
self.offlineHelpItem = shortcut_action(
mainWindow, u'offlineHelpItem', [QtGui.QKeySequence(u'F1')],
self.onOfflineHelpClicked,
u':/system/system_help_contents.png', category=UiStrings().Help)
self.onlineHelpItem = shortcut_action(
mainWindow, u'onlineHelpItem',
[QtGui.QKeySequence(u'Alt+F1')], self.onOnlineHelpClicked,
u':/system/system_online_help.png', category=UiStrings().Help)
self.webSiteItem = base_action(
mainWindow, u'webSiteItem', category=UiStrings().Help)
add_actions(self.fileImportMenu,
(self.importThemeItem, self.importLanguageItem))
add_actions(self.fileExportMenu,
(self.exportThemeItem, self.exportLanguageItem))
self.fileMenuActions = (self.fileNewItem, self.fileOpenItem,
self.fileSaveItem, self.fileSaveAsItem, None,
self.printServiceOrderItem, None, self.fileImportMenu.menuAction(),
self.fileExportMenu.menuAction(), self.fileExitItem)
add_actions(self.fileMenu, (self.fileNewItem, self.fileOpenItem,
self.fileSaveItem, self.fileSaveAsItem,
self.recentFilesMenu.menuAction(), None,
self.fileImportMenu.menuAction(), self.fileExportMenu.menuAction(),
None, self.printServiceOrderItem, self.fileExitItem))
add_actions(self.viewModeMenu, (self.modeDefaultItem,
self.modeSetupItem, self.modeLiveItem))
add_actions(self.viewMenu, (self.viewModeMenu.menuAction(),
None, self.viewMediaManagerItem, self.viewServiceManagerItem,
self.viewThemeManagerItem, None, self.viewPreviewPanel,
self.viewLivePanel))
self.viewLivePanel, None, self.lockPanel))
# i18n add Language Actions
add_actions(self.settingsLanguageMenu, (self.autoLanguageItem, None))
add_actions(self.settingsLanguageMenu, self.languageGroup.actions())
@ -298,18 +325,23 @@ class Ui_MainWindow(object):
add_actions(self.settingsMenu, (self.settingsPluginListItem,
self.settingsLanguageMenu.menuAction(), None,
self.settingsConfigureItem, self.settingsShortcutsItem,
self.displayTagItem))
self.formattingTagItem))
else:
add_actions(self.settingsMenu, (self.settingsPluginListItem,
self.settingsLanguageMenu.menuAction(), None,
self.displayTagItem, self.settingsShortcutsItem,
self.formattingTagItem, self.settingsShortcutsItem,
self.settingsConfigureItem))
add_actions(self.toolsMenu, (self.toolsAddToolItem, None))
add_actions(self.toolsMenu, (self.toolsOpenDataFolder, None))
add_actions(self.toolsMenu, (self.toolsFirstTimeWizard, None))
add_actions(self.toolsMenu, [self.updateThemeImages])
add_actions(self.helpMenu, (self.helpDocumentationItem,
self.helpOnlineHelpItem, None, self.helpWebSiteItem,
self.helpAboutItem))
if os.name == u'nt':
add_actions(self.helpMenu, (self.offlineHelpItem,
self.onlineHelpItem, None, self.webSiteItem,
self.aboutItem))
else:
add_actions(self.helpMenu, (self.onlineHelpItem, None,
self.webSiteItem, self.aboutItem))
add_actions(self.menuBar, (self.fileMenu.menuAction(),
self.viewMenu.menuAction(), self.toolsMenu.menuAction(),
self.settingsMenu.menuAction(), self.helpMenu.menuAction()))
@ -318,13 +350,13 @@ class Ui_MainWindow(object):
self.mediaToolBox.setCurrentIndex(0)
# Connect up some signals and slots
QtCore.QObject.connect(self.fileMenu,
QtCore.SIGNAL(u'aboutToShow()'), self.updateFileMenu)
QtCore.SIGNAL(u'aboutToShow()'), self.updateRecentFilesMenu)
QtCore.QMetaObject.connectSlotsByName(mainWindow)
# Hide the entry, as it does not have any functionality yet.
self.toolsAddToolItem.setVisible(False)
self.importLanguageItem.setVisible(False)
self.exportLanguageItem.setVisible(False)
self.helpDocumentationItem.setVisible(False)
self.setLockPanel(panelLocked)
def retranslateUi(self, mainWindow):
"""
@ -335,6 +367,8 @@ class Ui_MainWindow(object):
self.fileMenu.setTitle(translate('OpenLP.MainWindow', '&File'))
self.fileImportMenu.setTitle(translate('OpenLP.MainWindow', '&Import'))
self.fileExportMenu.setTitle(translate('OpenLP.MainWindow', '&Export'))
self.recentFilesMenu.setTitle(
translate('OpenLP.MainWindow', '&Recent Files'))
self.viewMenu.setTitle(translate('OpenLP.MainWindow', '&View'))
self.viewModeMenu.setTitle(translate('OpenLP.MainWindow', 'M&ode'))
self.toolsMenu.setTitle(translate('OpenLP.MainWindow', '&Tools'))
@ -382,8 +416,8 @@ class Ui_MainWindow(object):
translate('OpenLP.MainWindow', '&Language'))
self.settingsShortcutsItem.setText(
translate('OpenLP.MainWindow', 'Configure &Shortcuts...'))
self.displayTagItem.setText(
translate('OpenLP.MainWindow', '&Configure Display Tags'))
self.formattingTagItem.setText(
translate('OpenLP.MainWindow', '&Configure Formatting Tags...'))
self.settingsConfigureItem.setText(
translate('OpenLP.MainWindow', '&Configure OpenLP...'))
self.viewMediaManagerItem.setText(
@ -414,20 +448,25 @@ class Ui_MainWindow(object):
translate('OpenLP.MainWindow', '&Live Panel'))
self.viewLivePanel.setToolTip(
translate('OpenLP.MainWindow', 'Toggle Live Panel'))
self.lockPanel.setText(
translate('OpenLP.MainWindow', 'L&ock Panels'))
self.lockPanel.setStatusTip(
translate('OpenLP.MainWindow', 'Prevent the panels being moved.'))
self.viewLivePanel.setStatusTip(translate('OpenLP.MainWindow',
'Toggle the visibility of the live panel.'))
self.settingsPluginListItem.setText(translate('OpenLP.MainWindow',
'&Plugin List'))
self.settingsPluginListItem.setStatusTip(
translate('OpenLP.MainWindow', 'List the Plugins'))
self.helpDocumentationItem.setText(
translate('OpenLP.MainWindow', '&User Guide'))
self.helpAboutItem.setText(translate('OpenLP.MainWindow', '&About'))
self.helpAboutItem.setStatusTip(
self.aboutItem.setText(translate('OpenLP.MainWindow', '&About'))
self.aboutItem.setStatusTip(
translate('OpenLP.MainWindow', 'More information about OpenLP'))
self.helpOnlineHelpItem.setText(
if os.name == u'nt':
self.offlineHelpItem.setText(
translate('OpenLP.MainWindow', '&User Guide'))
self.onlineHelpItem.setText(
translate('OpenLP.MainWindow', '&Online Help'))
self.helpWebSiteItem.setText(
self.webSiteItem.setText(
translate('OpenLP.MainWindow', '&Web Site'))
for item in self.languageGroup.actions():
item.setText(item.objectName())
@ -445,6 +484,10 @@ class Ui_MainWindow(object):
translate('OpenLP.MainWindow', 'Open &Data Folder...'))
self.toolsOpenDataFolder.setStatusTip(translate('OpenLP.MainWindow',
'Open the folder where songs, bibles and other data resides.'))
self.toolsFirstTimeWizard.setText(
translate('OpenLP.MainWindow', 'Re-run First Time Wizard'))
self.toolsFirstTimeWizard.setStatusTip(translate('OpenLP.MainWindow',
'Re-run the First Time Wizard, importing songs, Bibles and themes.'))
self.updateThemeImages.setText(
translate('OpenLP.MainWindow', 'Update Theme Images'))
self.updateThemeImages.setStatusTip(
@ -485,7 +528,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.serviceNotSaved = False
self.aboutForm = AboutForm(self)
self.settingsForm = SettingsForm(self, self)
self.displayTagForm = DisplayTagForm(self)
self.formattingTagForm = FormattingTagForm(self)
self.shortcutForm = ShortcutListForm(self)
self.recentFiles = QtCore.QStringList()
# Set up the path with plugins
@ -497,8 +540,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.setupUi(self)
# Load settings after setupUi so default UI sizes are overwritten
self.loadSettings()
# Once settings are loaded update FileMenu with recentFiles
self.updateFileMenu()
# Once settings are loaded update the menu with the recent files.
self.updateRecentFilesMenu()
self.pluginForm = PluginForm(self)
# Set up signals and slots
QtCore.QObject.connect(self.importThemeItem,
@ -516,14 +559,16 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
QtCore.QObject.connect(self.themeManagerDock,
QtCore.SIGNAL(u'visibilityChanged(bool)'),
self.viewThemeManagerItem.setChecked)
QtCore.QObject.connect(self.helpWebSiteItem,
QtCore.QObject.connect(self.webSiteItem,
QtCore.SIGNAL(u'triggered()'), self.onHelpWebSiteClicked)
QtCore.QObject.connect(self.toolsOpenDataFolder,
QtCore.SIGNAL(u'triggered()'), self.onToolsOpenDataFolderClicked)
QtCore.QObject.connect(self.toolsFirstTimeWizard,
QtCore.SIGNAL(u'triggered()'), self.onFirstTimeWizardClicked)
QtCore.QObject.connect(self.updateThemeImages,
QtCore.SIGNAL(u'triggered()'), self.onUpdateThemeImages)
QtCore.QObject.connect(self.displayTagItem,
QtCore.SIGNAL(u'triggered()'), self.onDisplayTagItemClicked)
QtCore.QObject.connect(self.formattingTagItem,
QtCore.SIGNAL(u'triggered()'), self.onFormattingTagItemClicked)
QtCore.QObject.connect(self.settingsConfigureItem,
QtCore.SIGNAL(u'triggered()'), self.onSettingsConfigureItemClicked)
QtCore.QObject.connect(self.settingsShortcutsItem,
@ -653,7 +698,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
QtCore.QVariant(False)).toBool():
self.serviceManagerContents.loadLastFile()
view_mode = QtCore.QSettings().value(u'%s/view mode' % \
self.generalSettingsSection, u'default')
self.generalSettingsSection, u'default').toString()
if view_mode == u'default':
self.modeDefaultItem.setChecked(True)
elif view_mode == u'setup':
@ -682,11 +727,46 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
plugin.firstTime()
Receiver.send_message(u'openlp_process_events')
temp_dir = os.path.join(unicode(gettempdir()), u'openlp')
if not os.path.exists(temp_dir):
shutil.rmtree(temp_dir, True)
def onFirstTimeWizardClicked(self):
"""
Re-run the first time wizard. Prompts the user for run confirmation
If wizard is run, songs, bibles and themes are imported. The default
theme is changed (if necessary). The plugins in pluginmanager are
set active/in-active to match the selection in the wizard.
"""
answer = QtGui.QMessageBox.warning(self,
translate('OpenLP.MainWindow', 'Re-run First Time Wizard?'),
translate('OpenLP.MainWindow',
'Are you sure you want to re-run the First Time Wizard?\n\n'
'Re-running this wizard may make changes to your current '
'OpenLP configuration and possibly add songs to your '
'existing songs list and change your default theme.'),
QtGui.QMessageBox.StandardButtons(
QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No),
QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return
for filename in os.listdir(temp_dir):
delete_file(os.path.join(temp_dir, filename))
os.removedirs(temp_dir)
Receiver.send_message(u'cursor_busy')
screens = ScreenList.get_instance()
if FirstTimeForm(screens, self).exec_() == QtGui.QDialog.Accepted:
self.firstTime()
for plugin in self.pluginManager.plugins:
self.activePlugin = plugin
oldStatus = self.activePlugin.status
self.activePlugin.setStatus()
if oldStatus != self.activePlugin.status:
if self.activePlugin.status == PluginStatus.Active:
self.activePlugin.toggleStatus(PluginStatus.Active)
self.activePlugin.appStartup()
else:
self.activePlugin.toggleStatus(PluginStatus.Inactive)
self.themeManagerContents.configUpdated()
self.themeManagerContents.loadThemes(True)
Receiver.send_message(u'theme_update_global',
self.themeManagerContents.global_theme)
def blankCheck(self):
"""
@ -723,14 +803,20 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
import webbrowser
webbrowser.open_new(u'http://openlp.org/')
def onHelpOnlineHelpClicked(self):
def onOfflineHelpClicked(self):
"""
Load the local OpenLP help file
"""
os.startfile(self.localHelpFile)
def onOnlineHelpClicked(self):
"""
Load the online OpenLP manual
"""
import webbrowser
webbrowser.open_new(u'http://manual.openlp.org/')
def onHelpAboutItemClicked(self):
def onAboutItemClicked(self):
"""
Show the About form
"""
@ -756,11 +842,11 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
"""
self.themeManagerContents.updatePreviewImages()
def onDisplayTagItemClicked(self):
def onFormattingTagItemClicked(self):
"""
Show the Settings dialog
"""
self.displayTagForm.exec_()
self.formattingTagForm.exec_()
def onSettingsConfigureItemClicked(self):
"""
@ -936,7 +1022,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.mediaManagerDock.setVisible(not self.mediaManagerDock.isVisible())
def toggleServiceManager(self):
self.serviceManagerDock.setVisible(not self.serviceManagerDock.isVisible())
self.serviceManagerDock.setVisible(
not self.serviceManagerDock.isVisible())
def toggleThemeManager(self):
self.themeManagerDock.setVisible(not self.themeManagerDock.isVisible())
@ -956,6 +1043,37 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
QtCore.QVariant(visible))
self.viewPreviewPanel.setChecked(visible)
def setLockPanel(self, lock):
"""
Sets the ability to stop the toolbars being changed.
"""
if lock:
self.themeManagerDock.setFeatures(
QtGui.QDockWidget.NoDockWidgetFeatures)
self.serviceManagerDock.setFeatures(
QtGui.QDockWidget.NoDockWidgetFeatures)
self.mediaManagerDock.setFeatures(
QtGui.QDockWidget.NoDockWidgetFeatures)
self.viewMediaManagerItem.setEnabled(False)
self.viewServiceManagerItem.setEnabled(False)
self.viewThemeManagerItem.setEnabled(False)
self.viewPreviewPanel.setEnabled(False)
self.viewLivePanel.setEnabled(False)
else:
self.themeManagerDock.setFeatures(
QtGui.QDockWidget.AllDockWidgetFeatures)
self.serviceManagerDock.setFeatures(
QtGui.QDockWidget.AllDockWidgetFeatures)
self.mediaManagerDock.setFeatures(
QtGui.QDockWidget.AllDockWidgetFeatures)
self.viewMediaManagerItem.setEnabled(True)
self.viewServiceManagerItem.setEnabled(True)
self.viewThemeManagerItem.setEnabled(True)
self.viewPreviewPanel.setEnabled(True)
self.viewLivePanel.setEnabled(True)
QtCore.QSettings().setValue(u'user interface/lock panel',
QtCore.QVariant(lock))
def setLivePanelVisibility(self, visible):
"""
Sets the visibility of the live panel including saving the setting and
@ -986,6 +1104,13 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.restoreGeometry(
settings.value(u'main window geometry').toByteArray())
self.restoreState(settings.value(u'main window state').toByteArray())
self.liveController.splitter.restoreState(
settings.value(u'live splitter geometry').toByteArray())
self.previewController.splitter.restoreState(
settings.value(u'preview splitter geometry').toByteArray())
self.controlSplitter.restoreState(
settings.value(u'mainwindow splitter geometry').toByteArray())
settings.endGroup()
def saveSettings(self):
@ -1006,32 +1131,44 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
QtCore.QVariant(self.saveState()))
settings.setValue(u'main window geometry',
QtCore.QVariant(self.saveGeometry()))
settings.setValue(u'live splitter geometry',
QtCore.QVariant(self.liveController.splitter.saveState()))
settings.setValue(u'preview splitter geometry',
QtCore.QVariant(self.previewController.splitter.saveState()))
settings.setValue(u'mainwindow splitter geometry',
QtCore.QVariant(self.controlSplitter.saveState()))
settings.endGroup()
def updateFileMenu(self):
def updateRecentFilesMenu(self):
"""
Updates the file menu with the latest list of service files accessed.
Updates the recent file menu with the latest list of service files
accessed.
"""
recentFileCount = QtCore.QSettings().value(
u'advanced/recent file count', QtCore.QVariant(4)).toInt()[0]
self.fileMenu.clear()
add_actions(self.fileMenu, self.fileMenuActions[:-1])
existingRecentFiles = [recentFile for recentFile in self.recentFiles
if QtCore.QFile.exists(recentFile)]
recentFilesToDisplay = existingRecentFiles[0:recentFileCount]
if recentFilesToDisplay:
self.fileMenu.addSeparator()
for fileId, filename in enumerate(recentFilesToDisplay):
log.debug('Recent file name: %s', filename)
action = base_action(self, u'')
action.setText(u'&%d %s' %
(fileId + 1, QtCore.QFileInfo(filename).fileName()))
action.setData(QtCore.QVariant(filename))
self.connect(action, QtCore.SIGNAL(u'triggered()'),
self.serviceManagerContents.onRecentServiceClicked)
self.fileMenu.addAction(action)
self.fileMenu.addSeparator()
self.fileMenu.addAction(self.fileMenuActions[-1])
self.recentFilesMenu.clear()
for fileId, filename in enumerate(recentFilesToDisplay):
log.debug('Recent file name: %s', filename)
action = base_action(self, u'')
action.setText(u'&%d %s' %
(fileId + 1, QtCore.QFileInfo(filename).fileName()))
action.setData(QtCore.QVariant(filename))
self.connect(action, QtCore.SIGNAL(u'triggered()'),
self.serviceManagerContents.onRecentServiceClicked)
self.recentFilesMenu.addAction(action)
clearRecentFilesAction = base_action(self, u'')
clearRecentFilesAction.setText(
translate('OpenLP.MainWindow', 'Clear List',
'Clear List of recent files'))
clearRecentFilesAction.setStatusTip(
translate('OpenLP.MainWindow', 'Clear the list of recent files.'))
add_actions(self.recentFilesMenu, (None, clearRecentFilesAction))
self.connect(clearRecentFilesAction, QtCore.SIGNAL(u'triggered()'),
self.recentFiles.clear)
clearRecentFilesAction.setEnabled(not self.recentFiles.isEmpty())
def addRecentFile(self, filename):
"""

View File

@ -66,7 +66,7 @@ class MediaDockManager(object):
match = False
for dock_index in range(0, self.media_dock.count()):
if self.media_dock.widget(dock_index).settingsSection == \
media_item.plugin.name.lower():
media_item.plugin.name:
match = True
break
if not match:
@ -84,6 +84,6 @@ class MediaDockManager(object):
for dock_index in range(0, self.media_dock.count()):
if self.media_dock.widget(dock_index):
if self.media_dock.widget(dock_index).settingsSection == \
media_item.plugin.name.lower():
media_item.plugin.name:
self.media_dock.widget(dock_index).setVisible(False)
self.media_dock.removeItem(dock_index)

View File

@ -108,7 +108,7 @@ class Ui_PrintServiceDialog(object):
self.footerLabel = QtGui.QLabel(self.optionsWidget)
self.footerLabel.setObjectName(u'footerLabel')
self.optionsLayout.addWidget(self.footerLabel)
self.footerTextEdit = SpellTextEdit(self.optionsWidget)
self.footerTextEdit = SpellTextEdit(self.optionsWidget, False)
self.footerTextEdit.setObjectName(u'footerTextEdit')
self.optionsLayout.addWidget(self.footerTextEdit)
self.optionsGroupBox = QtGui.QGroupBox()

View File

@ -24,6 +24,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import cgi
import datetime
import os
@ -183,7 +184,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
self._addElement(u'style', custom_css, html_data.head,
attribute=(u'type', u'text/css'))
self._addElement(u'body', parent=html_data)
self._addElement(u'h1', unicode(self.titleLineEdit.text()),
self._addElement(u'h1', cgi.escape(unicode(self.titleLineEdit.text())),
html_data.body, classId=u'serviceTitle')
for index, item in enumerate(self.serviceManager.serviceItems):
self._addPreviewItem(html_data.body, item[u'service_item'], index)
@ -193,8 +194,9 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
classId=u'customNotes')
self._addElement(u'span', translate('OpenLP.ServiceManager',
'Custom Service Notes: '), div, classId=u'customNotesTitle')
self._addElement(u'span', self.footerTextEdit.toPlainText(), div,
classId=u'customNotesText')
self._addElement(u'span',
cgi.escape(self.footerTextEdit.toPlainText()),
div, classId=u'customNotesText')
self.document.setHtml(html.tostring(html_data))
self.previewWidget.updatePreview()
@ -204,8 +206,8 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
item_title = self._addElement(u'h2', parent=div, classId=u'itemTitle')
self._addElement(u'img', parent=item_title,
attribute=(u'src', item.icon))
self._addElement(u'span', u'&nbsp;' + item.get_display_title(),
item_title)
self._addElement(u'span',
u'&nbsp;' + cgi.escape(item.get_display_title()), item_title)
if self.slideTextCheckBox.isChecked():
# Add the text of the service item.
if item.is_text():
@ -230,8 +232,9 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
foot_text = item.foot_text
foot_text = foot_text.partition(u'<br>')[2]
if foot_text:
foot = self._addElement(u'div', foot_text, parent=div,
classId=u'itemFooter')
foot_text = cgi.escape(foot_text.replace(u'<br>', u'\n'))
self._addElement(u'div', foot_text.replace(u'\n', u'<br>'),
parent=div, classId=u'itemFooter')
# Add service items' notes.
if self.notesCheckBox.isChecked():
if item.notes:
@ -239,8 +242,8 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
self._addElement(u'span',
translate('OpenLP.ServiceManager', 'Notes: '), p,
classId=u'itemNotesTitle')
notes = self._addElement(u'span',
item.notes.replace(u'\n', u'<br />'), p,
self._addElement(u'span',
cgi.escape(unicode(item.notes)).replace(u'\n', u'<br>'), p,
classId=u'itemNotesText')
# Add play length of media files.
if item.is_media() and self.metaDataCheckBox.isChecked():

View File

@ -35,6 +35,8 @@ class Ui_ServiceItemEditDialog(object):
def setupUi(self, serviceItemEditDialog):
serviceItemEditDialog.setObjectName(u'serviceItemEditDialog')
self.dialogLayout = QtGui.QGridLayout(serviceItemEditDialog)
self.dialogLayout.setContentsMargins(8, 8, 8, 8)
self.dialogLayout.setSpacing(8)
self.dialogLayout.setObjectName(u'dialogLayout')
self.listWidget = QtGui.QListWidget(serviceItemEditDialog)
self.listWidget.setAlternatingRowColors(True)

View File

@ -79,7 +79,7 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog):
if not item:
return
row = self.listWidget.row(item)
self.itemList.remove(self.itemList[row])
self.itemList.pop(row)
self.loadData()
if row == self.listWidget.count():
self.listWidget.setCurrentRow(row - 1)
@ -109,7 +109,7 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog):
return
row = self.listWidget.row(item)
temp = self.itemList[row]
self.itemList.remove(self.itemList[row])
self.itemList.pop(row)
if direction == u'up':
row -= 1
else:

View File

@ -24,6 +24,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import cgi
import cPickle
import logging
import os
@ -48,18 +49,18 @@ class ServiceManagerList(QtGui.QTreeWidget):
"""
Set up key bindings and mouse behaviour for the service list
"""
def __init__(self, mainwindow, parent=None, name=None):
def __init__(self, serviceManager, parent=None, name=None):
QtGui.QTreeWidget.__init__(self, parent)
self.mainwindow = mainwindow
self.serviceManager = serviceManager
def keyPressEvent(self, event):
if isinstance(event, QtGui.QKeyEvent):
# here accept the event and do something
if event.key() == QtCore.Qt.Key_Up:
self.mainwindow.onMoveSelectionUp()
self.serviceManager.onMoveSelectionUp()
event.accept()
elif event.key() == QtCore.Qt.Key_Down:
self.mainwindow.onMoveSelectionDown()
self.serviceManager.onMoveSelectionDown()
event.accept()
event.ignore()
else:
@ -408,20 +409,33 @@ class ServiceManager(QtGui.QWidget):
return False
self.newFile()
def onLoadServiceClicked(self):
def onLoadServiceClicked(self, loadFile=None):
"""
Loads the service file and saves the existing one it there is one
unchanged
``loadFile``
The service file to the loaded. Will be None is from menu so
selection will be required.
"""
if self.isModified():
result = self.saveModifiedService()
if result == QtGui.QMessageBox.Cancel:
return False
elif result == QtGui.QMessageBox.Save:
self.saveFile()
fileName = unicode(QtGui.QFileDialog.getOpenFileName(self.mainwindow,
translate('OpenLP.ServiceManager', 'Open File'),
SettingsManager.get_last_dir(
self.mainwindow.serviceSettingsSection),
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)')))
if not fileName:
return False
if not loadFile:
fileName = unicode(QtGui.QFileDialog.getOpenFileName(
self.mainwindow,
translate('OpenLP.ServiceManager', 'Open File'),
SettingsManager.get_last_dir(
self.mainwindow.serviceSettingsSection),
translate('OpenLP.ServiceManager',
'OpenLP Service Files (*.osz)')))
if not fileName:
return False
else:
fileName = loadFile
SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection,
split_filename(fileName)[0])
self.loadFile(fileName)
@ -474,6 +488,7 @@ class ServiceManager(QtGui.QWidget):
item[u'service_item'].get_service_repr()})
if not item[u'service_item'].uses_file():
continue
skipMissing = False
for frame in item[u'service_item'].get_frames():
if item[u'service_item'].is_image():
path_from = frame[u'path']
@ -482,25 +497,29 @@ class ServiceManager(QtGui.QWidget):
# Only write a file once
if path_from in write_list:
continue
file_size = os.path.getsize(path_from)
size_limit = 52428800 # 50MiB
#if file_size > size_limit:
# # File exeeds size_limit bytes, ask user
# message = unicode(translate('OpenLP.ServiceManager',
# 'Do you want to include \n%.1f MB file "%s"\n'
# 'into the service file?\nThis may take some time.\n\n'
# 'Please note that you need to\ntake care of that file'
# ' yourself,\nif you leave it out.')) % \
# (file_size/1048576, os.path.split(path_from)[1])
# ans = QtGui.QMessageBox.question(self.mainwindow,
# translate('OpenLP.ServiceManager', 'Including Large '
# 'File'), message, QtGui.QMessageBox.StandardButtons(
# QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel),
# QtGui.QMessageBox.Ok)
# if ans == QtGui.QMessageBox.Cancel:
# continue
write_list.append(path_from)
total_size += file_size
if not os.path.exists(path_from):
if not skipMissing:
Receiver.send_message(u'cursor_normal')
title = unicode(translate('OpenLP.ServiceManager',
'Service File Missing'))
message = unicode(translate('OpenLP.ServiceManager',
'File missing from service\n\n %s \n\n'
'Continue saving?' % path_from ))
answer = QtGui.QMessageBox.critical(self, title,
message,
QtGui.QMessageBox.StandardButtons(
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No |
QtGui.QMessageBox.YesToAll))
if answer == QtGui.QMessageBox.No:
self.mainwindow.finishedProgressBar()
return False
if answer == QtGui.QMessageBox.YesToAll:
skipMissing = True
Receiver.send_message(u'cursor_busy')
else:
file_size = os.path.getsize(path_from)
write_list.append(path_from)
total_size += file_size
log.debug(u'ServiceManager.saveFile - ZIP contents size is %i bytes' %
total_size)
service_content = cPickle.dumps(service)
@ -688,7 +707,7 @@ class ServiceManager(QtGui.QWidget):
QtGui.QAction, serviceItem[u'service_item'].theme)
if themeAction is not None:
themeAction.setChecked(True)
action = self.menu.exec_(self.serviceManagerList.mapToGlobal(point))
self.menu.exec_(self.serviceManagerList.mapToGlobal(point))
def onServiceItemNoteForm(self):
item = self.findServiceItem()[0]
@ -701,6 +720,9 @@ class ServiceManager(QtGui.QWidget):
self.setModified()
def onStartTimeForm(self):
"""
Opens a dialog to type in service item notes.
"""
item = self.findServiceItem()[0]
self.startTimeForm.item = self.serviceItems[item]
if self.startTimeForm.exec_():
@ -781,50 +803,25 @@ class ServiceManager(QtGui.QWidget):
def onMoveSelectionUp(self):
"""
Moves the selection up the window. Called by the up arrow.
Moves the cursor selection up the window.
Called by the up arrow.
"""
serviceIterator = QtGui.QTreeWidgetItemIterator(self.serviceManagerList)
tempItem = None
setLastItem = False
while serviceIterator.value():
if serviceIterator.value().isSelected() and tempItem is None:
setLastItem = True
serviceIterator.value().setSelected(False)
if serviceIterator.value().isSelected():
# We are on the first record
if tempItem:
tempItem.setSelected(True)
serviceIterator.value().setSelected(False)
else:
tempItem = serviceIterator.value()
lastItem = serviceIterator.value()
serviceIterator += 1
# Top Item was selected so set the last one
if setLastItem:
lastItem.setSelected(True)
self.setModified()
item = self.serviceManagerList.currentItem()
itemBefore = self.serviceManagerList.itemAbove(item)
if itemBefore is None:
return
self.serviceManagerList.setCurrentItem(itemBefore)
def onMoveSelectionDown(self):
"""
Moves the selection down the window. Called by the down arrow.
Moves the cursor selection down the window.
Called by the down arrow.
"""
serviceIterator = QtGui.QTreeWidgetItemIterator(self.serviceManagerList)
firstItem = None
setSelected = False
while serviceIterator.value():
if not firstItem:
firstItem = serviceIterator.value()
if setSelected:
setSelected = False
serviceIterator.value().setSelected(True)
elif serviceIterator.value() and \
serviceIterator.value().isSelected():
serviceIterator.value().setSelected(False)
setSelected = True
serviceIterator += 1
if setSelected:
firstItem.setSelected(True)
self.setModified()
item = self.serviceManagerList.currentItem()
itemAfter = self.serviceManagerList.itemBelow(item)
if itemAfter is None:
return
self.serviceManagerList.setCurrentItem(itemAfter)
def onCollapseAll(self):
"""
@ -832,7 +829,7 @@ class ServiceManager(QtGui.QWidget):
"""
for item in self.serviceItems:
item[u'expanded'] = False
self.regenerateServiceItems()
self.serviceManagerList.collapseAll()
def collapsed(self, item):
"""
@ -848,7 +845,7 @@ class ServiceManager(QtGui.QWidget):
"""
for item in self.serviceItems:
item[u'expanded'] = True
self.regenerateServiceItems()
self.serviceManagerList.expandAll()
def expanded(self, item):
"""
@ -856,19 +853,19 @@ class ServiceManager(QtGui.QWidget):
correct state.
"""
pos = item.data(0, QtCore.Qt.UserRole).toInt()[0]
self.serviceItems[pos -1 ][u'expanded'] = True
self.serviceItems[pos - 1][u'expanded'] = True
def onServiceTop(self):
"""
Move the current ServiceItem to the top of the list.
"""
item, child = self.findServiceItem()
if item < len(self.serviceItems) and item is not -1:
if item < len(self.serviceItems) and item != -1:
temp = self.serviceItems[item]
self.serviceItems.remove(self.serviceItems[item])
self.serviceItems.insert(0, temp)
self.repaintServiceList(0, child)
self.setModified()
self.setModified()
def onServiceUp(self):
"""
@ -880,31 +877,31 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems.remove(self.serviceItems[item])
self.serviceItems.insert(item - 1, temp)
self.repaintServiceList(item - 1, child)
self.setModified()
self.setModified()
def onServiceDown(self):
"""
Move the current ServiceItem one position down in the list.
"""
item, child = self.findServiceItem()
if item < len(self.serviceItems) and item is not -1:
if item < len(self.serviceItems) and item != -1:
temp = self.serviceItems[item]
self.serviceItems.remove(self.serviceItems[item])
self.serviceItems.insert(item + 1, temp)
self.repaintServiceList(item + 1, child)
self.setModified()
self.setModified()
def onServiceEnd(self):
"""
Move the current ServiceItem to the bottom of the list.
"""
item, child = self.findServiceItem()
if item < len(self.serviceItems) and item is not -1:
if item < len(self.serviceItems) and item != -1:
temp = self.serviceItems[item]
self.serviceItems.remove(self.serviceItems[item])
self.serviceItems.insert(len(self.serviceItems), temp)
self.repaintServiceList(len(self.serviceItems) - 1, child)
self.setModified()
self.setModified()
def onDeleteFromService(self):
"""
@ -914,7 +911,7 @@ class ServiceManager(QtGui.QWidget):
if item != -1:
self.serviceItems.remove(self.serviceItems[item])
self.repaintServiceList(item - 1, -1)
self.setModified()
self.setModified()
def repaintServiceList(self, serviceItem, serviceItemChild):
"""
@ -956,7 +953,19 @@ class ServiceManager(QtGui.QWidget):
treewidgetitem.setIcon(0,
build_icon(u':/general/general_delete.png'))
treewidgetitem.setText(0, serviceitem.get_display_title())
treewidgetitem.setToolTip(0, serviceitem.notes)
tips = []
if serviceitem.theme and serviceitem.theme != -1:
tips.append(u'<strong>%s:</strong> <em>%s</em>' %
(unicode(translate('OpenLP.ServiceManager', 'Slide theme')),
serviceitem.theme))
if serviceitem.notes:
tips.append(u'<strong>%s: </strong> %s' %
(unicode(translate('OpenLP.ServiceManager', 'Notes')),
cgi.escape(unicode(serviceitem.notes))))
if item[u'service_item'] \
.is_capable(ItemCapabilities.AllowsVariableStartTime):
tips.append(item[u'service_item'].get_media_time())
treewidgetitem.setToolTip(0, u'<br>'.join(tips))
treewidgetitem.setData(0, QtCore.Qt.UserRole,
QtCore.QVariant(item[u'order']))
treewidgetitem.setSelected(item[u'selected'])
@ -966,11 +975,6 @@ class ServiceManager(QtGui.QWidget):
text = frame[u'title'].replace(u'\n', u' ')
child.setText(0, text[:40])
child.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(count))
if item[u'service_item'] \
.is_capable(ItemCapabilities.AllowsVariableStartTime):
tip = item[u'service_item'].get_media_time()
if tip:
child.setToolTip(0, tip)
if serviceItem == itemcount:
if item[u'expanded'] and serviceItemChild == count:
self.serviceManagerList.setCurrentItem(child)
@ -1257,7 +1261,14 @@ class ServiceManager(QtGui.QWidget):
Handle of the event pint passed
"""
link = event.mimeData()
if link.hasText():
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
for url in event.mimeData().urls():
filename = unicode(url.toLocalFile())
if filename.endswith(u'.osz'):
self.onLoadServiceClicked(filename)
elif event.mimeData().hasText():
plugin = unicode(event.mimeData().text())
item = self.serviceManagerList.itemAt(event.pos())
# ServiceManager started the drag and drop
@ -1338,7 +1349,7 @@ class ServiceManager(QtGui.QWidget):
if not theme:
theme = None
item = self.findServiceItem()[0]
self.serviceItems[item][u'service_item'].theme = theme
self.serviceItems[item][u'service_item'].update_theme(theme)
self.regenerateServiceItems()
def _getParentItemData(self, item):

View File

@ -27,7 +27,7 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate
from openlp.core.lib import translate, SpellTextEdit
from openlp.core.lib.ui import create_accept_reject_button_box
class ServiceNoteForm(QtGui.QDialog):
@ -49,8 +49,10 @@ class ServiceNoteForm(QtGui.QDialog):
def setupUi(self):
self.setObjectName(u'serviceNoteEdit')
self.dialogLayout = QtGui.QVBoxLayout(self)
self.dialogLayout.setContentsMargins(8, 8, 8, 8)
self.dialogLayout.setSpacing(8)
self.dialogLayout.setObjectName(u'verticalLayout')
self.textEdit = QtGui.QTextEdit(self)
self.textEdit = SpellTextEdit(self, False)
self.textEdit.setObjectName(u'textEdit')
self.dialogLayout.addWidget(self.textEdit)
self.dialogLayout.addWidget(create_accept_reject_button_box(self))

View File

@ -29,7 +29,7 @@ The :mod:`settingsform` provides a user interface for the OpenLP settings
"""
import logging
from PyQt4 import QtGui, QtCore
from PyQt4 import QtGui
from openlp.core.lib import Receiver, build_icon, PluginStatus
from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab

View File

@ -123,7 +123,7 @@ class Ui_ShortcutListDialog(object):
def retranslateUi(self, shortcutListDialog):
shortcutListDialog.setWindowTitle(
translate('OpenLP.ShortcutListDialog', 'Customize Shortcuts'))
translate('OpenLP.ShortcutListDialog', 'Configure Shortcuts'))
self.descriptionLabel.setText(translate('OpenLP.ShortcutListDialog',
'Select an action and click one of the buttons below to start '
'capturing a new primary or alternate shortcut, respectively.'))

View File

@ -247,7 +247,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
alternate_label_text = action.defaultShortcuts[1].toString()
shortcuts = self._actionShortcuts(action)
# We do not want to loose pending changes, that is why we have to
# keep the text when, this function has not been triggered by a signal.
# keep the text when, this function has not been triggered by a
# signal.
if item is None:
primary_text = self.primaryPushButton.text()
alternate_text = self.alternatePushButton.text()
@ -280,7 +281,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
"""
Restores all default shortcuts.
"""
if self.buttonBox.buttonRole(button) != QtGui.QDialogButtonBox.ResetRole:
if self.buttonBox.buttonRole(button) != \
QtGui.QDialogButtonBox.ResetRole:
return
if QtGui.QMessageBox.question(self,
translate('OpenLP.ShortcutListDialog', 'Restore Default Shortcuts'),

View File

@ -27,12 +27,14 @@
import logging
import os
import time
import copy
from PyQt4 import QtCore, QtGui
from PyQt4.phonon import Phonon
from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \
ItemCapabilities, translate
from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \
translate, build_icon
from openlp.core.lib.ui import UiStrings, shortcut_action
from openlp.core.ui import HideMode, MainDisplay, ScreenList
from openlp.core.utils.actions import ActionList, CategoryOrder
@ -193,13 +195,11 @@ class SlideController(QtGui.QWidget):
self.playSlidesLoop = shortcut_action(self.playSlidesMenu,
u'playSlidesLoop', [], self.onPlaySlidesLoop,
u':/media/media_time.png', False, UiStrings().LiveToolbar)
self.playSlidesLoop.setText(
translate('OpenLP.SlideController', 'Play Slides in Loop'))
self.playSlidesLoop.setText(UiStrings().PlaySlidesInLoop)
self.playSlidesOnce = shortcut_action(self.playSlidesMenu,
u'playSlidesOnce', [], self.onPlaySlidesOnce,
u':/media/media_time.png', False, UiStrings().LiveToolbar)
self.playSlidesOnce.setText(
translate('OpenLP.SlideController', 'Play Slides to End'))
self.playSlidesOnce.setText(UiStrings().PlaySlidesToEnd)
if QtCore.QSettings().value(self.parent().generalSettingsSection +
u'/enable slide loop', QtCore.QVariant(True)).toBool():
self.playSlidesMenu.setDefaultAction(self.playSlidesLoop)
@ -412,9 +412,11 @@ class SlideController(QtGui.QWidget):
self.display.videoStop()
def servicePrevious(self):
time.sleep(0.1)
Receiver.send_message('servicemanager_previous_item')
def serviceNext(self):
time.sleep(0.1)
Receiver.send_message('servicemanager_next_item')
def screenSizeChanged(self):
@ -506,6 +508,11 @@ class SlideController(QtGui.QWidget):
self.mediabar.setVisible(False)
self.toolbar.makeWidgetsInvisible([u'Song Menu'])
self.toolbar.makeWidgetsInvisible(self.loopList)
# Reset the button
self.playSlidesOnce.setChecked(False)
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time.png'))
self.playSlidesLoop.setChecked(False)
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
if item.is_text():
if QtCore.QSettings().value(
self.parent().songsSettingsSection + u'/display songbar',
@ -597,7 +604,8 @@ class SlideController(QtGui.QWidget):
log.debug(u'processManagerItem live = %s' % self.isLive)
self.onStopLoop()
old_item = self.serviceItem
self.serviceItem = serviceItem
# take a copy not a link to the servicemeanager copy.
self.serviceItem = copy.copy(serviceItem)
if old_item and self.isLive and old_item.is_capable(
ItemCapabilities.ProvidesOwnDisplay):
self._resetBlank()
@ -1056,6 +1064,14 @@ class SlideController(QtGui.QWidget):
else:
self.playSlidesLoop.setChecked(checked)
log.debug(u'onPlaySlidesLoop %s' % checked)
if checked:
self.playSlidesLoop.setIcon(build_icon(u':/media/media_stop.png'))
self.playSlidesLoop.setText(UiStrings().StopPlaySlidesInLoop)
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time.png'))
self.playSlidesOnce.setText(UiStrings().PlaySlidesToEnd)
else:
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
self.playSlidesLoop.setText(UiStrings().PlaySlidesInLoop)
self.playSlidesMenu.setDefaultAction(self.playSlidesLoop)
self.playSlidesOnce.setChecked(False)
self.onToggleLoop()
@ -1069,6 +1085,14 @@ class SlideController(QtGui.QWidget):
else:
self.playSlidesOnce.setChecked(checked)
log.debug(u'onPlaySlidesOnce %s' % checked)
if checked:
self.playSlidesOnce.setIcon(build_icon(u':/media/media_stop.png'))
self.playSlidesOnce.setText(UiStrings().StopPlaySlidesToEnd)
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
self.playSlidesLoop.setText(UiStrings().PlaySlidesInLoop)
else:
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time'))
self.playSlidesOnce.setText(UiStrings().PlaySlidesToEnd)
self.playSlidesMenu.setDefaultAction(self.playSlidesOnce)
self.playSlidesLoop.setChecked(False)
self.onToggleLoop()

View File

@ -39,7 +39,8 @@ from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
check_directory_exists
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
BackgroundGradientType
from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
context_menu_action, context_menu_separator
from openlp.core.theme import Theme
from openlp.core.ui import FileRenameForm, ThemeForm
from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \
@ -104,25 +105,29 @@ class ThemeManager(QtGui.QWidget):
self.contextMenu)
# build the context menu
self.menu = QtGui.QMenu()
self.editAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Edit Theme'))
self.editAction.setIcon(build_icon(u':/themes/theme_edit.png'))
self.copyAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Copy Theme'))
self.copyAction.setIcon(build_icon(u':/themes/theme_edit.png'))
self.renameAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Rename Theme'))
self.renameAction.setIcon(build_icon(u':/themes/theme_edit.png'))
self.deleteAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Delete Theme'))
self.deleteAction.setIcon(build_icon(u':/general/general_delete.png'))
self.separator = self.menu.addSeparator()
self.globalAction = self.menu.addAction(
translate('OpenLP.ThemeManager', 'Set As &Global Default'))
self.globalAction.setIcon(build_icon(u':/general/general_export.png'))
self.exportAction = self.menu.addAction(
translate('OpenLP.ThemeManager', '&Export Theme'))
self.exportAction.setIcon(build_icon(u':/general/general_export.png'))
self.editAction = context_menu_action(
self.menu, u':/themes/theme_edit.png',
translate('OpenLP.ThemeManager', '&Edit Theme'), self.onEditTheme)
self.copyAction = context_menu_action(
self.menu, u':/themes/theme_edit.png',
translate('OpenLP.ThemeManager', '&Copy Theme'), self.onCopyTheme)
self.renameAction = context_menu_action(
self.menu, u':/themes/theme_edit.png',
translate('OpenLP.ThemeManager', '&Rename Theme'),
self.onRenameTheme)
self.deleteAction = context_menu_action(
self.menu, u':/general/general_delete.png',
translate('OpenLP.ThemeManager', '&Delete Theme'),
self.onDeleteTheme)
context_menu_separator(self.menu)
self.globalAction = context_menu_action(
self.menu, u':/general/general_export.png',
translate('OpenLP.ThemeManager', 'Set As &Global Default'),
self.changeGlobalFromScreen)
self.exportAction = context_menu_action(
self.menu, u':/general/general_export.png',
translate('OpenLP.ThemeManager', '&Export Theme'),
self.onExportTheme)
# Signals
QtCore.QObject.connect(self.themeListWidget,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
@ -198,19 +203,7 @@ class ThemeManager(QtGui.QWidget):
self.deleteAction.setVisible(True)
self.renameAction.setVisible(True)
self.globalAction.setVisible(True)
action = self.menu.exec_(self.themeListWidget.mapToGlobal(point))
if action == self.editAction:
self.onEditTheme()
if action == self.copyAction:
self.onCopyTheme()
if action == self.renameAction:
self.onRenameTheme()
if action == self.deleteAction:
self.onDeleteTheme()
if action == self.globalAction:
self.changeGlobalFromScreen()
if action == self.exportAction:
self.onExportTheme()
self.menu.exec_(self.themeListWidget.mapToGlobal(point))
def changeGlobalFromTab(self, themeName):
"""
@ -298,11 +291,10 @@ class ThemeManager(QtGui.QWidget):
Copies an existing theme to a new name
"""
item = self.themeListWidget.currentItem()
oldThemeName = unicode(
translate('OpenLP.ThemeManager', 'Copy of %s',
'Copy of <theme name>')) % unicode(
item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(oldThemeName)
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
self.fileRenameForm.fileNameEdit.setText(
unicode(translate('OpenLP.ThemeManager',
'Copy of %s','Copy of <theme name>')) % oldThemeName)
if self.fileRenameForm.exec_(True):
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
if self.checkIfThemeExists(newThemeName):

View File

@ -37,7 +37,7 @@ class ThemesTab(SettingsTab):
"""
def __init__(self, parent, mainwindow):
self.mainwindow = mainwindow
generalTranslated = translate('ThemeTab', 'Themes')
generalTranslated = translate('OpenLP.ThemesTab', 'Themes')
SettingsTab.__init__(self, parent, u'Themes', generalTranslated)
self.icon_path = u':/themes/theme_new.png'

View File

@ -53,6 +53,7 @@ APPLICATION_VERSION = {}
IMAGES_FILTER = None
UNO_CONNECTION_TYPE = u'pipe'
#UNO_CONNECTION_TYPE = u'socket'
VERSION_SPLITTER = re.compile(r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?')
class VersionThread(QtCore.QThread):
"""
@ -61,8 +62,6 @@ class VersionThread(QtCore.QThread):
"""
def __init__(self, parent):
QtCore.QThread.__init__(self, parent)
self.version_splitter = re.compile(
r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?')
def run(self):
"""
@ -73,7 +72,7 @@ class VersionThread(QtCore.QThread):
version = check_latest_version(app_version)
remote_version = {}
local_version = {}
match = self.version_splitter.match(version)
match = VERSION_SPLITTER.match(version)
if match:
remote_version[u'major'] = int(match.group(1))
remote_version[u'minor'] = int(match.group(2))
@ -82,7 +81,7 @@ class VersionThread(QtCore.QThread):
remote_version[u'revision'] = int(match.group(4))
else:
return
match = self.version_splitter.match(app_version[u'full'])
match = VERSION_SPLITTER.match(app_version[u'full'])
if match:
local_version[u'major'] = int(match.group(1))
local_version[u'minor'] = int(match.group(2))
@ -387,6 +386,17 @@ def split_filename(path):
else:
return os.path.split(path)
def clean_filename(filename):
"""
Removes invalid characters from the given ``filename``.
``filename``
The "dirty" file name to clean.
"""
if not isinstance(filename, unicode):
filename = unicode(filename, u'utf-8')
return re.sub(r'[/\\?*|<>\[\]":<>+%]+', u'_', filename).strip(u'_')
def delete_file(file_path_name):
"""
Deletes a file from the system.
@ -460,25 +470,6 @@ def file_is_unicode(filename):
return None
return ucsfile
def string_is_unicode(test_string):
"""
Makes sure a string is unicode.
``test_string``
The string to confirm is unicode.
"""
return_string = u''
if not test_string:
return return_string
if isinstance(test_string, unicode):
return_string = test_string
if not isinstance(test_string, unicode):
try:
return_string = unicode(test_string, u'utf-8')
except UnicodeError:
log.exception("Error encoding string to unicode")
return return_string
def get_uno_command():
"""
Returns the UNO command to launch an openoffice.org instance.
@ -511,5 +502,5 @@ from actions import ActionList
__all__ = [u'AppLocation', u'get_application_version', u'check_latest_version',
u'add_actions', u'get_filesystem_encoding', u'LanguageManager',
u'ActionList', u'get_web_page', u'file_is_unicode', u'string_is_unicode',
u'get_uno_command', u'get_uno_instance', u'delete_file']
u'ActionList', u'get_web_page', u'file_is_unicode', u'get_uno_command',
u'get_uno_instance', u'delete_file', u'clean_filename']

View File

@ -27,7 +27,7 @@
import logging
from PyQt4 import QtCore, QtGui
from PyQt4 import QtCore
from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.db import Manager
@ -43,7 +43,7 @@ class AlertsPlugin(Plugin):
log.info(u'Alerts Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Alerts', plugin_helpers,
Plugin.__init__(self, u'alerts', plugin_helpers,
settings_tab_class=AlertsTab)
self.weight = -3
self.icon_path = u':/plugins/plugin_alerts.png'
@ -104,7 +104,7 @@ class AlertsPlugin(Plugin):
def about(self):
about_text = translate('AlertsPlugin', '<strong>Alerts Plugin</strong>'
'<br />The alert plugin controls the displaying of nursery alerts '
'on the display screen')
'on the display screen.')
return about_text
def setPluginTextStrings(self):

View File

@ -176,8 +176,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
# We found '<>' in the alert text, but the ParameterEdit field is empty.
if text.find(u'<>') != -1 and not self.parameterEdit.text() and \
QtGui.QMessageBox.question(self,
translate('AlertPlugin.AlertForm', 'No Parameter Found'),
translate('AlertPlugin.AlertForm', 'You have not entered a '
translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
translate('AlertsPlugin.AlertForm', 'You have not entered a '
'parameter to be replaced.\nDo you want to continue anyway?'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
@ -187,8 +187,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
# in the alert text.
elif text.find(u'<>') == -1 and self.parameterEdit.text() and \
QtGui.QMessageBox.question(self,
translate('AlertPlugin.AlertForm', 'No Placeholder Found'),
translate('AlertPlugin.AlertForm', 'The alert text does not'
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
translate('AlertsPlugin.AlertForm', 'The alert text does not'
' contain \'<>\'.\nDo you want to continue anyway?'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:

View File

@ -44,85 +44,85 @@ class AlertsTab(SettingsTab):
self.fontGroupBox.setObjectName(u'fontGroupBox')
self.fontLayout = QtGui.QFormLayout(self.fontGroupBox)
self.fontLayout.setObjectName(u'fontLayout')
self.FontLabel = QtGui.QLabel(self.fontGroupBox)
self.FontLabel.setObjectName(u'FontLabel')
self.FontComboBox = QtGui.QFontComboBox(self.fontGroupBox)
self.FontComboBox.setObjectName(u'FontComboBox')
self.fontLayout.addRow(self.FontLabel, self.FontComboBox)
self.FontColorLabel = QtGui.QLabel(self.fontGroupBox)
self.FontColorLabel.setObjectName(u'FontColorLabel')
self.ColorLayout = QtGui.QHBoxLayout()
self.ColorLayout.setObjectName(u'ColorLayout')
self.FontColorButton = QtGui.QPushButton(self.fontGroupBox)
self.FontColorButton.setObjectName(u'FontColorButton')
self.ColorLayout.addWidget(self.FontColorButton)
self.ColorLayout.addSpacing(20)
self.BackgroundColorLabel = QtGui.QLabel(self.fontGroupBox)
self.BackgroundColorLabel.setObjectName(u'BackgroundColorLabel')
self.ColorLayout.addWidget(self.BackgroundColorLabel)
self.BackgroundColorButton = QtGui.QPushButton(self.fontGroupBox)
self.BackgroundColorButton.setObjectName(u'BackgroundColorButton')
self.ColorLayout.addWidget(self.BackgroundColorButton)
self.fontLayout.addRow(self.FontColorLabel, self.ColorLayout)
self.FontSizeLabel = QtGui.QLabel(self.fontGroupBox)
self.FontSizeLabel.setObjectName(u'FontSizeLabel')
self.FontSizeSpinBox = QtGui.QSpinBox(self.fontGroupBox)
self.FontSizeSpinBox.setObjectName(u'FontSizeSpinBox')
self.fontLayout.addRow(self.FontSizeLabel, self.FontSizeSpinBox)
self.TimeoutLabel = QtGui.QLabel(self.fontGroupBox)
self.TimeoutLabel.setObjectName(u'TimeoutLabel')
self.TimeoutSpinBox = QtGui.QSpinBox(self.fontGroupBox)
self.TimeoutSpinBox.setMaximum(180)
self.TimeoutSpinBox.setObjectName(u'TimeoutSpinBox')
self.fontLayout.addRow(self.TimeoutLabel, self.TimeoutSpinBox)
self.fontLabel = QtGui.QLabel(self.fontGroupBox)
self.fontLabel.setObjectName(u'fontLabel')
self.fontComboBox = QtGui.QFontComboBox(self.fontGroupBox)
self.fontComboBox.setObjectName(u'fontComboBox')
self.fontLayout.addRow(self.fontLabel, self.fontComboBox)
self.fontColorLabel = QtGui.QLabel(self.fontGroupBox)
self.fontColorLabel.setObjectName(u'fontColorLabel')
self.colorLayout = QtGui.QHBoxLayout()
self.colorLayout.setObjectName(u'colorLayout')
self.fontColorButton = QtGui.QPushButton(self.fontGroupBox)
self.fontColorButton.setObjectName(u'fontColorButton')
self.colorLayout.addWidget(self.fontColorButton)
self.colorLayout.addSpacing(20)
self.backgroundColorLabel = QtGui.QLabel(self.fontGroupBox)
self.backgroundColorLabel.setObjectName(u'backgroundColorLabel')
self.colorLayout.addWidget(self.backgroundColorLabel)
self.backgroundColorButton = QtGui.QPushButton(self.fontGroupBox)
self.backgroundColorButton.setObjectName(u'backgroundColorButton')
self.colorLayout.addWidget(self.backgroundColorButton)
self.fontLayout.addRow(self.fontColorLabel, self.colorLayout)
self.fontSizeLabel = QtGui.QLabel(self.fontGroupBox)
self.fontSizeLabel.setObjectName(u'fontSizeLabel')
self.fontSizeSpinBox = QtGui.QSpinBox(self.fontGroupBox)
self.fontSizeSpinBox.setObjectName(u'fontSizeSpinBox')
self.fontLayout.addRow(self.fontSizeLabel, self.fontSizeSpinBox)
self.timeoutLabel = QtGui.QLabel(self.fontGroupBox)
self.timeoutLabel.setObjectName(u'timeoutLabel')
self.timeoutSpinBox = QtGui.QSpinBox(self.fontGroupBox)
self.timeoutSpinBox.setMaximum(180)
self.timeoutSpinBox.setObjectName(u'timeoutSpinBox')
self.fontLayout.addRow(self.timeoutLabel, self.timeoutSpinBox)
create_valign_combo(self, self.fontGroupBox, self.fontLayout)
self.leftLayout.addWidget(self.fontGroupBox)
self.leftLayout.addStretch()
self.PreviewGroupBox = QtGui.QGroupBox(self.rightColumn)
self.PreviewGroupBox.setObjectName(u'PreviewGroupBox')
self.PreviewLayout = QtGui.QVBoxLayout(self.PreviewGroupBox)
self.PreviewLayout.setObjectName(u'PreviewLayout')
self.FontPreview = QtGui.QLineEdit(self.PreviewGroupBox)
self.FontPreview.setObjectName(u'FontPreview')
self.PreviewLayout.addWidget(self.FontPreview)
self.rightLayout.addWidget(self.PreviewGroupBox)
self.previewGroupBox = QtGui.QGroupBox(self.rightColumn)
self.previewGroupBox.setObjectName(u'previewGroupBox')
self.previewLayout = QtGui.QVBoxLayout(self.previewGroupBox)
self.previewLayout.setObjectName(u'previewLayout')
self.fontPreview = QtGui.QLineEdit(self.previewGroupBox)
self.fontPreview.setObjectName(u'fontPreview')
self.previewLayout.addWidget(self.fontPreview)
self.rightLayout.addWidget(self.previewGroupBox)
self.rightLayout.addStretch()
# Signals and slots
QtCore.QObject.connect(self.BackgroundColorButton,
QtCore.QObject.connect(self.backgroundColorButton,
QtCore.SIGNAL(u'pressed()'), self.onBackgroundColorButtonClicked)
QtCore.QObject.connect(self.FontColorButton,
QtCore.QObject.connect(self.fontColorButton,
QtCore.SIGNAL(u'pressed()'), self.onFontColorButtonClicked)
QtCore.QObject.connect(self.FontComboBox,
QtCore.QObject.connect(self.fontComboBox,
QtCore.SIGNAL(u'activated(int)'), self.onFontComboBoxClicked)
QtCore.QObject.connect(self.TimeoutSpinBox,
QtCore.QObject.connect(self.timeoutSpinBox,
QtCore.SIGNAL(u'valueChanged(int)'), self.onTimeoutSpinBoxChanged)
QtCore.QObject.connect(self.FontSizeSpinBox,
QtCore.QObject.connect(self.fontSizeSpinBox,
QtCore.SIGNAL(u'valueChanged(int)'), self.onFontSizeSpinBoxChanged)
def retranslateUi(self):
self.fontGroupBox.setTitle(
translate('AlertsPlugin.AlertsTab', 'Font'))
self.FontLabel.setText(
self.fontLabel.setText(
translate('AlertsPlugin.AlertsTab', 'Font name:'))
self.FontColorLabel.setText(
self.fontColorLabel.setText(
translate('AlertsPlugin.AlertsTab', 'Font color:'))
self.BackgroundColorLabel.setText(
self.backgroundColorLabel.setText(
translate('AlertsPlugin.AlertsTab', 'Background color:'))
self.FontSizeLabel.setText(
self.fontSizeLabel.setText(
translate('AlertsPlugin.AlertsTab', 'Font size:'))
self.FontSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit)
self.TimeoutLabel.setText(
self.fontSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit)
self.timeoutLabel.setText(
translate('AlertsPlugin.AlertsTab', 'Alert timeout:'))
self.TimeoutSpinBox.setSuffix(UiStrings().Seconds)
self.PreviewGroupBox.setTitle(UiStrings().Preview)
self.FontPreview.setText(UiStrings().OLPV2)
self.timeoutSpinBox.setSuffix(UiStrings().Seconds)
self.previewGroupBox.setTitle(UiStrings().Preview)
self.fontPreview.setText(UiStrings().OLPV2)
def onBackgroundColorButtonClicked(self):
new_color = QtGui.QColorDialog.getColor(
QtGui.QColor(self.bg_color), self)
if new_color.isValid():
self.bg_color = new_color.name()
self.BackgroundColorButton.setStyleSheet(
self.backgroundColorButton.setStyleSheet(
u'background-color: %s' % self.bg_color)
self.updateDisplay()
@ -134,15 +134,15 @@ class AlertsTab(SettingsTab):
QtGui.QColor(self.font_color), self)
if new_color.isValid():
self.font_color = new_color.name()
self.FontColorButton.setStyleSheet(
self.fontColorButton.setStyleSheet(
u'background-color: %s' % self.font_color)
self.updateDisplay()
def onTimeoutSpinBoxChanged(self):
self.timeout = self.TimeoutSpinBox.value()
self.timeout = self.timeoutSpinBox.value()
def onFontSizeSpinBoxChanged(self):
self.font_size = self.FontSizeSpinBox.value()
self.font_size = self.fontSizeSpinBox.value()
self.updateDisplay()
def load(self):
@ -160,16 +160,16 @@ class AlertsTab(SettingsTab):
self.location = settings.value(
u'location', QtCore.QVariant(1)).toInt()[0]
settings.endGroup()
self.FontSizeSpinBox.setValue(self.font_size)
self.TimeoutSpinBox.setValue(self.timeout)
self.FontColorButton.setStyleSheet(
self.fontSizeSpinBox.setValue(self.font_size)
self.timeoutSpinBox.setValue(self.timeout)
self.fontColorButton.setStyleSheet(
u'background-color: %s' % self.font_color)
self.BackgroundColorButton.setStyleSheet(
self.backgroundColorButton.setStyleSheet(
u'background-color: %s' % self.bg_color)
self.verticalComboBox.setCurrentIndex(self.location)
font = QtGui.QFont()
font.setFamily(self.font_face)
self.FontComboBox.setCurrentFont(font)
self.fontComboBox.setCurrentFont(font)
self.updateDisplay()
def save(self):
@ -178,7 +178,7 @@ class AlertsTab(SettingsTab):
settings.setValue(u'background color', QtCore.QVariant(self.bg_color))
settings.setValue(u'font color', QtCore.QVariant(self.font_color))
settings.setValue(u'font size', QtCore.QVariant(self.font_size))
self.font_face = self.FontComboBox.currentFont().family()
self.font_face = self.fontComboBox.currentFont().family()
settings.setValue(u'font face', QtCore.QVariant(self.font_face))
settings.setValue(u'timeout', QtCore.QVariant(self.timeout))
self.location = self.verticalComboBox.currentIndex()
@ -187,10 +187,10 @@ class AlertsTab(SettingsTab):
def updateDisplay(self):
font = QtGui.QFont()
font.setFamily(self.FontComboBox.currentFont().family())
font.setFamily(self.fontComboBox.currentFont().family())
font.setBold(True)
font.setPointSize(self.font_size)
self.FontPreview.setFont(font)
self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' %
self.fontPreview.setFont(font)
self.fontPreview.setStyleSheet(u'background-color: %s; color: %s' %
(self.bg_color, self.font_color))

View File

@ -41,7 +41,7 @@ class BiblePlugin(Plugin):
log.info(u'Bible Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Bibles', plugin_helpers,
Plugin.__init__(self, u'bibles', plugin_helpers,
BibleMediaItem, BiblesTab)
self.weight = -9
self.icon_path = u':/plugins/plugin_bibles.png'
@ -117,9 +117,9 @@ class BiblePlugin(Plugin):
self.toolsUpgradeItem = QtGui.QAction(tools_menu)
self.toolsUpgradeItem.setObjectName(u'toolsUpgradeItem')
self.toolsUpgradeItem.setText(
translate('BiblePlugin', '&Upgrade older Bibles'))
translate('BiblesPlugin', '&Upgrade older Bibles'))
self.toolsUpgradeItem.setStatusTip(
translate('BiblePlugin', 'Upgrade the Bible databases to the '
translate('BiblesPlugin', 'Upgrade the Bible databases to the '
'latest format.'))
tools_menu.addAction(self.toolsUpgradeItem)
QtCore.QObject.connect(self.toolsUpgradeItem,

View File

@ -27,7 +27,6 @@
"""
The bible import functions for OpenLP
"""
import csv
import logging
import os
import os.path
@ -39,7 +38,7 @@ from openlp.core.lib import Receiver, translate
from openlp.core.lib.db import delete_database
from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.utils import AppLocation, string_is_unicode
from openlp.core.utils import AppLocation
from openlp.plugins.bibles.lib.manager import BibleFormat
from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename

View File

@ -27,20 +27,19 @@
The bible import functions for OpenLP
"""
import logging
import os.path
import re
import os
import shutil
from tempfile import gettempdir
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver, SettingsManager, translate, \
check_directory_exists
from openlp.core.lib.db import delete_database
from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.utils import AppLocation, delete_file
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB,\
BiblesResourcesDB, clean_filename
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB, \
BiblesResourcesDB
from openlp.plugins.bibles.lib.http import BSExtract, BGExtract, CWExtract
log = logging.getLogger(__name__)
@ -70,8 +69,8 @@ class BibleUpgradeForm(OpenLPWizard):
self.mediaItem = bibleplugin.mediaItem
self.suffix = u'.sqlite'
self.settingsSection = u'bibles'
self.path = AppLocation.get_section_data_path(
self.settingsSection)
self.path = AppLocation.get_section_data_path(self.settingsSection)
self.temp_dir = os.path.join(gettempdir(), u'openlp')
self.files = self.manager.old_bible_databases
self.success = {}
self.newbibles = {}
@ -93,20 +92,6 @@ class BibleUpgradeForm(OpenLPWizard):
log.debug(u'Stopping import')
self.stop_import_flag = True
def onCheckBoxIndexChanged(self, index):
"""
Show/Hide warnings if CheckBox state has changed
"""
for number, filename in enumerate(self.files):
if not self.checkBox[number].checkState() == QtCore.Qt.Checked:
self.verticalWidget[number].hide()
self.formWidget[number].hide()
else:
version_name = unicode(self.versionNameEdit[number].text())
if self.manager.exists(version_name):
self.verticalWidget[number].show()
self.formWidget[number].show()
def reject(self):
"""
Stop the wizard on cancel button, close button or ESC key.
@ -115,8 +100,6 @@ class BibleUpgradeForm(OpenLPWizard):
self.stop_import_flag = True
if not self.currentPage() == self.progressPage:
self.done(QtGui.QDialog.Rejected)
else:
self.postWizard()
def onCurrentIdChanged(self, pageId):
"""
@ -126,7 +109,7 @@ class BibleUpgradeForm(OpenLPWizard):
self.preWizard()
self.performWizard()
self.postWizard()
elif self.page(pageId) == self.selectPage and self.maxBibles == 0:
elif self.page(pageId) == self.selectPage and not self.files:
self.next()
def onBackupBrowseButtonClicked(self):
@ -245,78 +228,13 @@ class BibleUpgradeForm(OpenLPWizard):
Add the content to the scrollArea.
"""
self.checkBox = {}
self.versionNameEdit = {}
self.versionNameLabel = {}
self.versionInfoLabel = {}
self.versionInfoPixmap = {}
self.verticalWidget = {}
self.horizontalLayout = {}
self.formWidget = {}
self.formLayoutAttention = {}
for number, filename in enumerate(self.files):
bible = OldBibleDB(self.mediaItem, path=self.path, file=filename[0])
self.checkBox[number] = QtGui.QCheckBox(self.scrollAreaContents)
checkBoxName = u'checkBox[%d]' % number
self.checkBox[number].setObjectName(checkBoxName)
self.checkBox[number].setObjectName(u'checkBox[%d]' % number)
self.checkBox[number].setText(bible.get_name())
self.checkBox[number].setCheckState(QtCore.Qt.Checked)
self.formLayout.addWidget(self.checkBox[number])
self.verticalWidget[number] = QtGui.QWidget(self.scrollAreaContents)
verticalWidgetName = u'verticalWidget[%d]' % number
self.verticalWidget[number].setObjectName(verticalWidgetName)
self.horizontalLayout[number] = QtGui.QHBoxLayout(
self.verticalWidget[number])
self.horizontalLayout[number].setContentsMargins(25, 0, 0, 0)
horizontalLayoutName = u'horizontalLayout[%d]' % number
self.horizontalLayout[number].setObjectName(horizontalLayoutName)
self.versionInfoPixmap[number] = QtGui.QLabel(
self.verticalWidget[number])
versionInfoPixmapName = u'versionInfoPixmap[%d]' % number
self.versionInfoPixmap[number].setObjectName(versionInfoPixmapName)
self.versionInfoPixmap[number].setPixmap(QtGui.QPixmap(
u':/bibles/bibles_upgrade_alert.png'))
self.versionInfoPixmap[number].setAlignment(QtCore.Qt.AlignRight)
self.horizontalLayout[number].addWidget(
self.versionInfoPixmap[number])
self.versionInfoLabel[number] = QtGui.QLabel(
self.verticalWidget[number])
versionInfoLabelName = u'versionInfoLabel[%d]' % number
self.versionInfoLabel[number].setObjectName(versionInfoLabelName)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(
self.versionInfoLabel[number].sizePolicy().hasHeightForWidth())
self.versionInfoLabel[number].setSizePolicy(sizePolicy)
self.horizontalLayout[number].addWidget(
self.versionInfoLabel[number])
self.formLayout.addWidget(self.verticalWidget[number])
self.formWidget[number] = QtGui.QWidget(self.scrollAreaContents)
formWidgetName = u'formWidget[%d]' % number
self.formWidget[number].setObjectName(formWidgetName)
self.formLayoutAttention[number] = QtGui.QFormLayout(
self.formWidget[number])
self.formLayoutAttention[number].setContentsMargins(25, 0, 0, 5)
formLayoutAttentionName = u'formLayoutAttention[%d]' % number
self.formLayoutAttention[number].setObjectName(
formLayoutAttentionName)
self.versionNameLabel[number] = QtGui.QLabel(
self.formWidget[number])
self.versionNameLabel[number].setObjectName(u'VersionNameLabel')
self.formLayoutAttention[number].setWidget(0,
QtGui.QFormLayout.LabelRole, self.versionNameLabel[number])
self.versionNameEdit[number] = QtGui.QLineEdit(
self.formWidget[number])
self.versionNameEdit[number].setObjectName(u'VersionNameEdit')
self.formLayoutAttention[number].setWidget(0,
QtGui.QFormLayout.FieldRole, self.versionNameEdit[number])
self.versionNameEdit[number].setText(bible.get_name())
self.formLayout.addWidget(self.formWidget[number])
# Set up the Signal for the checkbox.
QtCore.QObject.connect(self.checkBox[number],
QtCore.SIGNAL(u'stateChanged(int)'),
self.onCheckBoxIndexChanged)
self.spacerItem = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding)
self.formLayout.addItem(self.spacerItem)
@ -329,23 +247,6 @@ class BibleUpgradeForm(OpenLPWizard):
for number, filename in enumerate(self.files):
self.formLayout.removeWidget(self.checkBox[number])
self.checkBox[number].setParent(None)
self.horizontalLayout[number].removeWidget(
self.versionInfoPixmap[number])
self.versionInfoPixmap[number].setParent(None)
self.horizontalLayout[number].removeWidget(
self.versionInfoLabel[number])
self.versionInfoLabel[number].setParent(None)
self.formLayout.removeWidget(self.verticalWidget[number])
self.verticalWidget[number].setParent(None)
self.formLayoutAttention[number].removeWidget(
self.versionNameLabel[number])
self.versionNameLabel[number].setParent(None)
self.formLayoutAttention[number].removeWidget(
self.versionNameEdit[number])
self.formLayoutAttention[number].deleteLater()
self.versionNameEdit[number].setParent(None)
self.formLayout.removeWidget(self.formWidget[number])
self.formWidget[number].setParent(None)
self.formLayout.removeItem(self.spacerItem)
def retranslateUi(self):
@ -387,12 +288,6 @@ class BibleUpgradeForm(OpenLPWizard):
self.selectPage.setSubTitle(
translate('BiblesPlugin.UpgradeWizardForm',
'Please select the Bibles to upgrade'))
for number, bible in enumerate(self.files):
self.versionNameLabel[number].setText(
translate('BiblesPlugin.UpgradeWizardForm', 'Version name:'))
self.versionInfoLabel[number].setText(
translate('BiblesPlugin.UpgradeWizardForm', 'This '
'Bible still exists. Please change the name or uncheck it.'))
self.progressPage.setTitle(translate('BiblesPlugin.UpgradeWizardForm',
'Upgrading'))
self.progressPage.setSubTitle(
@ -413,7 +308,7 @@ class BibleUpgradeForm(OpenLPWizard):
if not backup_path:
critical_error_message_box(UiStrings().EmptyField,
translate('BiblesPlugin.UpgradeWizardForm',
'You need to specify a Backup Directory for your '
'You need to specify a backup directory for your '
'Bibles.'))
self.backupDirectoryEdit.setFocus()
return False
@ -427,58 +322,16 @@ class BibleUpgradeForm(OpenLPWizard):
return False
return True
elif self.currentPage() == self.selectPage:
check_directory_exists(self.temp_dir)
for number, filename in enumerate(self.files):
if not self.checkBox[number].checkState() == QtCore.Qt.Checked:
continue
version_name = unicode(self.versionNameEdit[number].text())
if not version_name:
critical_error_message_box(UiStrings().EmptyField,
translate('BiblesPlugin.UpgradeWizardForm',
'You need to specify a version name for your Bible.'))
self.versionNameEdit[number].setFocus()
return False
elif self.manager.exists(version_name):
critical_error_message_box(
translate('BiblesPlugin.UpgradeWizardForm',
'Bible Exists'),
translate('BiblesPlugin.UpgradeWizardForm',
'This Bible already exists. Please upgrade '
'a different Bible, delete the existing one or '
'uncheck.'))
self.versionNameEdit[number].setFocus()
return False
elif os.path.exists(os.path.join(self.path, clean_filename(
version_name))) and version_name == filename[1]:
newfilename = u'old_database_%s' % filename[0]
if not os.path.exists(os.path.join(self.path,
newfilename)):
os.rename(os.path.join(self.path, filename[0]),
os.path.join(self.path, newfilename))
self.files[number] = [newfilename, filename[1]]
continue
else:
critical_error_message_box(
translate('BiblesPlugin.UpgradeWizardForm',
'Bible Exists'),
translate('BiblesPlugin.UpgradeWizardForm',
'This Bible already exists. Please upgrade '
'a different Bible, delete the existing one or '
'uncheck.'))
self.verticalWidget[number].show()
self.formWidget[number].show()
self.versionNameEdit[number].setFocus()
return False
elif os.path.exists(os.path.join(self.path,
clean_filename(version_name))):
critical_error_message_box(
translate('BiblesPlugin.UpgradeWizardForm',
'Bible Exists'),
translate('BiblesPlugin.UpgradeWizardForm',
'This Bible already exists. Please upgrade '
'a different Bible, delete the existing one or '
'uncheck.'))
self.versionNameEdit[number].setFocus()
return False
# Move bibles to temp dir.
if not os.path.exists(os.path.join(self.temp_dir, filename[0])):
shutil.move(
os.path.join(self.path, filename[0]), self.temp_dir)
else:
delete_file(os.path.join(self.path, filename[0]))
return True
if self.currentPage() == self.progressPage:
return True
@ -497,16 +350,8 @@ class BibleUpgradeForm(OpenLPWizard):
self.files = self.manager.old_bible_databases
self.addScrollArea()
self.retranslateUi()
self.maxBibles = len(self.files)
for number, filename in enumerate(self.files):
self.checkBox[number].setCheckState(QtCore.Qt.Checked)
oldname = filename[1]
if self.manager.exists(oldname):
self.verticalWidget[number].show()
self.formWidget[number].show()
else:
self.verticalWidget[number].hide()
self.formWidget[number].hide()
self.progressBar.show()
self.restart()
self.finishButton.setVisible(False)
@ -518,9 +363,8 @@ class BibleUpgradeForm(OpenLPWizard):
Prepare the UI for the upgrade.
"""
OpenLPWizard.preWizard(self)
self.progressLabel.setText(translate(
'BiblesPlugin.UpgradeWizardForm',
'Starting Bible upgrade...'))
self.progressLabel.setText(
translate('BiblesPlugin.UpgradeWizardForm', 'Starting upgrade...'))
Receiver.send_message(u'openlp_process_events')
def performWizard(self):
@ -529,48 +373,42 @@ class BibleUpgradeForm(OpenLPWizard):
"""
self.include_webbible = False
proxy_server = None
if self.maxBibles == 0:
if not self.files:
self.progressLabel.setText(
translate('BiblesPlugin.UpgradeWizardForm', 'There are no '
'Bibles available to upgrade.'))
'Bibles that need to be upgraded.'))
self.progressBar.hide()
return
self.maxBibles = 0
max_bibles = 0
for number, file in enumerate(self.files):
if self.checkBox[number].checkState() == QtCore.Qt.Checked:
self.maxBibles += 1
number = 0
for biblenumber, filename in enumerate(self.files):
max_bibles += 1
oldBible = None
for number, filename in enumerate(self.files):
# Close the previous bible's connection.
if oldBible is not None:
oldBible.close_connection()
# Set to None to make obvious that we have already closed the
# database.
oldBible = None
if self.stop_import_flag:
bible_failed = True
self.success[number] = False
break
bible_failed = False
self.success[biblenumber] = False
if not self.checkBox[biblenumber].checkState() == QtCore.Qt.Checked:
if not self.checkBox[number].checkState() == QtCore.Qt.Checked:
self.success[number] = False
continue
self.progressBar.reset()
oldbible = OldBibleDB(self.mediaItem, path=self.path,
oldBible = OldBibleDB(self.mediaItem, path=self.temp_dir,
file=filename[0])
name = filename[1]
if name is None:
delete_file(os.path.join(self.path, filename[0]))
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed')) %
(number + 1, self.maxBibles, name),
self.progressBar.maximum() - self.progressBar.value())
number += 1
continue
self.progressLabel.setText(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nUpgrading ...')) %
(number + 1, self.maxBibles, name))
if os.path.exists(os.path.join(self.path, filename[0])):
name = unicode(self.versionNameEdit[biblenumber].text())
(number + 1, max_bibles, name))
self.newbibles[number] = BibleDB(self.mediaItem, path=self.path,
name=name)
name=name, file=filename[0])
self.newbibles[number].register(self.plugin.upgrade_wizard)
metadata = oldbible.get_metadata()
metadata = oldBible.get_metadata()
webbible = False
meta_data = {}
for meta in metadata:
@ -597,7 +435,7 @@ class BibleUpgradeForm(OpenLPWizard):
u'name: "%s" failed' % (
meta_data[u'download source'],
meta_data[u'download name']))
delete_database(self.path, clean_filename(name))
self.newbibles[number].session.close()
del self.newbibles[number]
critical_error_message_box(
translate('BiblesPlugin.UpgradeWizardForm',
@ -608,9 +446,9 @@ class BibleUpgradeForm(OpenLPWizard):
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed')) %
(number + 1, self.maxBibles, name),
(number + 1, max_bibles, name),
self.progressBar.maximum() - self.progressBar.value())
number += 1
self.success[number] = False
continue
bible = BiblesResourcesDB.get_webbible(
meta_data[u'download name'],
@ -623,25 +461,25 @@ class BibleUpgradeForm(OpenLPWizard):
language_id = self.newbibles[number].get_language(name)
if not language_id:
log.warn(u'Upgrading from "%s" failed' % filename[0])
delete_database(self.path, clean_filename(name))
self.newbibles[number].session.close()
del self.newbibles[number]
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed')) %
(number + 1, self.maxBibles, name),
(number + 1, max_bibles, name),
self.progressBar.maximum() - self.progressBar.value())
number += 1
self.success[number] = False
continue
self.progressBar.setMaximum(len(books))
for book in books:
if self.stop_import_flag:
bible_failed = True
self.success[number] = False
break
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\n'
'Upgrading %s ...')) %
(number + 1, self.maxBibles, name, book))
(number + 1, max_bibles, name, book))
book_ref_id = self.newbibles[number].\
get_book_ref_id_by_name(book, len(books), language_id)
if not book_ref_id:
@ -649,24 +487,24 @@ class BibleUpgradeForm(OpenLPWizard):
u'name: "%s" aborted by user' % (
meta_data[u'download source'],
meta_data[u'download name']))
delete_database(self.path, clean_filename(name))
self.newbibles[number].session.close()
del self.newbibles[number]
bible_failed = True
self.success[number] = False
break
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.newbibles[number].create_book(book,
book_ref_id, book_details[u'testament_id'])
# Try to import still downloaded verses
oldbook = oldbible.get_book(book)
# Try to import already downloaded verses.
oldbook = oldBible.get_book(book)
if oldbook:
verses = oldbible.get_verses(oldbook[u'id'])
verses = oldBible.get_verses(oldbook[u'id'])
if not verses:
log.warn(u'No verses found to import for book '
u'"%s"', book)
continue
for verse in verses:
if self.stop_import_flag:
bible_failed = True
self.success[number] = False
break
self.newbibles[number].create_verse(db_book.id,
int(verse[u'chapter']),
@ -680,40 +518,40 @@ class BibleUpgradeForm(OpenLPWizard):
language_id = self.newbibles[number].get_language(name)
if not language_id:
log.warn(u'Upgrading books from "%s" failed' % name)
delete_database(self.path, clean_filename(name))
self.newbibles[number].session.close()
del self.newbibles[number]
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed')) %
(number + 1, self.maxBibles, name),
(number + 1, max_bibles, name),
self.progressBar.maximum() - self.progressBar.value())
number += 1
self.success[number] = False
continue
books = oldbible.get_books()
books = oldBible.get_books()
self.progressBar.setMaximum(len(books))
for book in books:
if self.stop_import_flag:
bible_failed = True
self.success[number] = False
break
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\n'
'Upgrading %s ...')) %
(number + 1, self.maxBibles, name, book[u'name']))
(number + 1, max_bibles, name, book[u'name']))
book_ref_id = self.newbibles[number].\
get_book_ref_id_by_name(book[u'name'], len(books),
language_id)
if not book_ref_id:
log.warn(u'Upgrading books from %s " '\
'failed - aborted by user' % name)
delete_database(self.path, clean_filename(name))
self.newbibles[number].session.close()
del self.newbibles[number]
bible_failed = True
self.success[number] = False
break
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
db_book = self.newbibles[number].create_book(book[u'name'],
book_ref_id, book_details[u'testament_id'])
verses = oldbible.get_verses(book[u'id'])
verses = oldBible.get_verses(book[u'id'])
if not verses:
log.warn(u'No verses found to import for book '
u'"%s"', book[u'name'])
@ -721,31 +559,32 @@ class BibleUpgradeForm(OpenLPWizard):
continue
for verse in verses:
if self.stop_import_flag:
bible_failed = True
self.success[number] = False
break
self.newbibles[number].create_verse(db_book.id,
int(verse[u'chapter']),
int(verse[u'verse']), unicode(verse[u'text']))
Receiver.send_message(u'openlp_process_events')
self.newbibles[number].session.commit()
if not bible_failed:
if self.success.has_key(number) and not self.success[number]:
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed')) %
(number + 1, max_bibles, name),
self.progressBar.maximum() - self.progressBar.value())
else:
self.success[number] = True
self.newbibles[number].create_meta(u'Version', name)
oldbible.close_connection()
delete_file(os.path.join(self.path, filename[0]))
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\n'
'Complete')) %
(number + 1, self.maxBibles, name))
self.success[biblenumber] = True
else:
self.incrementProgressBar(unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
'Upgrading Bible %s of %s: "%s"\nFailed')) %
(number + 1, self.maxBibles, name),
self.progressBar.maximum() - self.progressBar.value())
delete_database(self.path, clean_filename(name))
number += 1
(number + 1, max_bibles, name))
if self.newbibles.has_key(number):
self.newbibles[number].session.close()
# Close the last bible's connection if possible.
if oldBible is not None:
oldBible.close_connection()
def postWizard(self):
"""
@ -754,10 +593,14 @@ class BibleUpgradeForm(OpenLPWizard):
successful_import = 0
failed_import = 0
for number, filename in enumerate(self.files):
if number in self.success and self.success[number] == True:
if self.success.has_key(number) and self.success[number]:
successful_import += 1
elif self.checkBox[number].checkState() == QtCore.Qt.Checked:
failed_import += 1
# Delete upgraded (but not complete, corrupted, ...) bible.
delete_file(os.path.join(self.path, filename[0]))
# Copy not upgraded bible back.
shutil.move(os.path.join(self.temp_dir, filename[0]), self.path)
if failed_import > 0:
failed_import_text = unicode(translate(
'BiblesPlugin.UpgradeWizardForm',
@ -778,7 +621,8 @@ class BibleUpgradeForm(OpenLPWizard):
'Bible(s): %s successful%s')) % (successful_import,
failed_import_text))
else:
self.progressLabel.setText(
translate('BiblesPlugin.UpgradeWizardForm', 'Upgrade '
'failed.'))
self.progressLabel.setText(translate(
'BiblesPlugin.UpgradeWizardForm', 'Upgrade failed.'))
# Remove temp directory.
shutil.rmtree(self.temp_dir, True)
OpenLPWizard.postWizard(self)

View File

@ -44,8 +44,8 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
Class to manage a dialog which ask the user for a language.
"""
log.info(u'LanguageForm loaded')
def __init__(self, parent = None):
def __init__(self, parent=None):
"""
Constructor
"""
@ -57,12 +57,11 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
if bible_name:
self.bibleLabel.setText(unicode(bible_name))
items = BiblesResourcesDB.get_languages()
for item in items:
self.languageComboBox.addItem(item[u'name'])
self.languageComboBox.addItems([item[u'name'] for item in items])
return QDialog.exec_(self)
def accept(self):
if self.languageComboBox.currentText() == u'':
if not self.languageComboBox.currentText():
critical_error_message_box(
message=translate('BiblesPlugin.LanguageForm',
'You need to choose a language.'))

View File

@ -115,7 +115,8 @@ class CSVBible(BibleDB):
if self.stop_import_flag:
break
self.wizard.incrementProgressBar(unicode(
translate('BibleDB.Wizard', 'Importing books... %s')) %
translate('BiblesPlugin.CSVBible',
'Importing books... %s')) %
unicode(line[2], details['encoding']))
book_ref_id = self.get_book_ref_id_by_name(
unicode(line[2], details['encoding']), 67, language_id)
@ -155,7 +156,7 @@ class CSVBible(BibleDB):
book = self.get_book(line_book)
book_ptr = book.name
self.wizard.incrementProgressBar(unicode(translate(
'BibleDB.Wizard', 'Importing verses from %s...',
'BiblesPlugin.CSVBible', 'Importing verses from %s...',
'Importing verses from <book name>...')) % book.name)
self.session.commit()
try:
@ -163,7 +164,7 @@ class CSVBible(BibleDB):
except UnicodeError:
verse_text = unicode(line[3], u'cp1252')
self.create_verse(book.id, line[1], line[2], verse_text)
self.wizard.incrementProgressBar(translate('BibleDB.Wizard',
self.wizard.incrementProgressBar(translate('BiblesPlugin.CSVBible',
'Importing verses... done.'))
Receiver.send_message(u'openlp_process_events')
self.session.commit()

View File

@ -36,10 +36,10 @@ from sqlalchemy import Column, ForeignKey, or_, Table, types
from sqlalchemy.orm import class_mapper, mapper, relation
from sqlalchemy.orm.exc import UnmappedClassError
from openlp.core.lib import Receiver, translate, check_directory_exists
from openlp.core.lib import Receiver, translate
from openlp.core.lib.db import BaseModel, init_db, Manager
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.utils import AppLocation
from openlp.core.utils import AppLocation, clean_filename
log = logging.getLogger(__name__)
@ -63,19 +63,6 @@ class Verse(BaseModel):
"""
pass
def clean_filename(filename):
"""
Clean up the version name of the Bible and convert it into a valid
file name.
``filename``
The "dirty" file name or version name.
"""
if not isinstance(filename, unicode):
filename = unicode(filename, u'utf-8')
filename = re.sub(r'[^\w]+', u'_', filename).strip(u'_')
return filename + u'.sqlite'
def init_schema(url):
"""
Setup a bible database connection and initialise the database schema.
@ -158,7 +145,7 @@ class BibleDB(QtCore.QObject, Manager):
self.name = kwargs[u'name']
if not isinstance(self.name, unicode):
self.name = unicode(self.name, u'utf-8')
self.file = clean_filename(self.name)
self.file = clean_filename(self.name) + u'.sqlite'
if u'file' in kwargs:
self.file = kwargs[u'file']
Manager.__init__(self, u'bibles', init_schema, self.file)
@ -210,7 +197,7 @@ class BibleDB(QtCore.QObject, Manager):
The book_reference_id from bibles_resources.sqlite of the book.
``testament``
*Defaults to 1.* The testament_reference_id from
*Defaults to 1.* The testament_reference_id from
bibles_resources.sqlite of the testament this book belongs to.
"""
log.debug(u'BibleDB.create_book("%s", "%s")', name, bk_ref_id)
@ -329,7 +316,7 @@ class BibleDB(QtCore.QObject, Manager):
return self.get_object_filtered(Book, Book.book_reference_id.like(id))
def get_book_ref_id_by_name(self, book, maxbooks, language_id=None):
log.debug(u'BibleDB.get_book_ref_id_by_name:("%s", "%s")', book,
log.debug(u'BibleDB.get_book_ref_id_by_name:("%s", "%s")', book,
language_id)
if BiblesResourcesDB.get_book(book, True):
book_temp = BiblesResourcesDB.get_book(book, True)
@ -471,7 +458,7 @@ class BibleDB(QtCore.QObject, Manager):
def get_language(self, bible_name=None):
"""
If no language is given it calls a dialog window where the user could
If no language is given it calls a dialog window where the user could
select the bible language.
Return the language id of a bible.
@ -521,9 +508,9 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
some resources which are used in the Bibles plugin.
A wrapper class around a small SQLite database which contains the download
resources, a biblelist from the different download resources, the books,
chapter counts and verse counts for the web download Bibles, a language
reference, the testament reference and some alternative book names. This
class contains a singleton "cursor" so that only one connection to the
chapter counts and verse counts for the web download Bibles, a language
reference, the testament reference and some alternative book names. This
class contains a singleton "cursor" so that only one connection to the
SQLite database is ever used.
"""
cursor = None
@ -582,7 +569,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
``name``
The name or abbreviation of the book.
``lower``
True if the comparsion should be only lowercase
"""
@ -592,7 +579,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
if lower:
books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, '
u'abbreviation, chapters FROM book_reference WHERE '
u'LOWER(name) = ? OR LOWER(abbreviation) = ?',
u'LOWER(name) = ? OR LOWER(abbreviation) = ?',
(name.lower(), name.lower()))
else:
books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, '
@ -621,7 +608,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
if not isinstance(id, int):
id = int(id)
books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, '
u'abbreviation, chapters FROM book_reference WHERE id = ?',
u'abbreviation, chapters FROM book_reference WHERE id = ?',
(id, ))
if books:
return {
@ -645,12 +632,12 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
``chapter``
The chapter number.
"""
log.debug(u'BiblesResourcesDB.get_chapter("%s", "%s")', book_id,
log.debug(u'BiblesResourcesDB.get_chapter("%s", "%s")', book_id,
chapter)
if not isinstance(chapter, int):
chapter = int(chapter)
chapters = BiblesResourcesDB.run_sql(u'SELECT id, book_reference_id, '
u'chapter, verse_count FROM chapters WHERE book_reference_id = ?',
u'chapter, verse_count FROM chapters WHERE book_reference_id = ?',
(book_id,))
if chapters:
return {
@ -687,7 +674,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
``chapter``
The number of the chapter.
"""
log.debug(u'BiblesResourcesDB.get_verse_count("%s", "%s")', book_id,
log.debug(u'BiblesResourcesDB.get_verse_count("%s", "%s")', book_id,
chapter)
details = BiblesResourcesDB.get_chapter(book_id, chapter)
if details:
@ -715,7 +702,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
}
else:
return None
@staticmethod
def get_webbibles(source):
"""
@ -737,7 +724,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
u'id': bible[0],
u'name': bible[1],
u'abbreviation': bible[2],
u'language_id': bible[3],
u'language_id': bible[3],
u'download_source_id': bible[4]
}
for bible in bibles
@ -752,11 +739,11 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
``abbreviation``
The abbreviation of the webbible.
``source``
The source of the webbible.
"""
log.debug(u'BiblesResourcesDB.get_webbibles("%s", "%s")', abbreviation,
log.debug(u'BiblesResourcesDB.get_webbibles("%s", "%s")', abbreviation,
source)
if not isinstance(abbreviation, unicode):
abbreviation = unicode(abbreviation)
@ -765,14 +752,14 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
source = BiblesResourcesDB.get_download_source(source)
bible = BiblesResourcesDB.run_sql(u'SELECT id, name, abbreviation, '
u'language_id, download_source_id FROM webbibles WHERE '
u'download_source_id = ? AND abbreviation = ?', (source[u'id'],
u'download_source_id = ? AND abbreviation = ?', (source[u'id'],
abbreviation))
if bible:
return {
u'id': bible[0][0],
u'name': bible[0][1],
u'abbreviation': bible[0][2],
u'language_id': bible[0][3],
u'language_id': bible[0][3],
u'download_source_id': bible[0][4]
}
else:
@ -785,11 +772,11 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
``name``
The name to search the id.
``language_id``
The language_id for which language should be searched
"""
log.debug(u'BiblesResourcesDB.get_alternative_book_name("%s", "%s")',
log.debug(u'BiblesResourcesDB.get_alternative_book_name("%s", "%s")',
name, language_id)
if language_id:
books = BiblesResourcesDB.run_sql(u'SELECT book_reference_id, name '
@ -806,7 +793,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
@staticmethod
def get_language(name):
"""
Return a dict containing the language id, name and code by name or
Return a dict containing the language id, name and code by name or
abbreviation.
``name``
@ -865,7 +852,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
class AlternativeBookNamesDB(QtCore.QObject, Manager):
"""
This class represents a database-bound alternative book names system.
This class represents a database-bound alternative book names system.
"""
cursor = None
conn = None
@ -874,7 +861,7 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
def get_cursor():
"""
Return the cursor object. Instantiate one if it doesn't exist yet.
If necessary loads up the database and creates the tables if the
If necessary loads up the database and creates the tables if the
database doesn't exist.
"""
if AlternativeBookNamesDB.cursor is None:
@ -904,7 +891,7 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
``parameters``
Any variable parameters to add to the query
``commit``
If a commit statement is necessary this should be True.
"""
@ -921,11 +908,11 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
``name``
The name to search the id.
``language_id``
The language_id for which language should be searched
"""
log.debug(u'AlternativeBookNamesDB.get_book_reference_id("%s", "%s")',
log.debug(u'AlternativeBookNamesDB.get_book_reference_id("%s", "%s")',
name, language_id)
if language_id:
books = AlternativeBookNamesDB.run_sql(u'SELECT book_reference_id, '
@ -962,11 +949,11 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
class OldBibleDB(QtCore.QObject, Manager):
"""
This class conects to the old bible databases to reimport them to the new
This class conects to the old bible databases to reimport them to the new
database scheme.
"""
cursor = None
def __init__(self, parent, **kwargs):
"""
The constructor loads up the database and creates and initialises the

View File

@ -29,9 +29,7 @@ The :mod:`http` module enables OpenLP to retrieve scripture from bible
websites.
"""
import logging
import os
import re
import sqlite3
import socket
import urllib
from HTMLParser import HTMLParseError
@ -40,7 +38,7 @@ from BeautifulSoup import BeautifulSoup, NavigableString, Tag
from openlp.core.lib import Receiver, translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.utils import AppLocation, get_web_page
from openlp.core.utils import get_web_page
from openlp.plugins.bibles.lib import SearchResults
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB, \
Book
@ -69,10 +67,10 @@ class BGExtract(object):
``chapter``
Chapter number.
"""
log.debug(u'BGExtract.get_bible_chapter("%s", "%s", "%s")', version,
log.debug(u'BGExtract.get_bible_chapter("%s", "%s", "%s")', version,
bookname, chapter)
urlbookname = urllib.quote(bookname.encode("utf-8"))
url_params = u'search=%s+%s&version=%s' % (urlbookname, chapter,
url_params = u'search=%s+%s&version=%s' % (urlbookname, chapter,
version)
cleaner = [(re.compile('&nbsp;|<br />|\'\+\''), lambda match: '')]
soup = get_soup_for_bible_ref(
@ -147,7 +145,10 @@ class BGExtract(object):
send_error_message(u'download')
return None
page_source = page.read()
page_source = unicode(page_source, 'utf8')
try:
page_source = unicode(page_source, u'utf8')
except UnicodeDecodeError:
page_source = unicode(page_source, u'cp1251')
page_source_temp = re.search(u'<table .*?class="infotable".*?>.*?'\
u'</table>', page_source, re.DOTALL)
if page_source_temp:
@ -200,7 +201,7 @@ class BSExtract(object):
``chapter``
Chapter number
"""
log.debug(u'BSExtract.get_bible_chapter("%s", "%s", "%s")', version,
log.debug(u'BSExtract.get_bible_chapter("%s", "%s", "%s")', version,
bookname, chapter)
urlversion = urllib.quote(version.encode("utf-8"))
urlbookname = urllib.quote(bookname.encode("utf-8"))
@ -227,7 +228,7 @@ class BSExtract(object):
def get_books_from_http(self, version):
"""
Load a list of all books a Bible contains from Bibleserver mobile
Load a list of all books a Bible contains from Bibleserver mobile
website.
``version``
@ -273,7 +274,7 @@ class CWExtract(object):
``chapter``
Chapter number
"""
log.debug(u'CWExtract.get_bible_chapter("%s", "%s", "%s")', version,
log.debug(u'CWExtract.get_bible_chapter("%s", "%s", "%s")', version,
bookname, chapter)
urlbookname = bookname.replace(u' ', u'-')
urlbookname = urlbookname.lower()
@ -386,7 +387,7 @@ class HTTPBible(BibleDB):
"""
self.wizard.progressBar.setMaximum(68)
self.wizard.incrementProgressBar(unicode(translate(
'BiblesPlugin.HTTPBible',
'BiblesPlugin.HTTPBible',
'Registering Bible and loading books...')))
self.create_meta(u'download source', self.download_source)
self.create_meta(u'download name', self.download_name)
@ -412,7 +413,7 @@ class HTTPBible(BibleDB):
self.wizard.progressBar.setMaximum(len(books)+2)
self.wizard.incrementProgressBar(unicode(translate(
'BiblesPlugin.HTTPBible', 'Registering Language...')))
bible = BiblesResourcesDB.get_webbible(self.download_name,
bible = BiblesResourcesDB.get_webbible(self.download_name,
self.download_source.lower())
if bible[u'language_id']:
language_id = bible[u'language_id']
@ -429,14 +430,14 @@ class HTTPBible(BibleDB):
self.wizard.incrementProgressBar(unicode(translate(
'BiblesPlugin.HTTPBible', 'Importing %s...',
'Importing <book name>...')) % book)
book_ref_id = self.get_book_ref_id_by_name(book, len(books),
book_ref_id = self.get_book_ref_id_by_name(book, len(books),
language_id)
if not book_ref_id:
log.exception(u'Importing books from %s - download name: "%s" '\
'failed' % (self.download_source, self.download_name))
return False
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
log.debug(u'Book details: Name:%s; id:%s; testament_id:%s',
log.debug(u'Book details: Name:%s; id:%s; testament_id:%s',
book, book_ref_id, book_details[u'testament_id'])
self.create_book(book, book_ref_id, book_details[u'testament_id'])
if self.stop_import_flag:
@ -521,7 +522,7 @@ class HTTPBible(BibleDB):
def get_chapter_count(self, book):
"""
Return the number of chapters in a particular book.
``book``
The book object to get the chapter count for.
"""
@ -594,14 +595,14 @@ def send_error_message(error_type):
"""
if error_type == u'download':
critical_error_message_box(
translate('BiblePlugin.HTTPBible', 'Download Error'),
translate('BiblePlugin.HTTPBible', 'There was a '
translate('BiblesPlugin.HTTPBible', 'Download Error'),
translate('BiblesPlugin.HTTPBible', 'There was a '
'problem downloading your verse selection. Please check your '
'Internet connection, and if this error continues to occur '
'please consider reporting a bug.'))
elif error_type == u'parse':
critical_error_message_box(
translate('BiblePlugin.HTTPBible', 'Parse Error'),
translate('BiblePlugin.HTTPBible', 'There was a '
translate('BiblesPlugin.HTTPBible', 'Parse Error'),
translate('BiblesPlugin.HTTPBible', 'There was a '
'problem extracting your verse selection. If this error continues '
'to occur please consider reporting a bug.'))

View File

@ -34,7 +34,7 @@ from openlp.core.lib import Receiver, SettingsManager, translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.utils import AppLocation, delete_file
from openlp.plugins.bibles.lib import parse_reference
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
from csvbible import CSVBible
from http import HTTPBible
from opensong import OpenSongBible
@ -151,9 +151,10 @@ class BibleManager(object):
name = bible.get_name()
# Remove corrupted files.
if name is None:
bible.session.close()
delete_file(os.path.join(self.path, filename))
continue
# Find old database versions
# Find old database versions.
if bible.is_old_database():
self.old_bible_databases.append([filename, name])
bible.session.close()
@ -220,7 +221,7 @@ class BibleManager(object):
return [
{
u'name': book.name,
u'book_reference_id': book.book_reference_id,
u'book_reference_id': book.book_reference_id,
u'chapters': self.db_cache[bible].get_chapter_count(book)
}
for book in self.db_cache[bible].get_books()
@ -229,10 +230,10 @@ class BibleManager(object):
def get_chapter_count(self, bible, book):
"""
Returns the number of Chapters for a given book.
``bible``
Unicode. The Bible to get the list of books from.
``book``
The book object to get the chapter count for.
"""
@ -295,7 +296,7 @@ class BibleManager(object):
if db_book:
book_id = db_book.book_reference_id
log.debug(u'Book name corrected to "%s"', db_book.name)
new_reflist.append((book_id, item[1], item[2],
new_reflist.append((book_id, item[1], item[2],
item[3]))
else:
log.debug(u'OpenLP failed to find book %s', item[0])

View File

@ -34,7 +34,8 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
translate
from openlp.core.lib.searchedit import SearchEdit
from openlp.core.lib.ui import UiStrings, add_widget_completer, \
media_item_combo_box, critical_error_message_box, find_and_set_in_combo_box
media_item_combo_box, critical_error_message_box, \
find_and_set_in_combo_box, build_icon
from openlp.plugins.bibles.forms import BibleImportForm
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, \
VerseReferenceList, get_reference_match
@ -57,8 +58,8 @@ class BibleMediaItem(MediaManagerItem):
def __init__(self, parent, plugin, icon):
self.IconPath = u'songs/song'
self.lockIcon = QtGui.QIcon(u':/bibles/bibles_search_lock.png')
self.unlockIcon = QtGui.QIcon(u':/bibles/bibles_search_unlock.png')
self.lockIcon = build_icon(u':/bibles/bibles_search_lock.png')
self.unlockIcon = build_icon(u':/bibles/bibles_search_unlock.png')
MediaManagerItem.__init__(self, parent, plugin, icon)
# Place to store the search results for both bibles.
self.settings = self.plugin.settings_tab
@ -86,7 +87,7 @@ class BibleMediaItem(MediaManagerItem):
not second_bible:
self.displayResults(bible, second_bible)
elif critical_error_message_box(
message=translate('BiblePlugin.MediaItem',
message=translate('BiblesPlugin.MediaItem',
'You cannot combine single and dual Bible verse search results. '
'Do you want to delete your search results and start a new '
'search?'),
@ -394,6 +395,7 @@ class BibleMediaItem(MediaManagerItem):
log.debug(u'Reloading Bibles')
self.plugin.manager.reload_bibles()
self.loadBibles()
self.updateAutoCompleter()
def initialiseAdvancedBible(self, bible):
"""
@ -436,7 +438,7 @@ class BibleMediaItem(MediaManagerItem):
if verse_count == 0:
self.advancedSearchButton.setEnabled(False)
critical_error_message_box(
message=translate('BiblePlugin.MediaItem',
message=translate('BiblesPlugin.MediaItem',
'Bible not fully loaded.'))
else:
self.advancedSearchButton.setEnabled(True)
@ -611,7 +613,7 @@ class BibleMediaItem(MediaManagerItem):
if restore:
old_text = unicode(combo.currentText())
combo.clear()
combo.addItems([unicode(i) for i in range(range_from, range_to + 1)])
combo.addItems(map(unicode, range(range_from, range_to + 1)))
if restore and combo.findText(old_text) != -1:
combo.setCurrentIndex(combo.findText(old_text))
@ -693,8 +695,8 @@ class BibleMediaItem(MediaManagerItem):
verse.verse, verse.verse))
if passage_not_found:
QtGui.QMessageBox.information(self,
translate('BiblePlugin.MediaItem', 'Information'),
unicode(translate('BiblePlugin.MediaItem',
translate('BiblesPlugin.MediaItem', 'Information'),
unicode(translate('BiblesPlugin.MediaItem',
'The second Bible does not contain all the verses '
'that are in the main Bible. Only verses found in both '
'Bibles will be shown. %d verses have not been '
@ -983,7 +985,8 @@ class BibleMediaItem(MediaManagerItem):
Search for some Bible verses (by reference).
"""
bible = unicode(self.quickVersionComboBox.currentText())
search_results = self.plugin.manager.get_verses(bible, string, False, False)
search_results = self.plugin.manager.get_verses(bible, string, False,
False)
if search_results:
versetext = u' '.join([verse.text for verse in search_results])
return [[string, versetext]]

View File

@ -46,7 +46,7 @@ class CustomPlugin(Plugin):
log.info(u'Custom Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Custom', plugin_helpers,
Plugin.__init__(self, u'custom', plugin_helpers,
CustomMediaItem, CustomTab)
self.weight = -5
self.manager = Manager(u'custom', init_schema)

View File

@ -93,7 +93,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.titleEdit.setText(u'')
self.creditEdit.setText(u'')
self.themeComboBox.setCurrentIndex(0)
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
else:
self.customSlide = self.manager.get_object(CustomSlide, id)
self.titleEdit.setText(self.customSlide.title)
@ -104,10 +103,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.slideListView.addItem(slide[1])
theme = self.customSlide.theme_name
find_and_set_in_combo_box(self.themeComboBox, theme)
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
# If not preview hide the preview button.
self.previewButton.setVisible(False)
if preview:
self.previewButton.setVisible(True)
self.previewButton.setVisible(preview)
def reject(self):
Receiver.send_message(u'custom_edit_clear')

View File

@ -200,6 +200,17 @@ class CustomMediaItem(MediaManagerItem):
Remove a custom item from the list and database
"""
if check_item_selected(self.listView, UiStrings().SelectDelete):
items = self.listView.selectedIndexes()
if QtGui.QMessageBox.question(self,
UiStrings().ConfirmDelete,
translate('CustomPlugin.MediaItem',
'Are you sure you want to delete the %n selected custom'
' slides(s)?', '',
QtCore.QCoreApplication.CodecForTr, len(items)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
return
row_list = [item.row() for item in self.listView.selectedIndexes()]
row_list.sort(reverse=True)
id_list = [(item.data(QtCore.Qt.UserRole)).toInt()[0]

View File

@ -36,7 +36,7 @@ class ImagePlugin(Plugin):
log.info(u'Image Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Images', plugin_helpers, ImageMediaItem)
Plugin.__init__(self, u'images', plugin_helpers, ImageMediaItem)
self.weight = -7
self.icon_path = u':/plugins/plugin_images.png'
self.icon = build_icon(self.icon_path)

View File

@ -52,6 +52,8 @@ class ImageMediaItem(MediaManagerItem):
self.hasSearch = True
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged)
# Allow DnD from the desktop
self.listView.activateDnD()
def retranslateUi(self):
self.onNewPrompt = translate('ImagePlugin.MediaItem',
@ -131,6 +133,7 @@ class ImageMediaItem(MediaManagerItem):
icon = self.iconFromFile(imageFile, thumb)
item_name = QtGui.QListWidgetItem(filename)
item_name.setIcon(icon)
item_name.setToolTip(imageFile)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(imageFile))
self.listView.addItem(item_name)
if not initialLoad:
@ -208,8 +211,13 @@ class ImageMediaItem(MediaManagerItem):
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
if os.path.exists(filename):
(path, name) = os.path.split(filename)
self.plugin.liveController.display.directImage(name, filename)
self.resetAction.setVisible(True)
if self.plugin.liveController.display.directImage(name,
filename):
self.resetAction.setVisible(True)
else:
critical_error_message_box(UiStrings().LiveBGError,
translate('ImagePlugin.MediaItem',
'There was no display item to amend.'))
else:
critical_error_message_box(UiStrings().LiveBGError,
unicode(translate('ImagePlugin.MediaItem',

View File

@ -39,6 +39,8 @@ from PyQt4.phonon import Phonon
log = logging.getLogger(__name__)
CLAPPERBOARD = QtGui.QPixmap(u':/media/media_video.png').toImage()
class MediaMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Media Slides.
@ -48,8 +50,7 @@ class MediaMediaItem(MediaManagerItem):
def __init__(self, parent, plugin, icon):
self.IconPath = u'images/image'
self.background = False
self.PreviewFunction = QtGui.QPixmap(
u':/media/media_video.png').toImage()
self.PreviewFunction = CLAPPERBOARD
MediaManagerItem.__init__(self, parent, plugin, icon)
self.singleServiceItem = False
self.hasSearch = True
@ -60,6 +61,8 @@ class MediaMediaItem(MediaManagerItem):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_phonon_creation'),
self.createPhonon)
# Allow DnD from the desktop
self.listView.activateDnD()
def retranslateUi(self):
self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media')
@ -114,8 +117,12 @@ class MediaMediaItem(MediaManagerItem):
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
if os.path.exists(filename):
(path, name) = os.path.split(filename)
self.plugin.liveController.display.video(filename, 0, True)
self.resetAction.setVisible(True)
if self.plugin.liveController.display.video(filename, 0, True):
self.resetAction.setVisible(True)
else:
critical_error_message_box(UiStrings().LiveBGError,
translate('MediaPlugin.MediaItem',
'There was no display item to amend.'))
else:
critical_error_message_box(UiStrings().LiveBGError,
unicode(translate('MediaPlugin.MediaItem',
@ -197,17 +204,17 @@ class MediaMediaItem(MediaManagerItem):
SettingsManager.set_list(self.settingsSection,
u'media', self.getFileList())
def loadList(self, files):
def loadList(self, media):
# Sort the themes by its filename considering language specific
# characters. lower() is needed for windows!
files.sort(cmp=locale.strcoll,
media.sort(cmp=locale.strcoll,
key=lambda filename: os.path.split(unicode(filename))[1].lower())
for file in files:
filename = os.path.split(unicode(file))[1]
for track in media:
filename = os.path.split(unicode(track))[1]
item_name = QtGui.QListWidgetItem(filename)
img = QtGui.QPixmap(u':/media/media_video.png').toImage()
item_name.setIcon(build_icon(img))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
item_name.setIcon(build_icon(CLAPPERBOARD))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
item_name.setToolTip(track)
self.listView.addItem(item_name)
def createPhonon(self):

View File

@ -39,7 +39,7 @@ class MediaPlugin(Plugin):
log.info(u'%s MediaPlugin loaded', __name__)
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'Media', plugin_helpers,
Plugin.__init__(self, u'media', plugin_helpers,
MediaMediaItem, MediaTab)
self.weight = -6
self.icon_path = u':/plugins/plugin_media.png'

View File

@ -58,6 +58,8 @@ class PresentationMediaItem(MediaManagerItem):
self.hasSearch = True
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
# Allow DnD from the desktop
self.listView.activateDnD()
def retranslateUi(self):
"""
@ -205,6 +207,7 @@ class PresentationMediaItem(MediaManagerItem):
item_name = QtGui.QListWidgetItem(filename)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
item_name.setIcon(icon)
item_name.setToolTip(file)
self.listView.addItem(item_name)
Receiver.send_message(u'cursor_normal')
if not initialLoad:

View File

@ -52,7 +52,7 @@ class PresentationPlugin(Plugin):
"""
log.debug(u'Initialised')
self.controllers = {}
Plugin.__init__(self, u'Presentations', plugin_helpers)
Plugin.__init__(self, u'presentations', plugin_helpers)
self.weight = -8
self.icon_path = u':/plugins/plugin_presentations.png'
self.icon = build_icon(self.icon_path)

View File

@ -150,13 +150,11 @@ class HttpResponse(object):
class HttpServer(object):
"""
Ability to control OpenLP via a webbrowser
e.g. http://localhost:4316/send/slidecontroller_live_next
http://localhost:4316/send/alerts_text?q=your%20alert%20text
Ability to control OpenLP via a web browser.
"""
def __init__(self, plugin):
"""
Initialise the httpserver, and start the server
Initialise the httpserver, and start the server.
"""
log.debug(u'Initialise httpserver')
self.plugin = plugin
@ -170,9 +168,9 @@ class HttpServer(object):
def start_tcp(self):
"""
Start the http server, use the port in the settings default to 4316
Start the http server, use the port in the settings default to 4316.
Listen out for slide and song changes so they can be broadcast to
clients. Listen out for socket connections
clients. Listen out for socket connections.
"""
log.debug(u'Start TCP server')
port = QtCore.QSettings().value(
@ -195,20 +193,20 @@ class HttpServer(object):
def slide_change(self, row):
"""
Slide change listener. Store the item and tell the clients
Slide change listener. Store the item and tell the clients.
"""
self.current_slide = row
def item_change(self, items):
"""
Item (song) change listener. Store the slide and tell the clients
Item (song) change listener. Store the slide and tell the clients.
"""
self.current_item = items[0]
def new_connection(self):
"""
A new http connection has been made. Create a client object to handle
communication
communication.
"""
log.debug(u'new http connection')
socket = self.server.nextPendingConnection()
@ -225,15 +223,16 @@ class HttpServer(object):
def close(self):
"""
Close down the http server
Close down the http server.
"""
log.debug(u'close http server')
self.server.close()
class HttpConnection(object):
"""
A single connection, this handles communication between the server
and the client
and the client.
"""
def __init__(self, parent, socket):
"""
@ -287,9 +286,12 @@ class HttpConnection(object):
"""
self.template_vars = {
'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.0 Remote'),
'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.0 Stage View'),
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
'stage_title': translate('RemotePlugin.Mobile',
'OpenLP 2.0 Stage View'),
'service_manager': translate('RemotePlugin.Mobile',
'Service Manager'),
'slide_controller': translate('RemotePlugin.Mobile',
'Slide Controller'),
'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
'search': translate('RemotePlugin.Mobile', 'Search'),
'back': translate('RemotePlugin.Mobile', 'Back'),
@ -301,7 +303,8 @@ class HttpConnection(object):
'text': translate('RemotePlugin.Mobile', 'Text'),
'show_alert': translate('RemotePlugin.Mobile', 'Show Alert'),
'go_live': translate('RemotePlugin.Mobile', 'Go Live'),
'add_to_service': translate('RemotePlugin.Mobile', 'Add To Service'),
'add_to_service': translate('RemotePlugin.Mobile',
'Add to Service'),
'no_results': translate('RemotePlugin.Mobile', 'No Results'),
'options': translate('RemotePlugin.Mobile', 'Options')
}
@ -357,7 +360,8 @@ class HttpConnection(object):
if ext == u'.html':
mimetype = u'text/html'
variables = self.template_vars
html = Template(filename=path, input_encoding=u'utf-8', output_encoding=u'utf-8').render(**variables)
html = Template(filename=path, input_encoding=u'utf-8',
output_encoding=u'utf-8').render(**variables)
elif ext == u'.css':
mimetype = u'text/css'
elif ext == u'.js':
@ -396,7 +400,7 @@ class HttpConnection(object):
if self.parent.current_item else u''
}
return HttpResponse(json.dumps({u'results': result}),
{u'Content-Type': u'application/json'})
{u'Content-Type': u'application/json'})
def display(self, action):
"""
@ -482,10 +486,11 @@ class HttpConnection(object):
def pluginInfo(self, action):
"""
Return plugin related information, based on the action
Return plugin related information, based on the action.
``action`` - The action to perform
if 'search' return a list of plugin names which support search
``action``
The action to perform. If *search* return a list of plugin names
which support search.
"""
if action == u'search':
searches = []
@ -500,10 +505,10 @@ class HttpConnection(object):
def search(self, type):
"""
Return a list of items that match the search text
Return a list of items that match the search text.
``type``
The plugin name to search in.
The plugin name to search in.
"""
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
@ -527,7 +532,7 @@ class HttpConnection(object):
def add_to_service(self, type):
"""
Add item of type ``type`` to the end of the service
Add item of type ``type`` to the end of the service.
"""
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)

View File

@ -39,7 +39,7 @@ class RemotesPlugin(Plugin):
"""
remotes constructor
"""
Plugin.__init__(self, u'Remotes', plugin_helpers,
Plugin.__init__(self, u'remotes', plugin_helpers,
settings_tab_class=RemoteTab)
self.icon_path = u':/plugins/plugin_remote.png'
self.icon = build_icon(self.icon_path)

View File

@ -90,7 +90,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.onVerseListViewPressed)
QtCore.QObject.connect(self.themeAddButton,
QtCore.SIGNAL(u'clicked()'),
self.mediaitem.plugin.renderer.theme_manager.onAddTheme)
self.mediaitem.plugin.renderer.themeManager.onAddTheme)
QtCore.QObject.connect(self.maintenanceButton,
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
QtCore.QObject.connect(Receiver.get_receiver(),
@ -209,9 +209,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.alternativeEdit.setText(u'')
if self.song.song_book_id != 0:
book_name = self.manager.get_object(Book, self.song.song_book_id)
find_and_set_in_combo_box(self.songBookComboBox, unicode(book_name.name))
find_and_set_in_combo_box(
self.songBookComboBox, unicode(book_name.name))
if self.song.theme_name:
find_and_set_in_combo_box(self.themeComboBox, unicode(self.song.theme_name))
find_and_set_in_combo_box(
self.themeComboBox, unicode(self.song.theme_name))
if self.song.copyright:
self.copyrightEdit.setText(self.song.copyright)
else:

View File

@ -37,6 +37,8 @@ from editversedialog import Ui_EditVerseDialog
log = logging.getLogger(__name__)
VERSE_REGEX = re.compile(r'---\[(.+):\D*(\d*)\D*.*\]---')
class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
"""
This is the form that is used to edit the verses of the song.
@ -60,7 +62,6 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
QtCore.QObject.connect(self.verseTypeComboBox,
QtCore.SIGNAL(u'currentIndexChanged(int)'),
self.onVerseTypeComboBoxChanged)
self.verse_regex = re.compile(r'---\[(.+):\D*(\d*)\D*.*\]---')
def contextMenu(self, point):
item = self.serviceManagerList.itemAt(point)
@ -105,7 +106,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
if position == -1:
return
text = text[:position + 4]
match = self.verse_regex.match(text)
match = VERSE_REGEX.match(text)
if match:
verse_tag = match.group(1)
try:
@ -136,7 +137,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
if position == -1:
return
text = text[:position + 4]
match = self.verse_regex.match(text)
match = VERSE_REGEX.match(text)
if match:
verse_type = match.group(1)
verse_type_index = VerseType.from_loose_input(verse_type)

View File

@ -28,6 +28,7 @@
The :mod:`songexportform` module provides the wizard for exporting songs to the
OpenLyrics format.
"""
import locale
import logging
from PyQt4 import QtCore, QtGui
@ -249,6 +250,7 @@ class SongExportForm(OpenLPWizard):
# Load the list of songs.
Receiver.send_message(u'cursor_busy')
songs = self.plugin.manager.get_all_objects(Song)
songs.sort(cmp=locale.strcoll, key=lambda song: song.title.lower())
for song in songs:
authors = u', '.join([author.display_name
for author in song.authors])

View File

@ -699,7 +699,8 @@ class SongImportForm(OpenLPWizard):
elif source_format == SongFormat.OpenLP1:
# Import an openlp.org database
importer = self.plugin.importSongs(SongFormat.OpenLP1,
filename=unicode(self.openLP1FilenameEdit.text())
filename=unicode(self.openLP1FilenameEdit.text()),
plugin=self.plugin
)
elif source_format == SongFormat.OpenLyrics:
# Import OpenLyrics songs

View File

@ -267,6 +267,12 @@ def clean_song(manager, song):
``song``
The song object.
"""
if isinstance(song.title, buffer):
song.title = unicode(song.title)
if isinstance(song.alternate_title, buffer):
song.alternate_title = unicode(song.alternate_title)
if isinstance(song.lyrics, buffer):
song.lyrics = unicode(song.lyrics)
song.title = song.title.rstrip() if song.title else u''
if song.alternate_title is None:
song.alternate_title = u''

View File

@ -29,7 +29,7 @@ The :mod:`db` module provides the database and schema that is the backend for
the Songs plugin
"""
from sqlalchemy import Column, ForeignKey, Index, Table, types
from sqlalchemy import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation
from openlp.core.lib.db import BaseModel, init_db

View File

@ -26,13 +26,10 @@
###############################################################################
import logging
import os
import re
from lxml import etree, objectify
from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport

View File

@ -31,18 +31,33 @@ EasyWorship song databases into the current installation database.
import os
import struct
import re
from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib import retrieve_windows_encoding
from songimport import SongImport
RTF_STRIPPING_REGEX = re.compile(r'\{\\tx[^}]*\}')
# regex: at least two newlines, can have spaces between them
SLIDE_BREAK_REGEX = re.compile(r'\n *?\n[\n ]*')
NUMBER_REGEX = re.compile(r'[0-9]+')
NOTE_REGEX = re.compile(r'\(.*?\)')
def strip_rtf(blob, encoding):
depth = 0
control = False
clear_text = []
control_word = []
# workaround for \tx bug: remove one pair of curly braces
# if \tx is encountered
match = RTF_STRIPPING_REGEX.search(blob)
if match:
# start and end indices of match are curly braces - filter them out
blob = ''.join([blob[i] for i in xrange(len(blob))
if i != match.start() and i !=match.end()])
for c in blob:
if control:
# for delimiters, set control to False
@ -259,9 +274,45 @@ class EasyWorshipSongImport(SongImport):
if words:
# Format the lyrics
words = strip_rtf(words, self.encoding)
for verse in words.split(u'\n\n'):
verse_type = VerseType.Tags[VerseType.Verse]
for verse in SLIDE_BREAK_REGEX.split(words):
verse = verse.strip()
if not verse:
continue
verse_split = verse.split(u'\n', 1)
first_line_is_tag = False
# EW tags: verse, chorus, pre-chorus, bridge, tag,
# intro, ending, slide
for type in VerseType.Names+[u'tag', u'slide']:
type = type.lower()
ew_tag = verse_split[0].strip().lower()
if ew_tag.startswith(type):
verse_type = type[0]
if type == u'tag' or type == u'slide':
verse_type = VerseType.Tags[VerseType.Other]
first_line_is_tag = True
number_found = False
# check if tag is followed by number and/or note
if len(ew_tag) > len(type):
match = NUMBER_REGEX.search(ew_tag)
if match:
number = match.group()
verse_type += number
number_found = True
match = NOTE_REGEX.search(ew_tag)
if match:
self.comments += ew_tag + u'\n'
if not number_found:
verse_type += u'1'
break
self.add_verse(
verse.strip(), VerseType.Tags[VerseType.Verse])
verse_split[-1].strip() if first_line_is_tag else verse,
verse_type)
if len(self.comments) > 5:
self.comments += unicode(
translate('SongsPlugin.EasyWorshipSongImport',
'\n[above are Song Tags with notes imported from \
EasyWorship]'))
if self.stop_import_flag:
break
if not self.finish():

View File

@ -35,7 +35,8 @@ from sqlalchemy.sql import or_
from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
translate, check_item_selected, PluginStatus
from openlp.core.lib.searchedit import SearchEdit
from openlp.core.lib.ui import UiStrings
from openlp.core.lib.ui import UiStrings, context_menu_action, \
context_menu_separator
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm, SongExportForm
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \
@ -128,6 +129,13 @@ class SongMediaItem(MediaManagerItem):
QtCore.SIGNAL(u'searchTypeChanged(int)'),
self.onSearchTextButtonClick)
def addCustomContextActions(self):
context_menu_separator(self.listView)
context_menu_action(
self.listView, u':/general/general_clone.png',
translate('OpenLP.MediaManagerItem',
'&Clone'), self.onCloneClick)
def onFocus(self):
self.searchTextEdit.setFocus()
@ -353,19 +361,37 @@ class SongMediaItem(MediaManagerItem):
if check_item_selected(self.listView, UiStrings().SelectDelete):
items = self.listView.selectedIndexes()
if QtGui.QMessageBox.question(self,
translate('SongsPlugin.MediaItem', 'Delete Song(s)?'),
UiStrings().ConfirmDelete,
translate('SongsPlugin.MediaItem',
'Are you sure you want to delete the %n selected song(s)?', '',
QtCore.QCoreApplication.CodecForTr, len(items)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok |
QtGui.QMessageBox.Cancel),
QtGui.QMessageBox.Ok) == QtGui.QMessageBox.Cancel:
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
return
for item in items:
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.plugin.manager.delete_object(Song, item_id)
self.onSearchTextButtonClick()
def onCloneClick(self):
"""
Clone a Song
"""
log.debug(u'onCloneClick')
if check_item_selected(self.listView, UiStrings().SelectEdit):
self.editItem = self.listView.currentItem()
item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0]
old_song = self.plugin.manager.get_object(Song, item_id)
song_xml = self.openLyrics.song_to_xml(old_song)
new_song_id = self.openLyrics.xml_to_song(song_xml)
new_song = self.plugin.manager.get_object(Song, new_song_id)
new_song.title = u'%s <%s>' % (new_song.title,
translate('SongsPlugin.MediaItem', 'copy',
'For song cloning'))
self.plugin.manager.save_object(new_song)
self.onSongListLoad()
def generateSlideData(self, service_item, item=None, xmlVersion=False):
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
item_id = self._getIdOfItemToGenerate(item, self.remoteSong)

View File

@ -57,6 +57,8 @@ class OpenLP1SongImport(SongImport):
The database providing the data to import.
"""
SongImport.__init__(self, manager, **kwargs)
self.available_themes = \
kwargs[u'plugin'].formparent.themeManagerContents.getThemes()
def do_import(self):
"""
@ -70,27 +72,34 @@ class OpenLP1SongImport(SongImport):
encoding = self.get_encoding()
if not encoding:
return
# Connect to the database
# Connect to the database.
connection = sqlite.connect(self.import_source, mode=0444,
encoding=(encoding, 'replace'))
cursor = connection.cursor()
# Determine if we're using a new or an old DB
# Determine if we're using a new or an old DB.
cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'tracks\'')
new_db = len(cursor.fetchall()) > 0
# "cache" our list of authors
# "cache" our list of authors.
cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT authorid, authorname FROM authors')
authors = cursor.fetchall()
if new_db:
# "cache" our list of tracks
# "cache" our list of tracks.
cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
tracks = cursor.fetchall()
# Import the songs
cursor.execute(u'-- types int, unicode, unicode, unicode')
# "cache" our list of themes.
cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT settingsid, settingsname FROM settings')
themes = {}
for theme_id, theme_name in cursor.fetchall():
if theme_name in self.available_themes:
themes[theme_id] = theme_name
# Import the songs.
cursor.execute(u'-- types int, unicode, unicode, unicode, int')
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
u'copyrightinfo FROM songs')
u'copyrightinfo, settingsid FROM songs')
songs = cursor.fetchall()
self.import_wizard.progressBar.setMaximum(len(songs))
for song in songs:
@ -101,8 +110,12 @@ class OpenLP1SongImport(SongImport):
self.title = song[1]
lyrics = song[2].replace(u'\r\n', u'\n')
self.add_copyright(song[3])
if themes.has_key(song[4]):
self.theme_name = themes[song[4]]
verses = lyrics.split(u'\n\n')
[self.add_verse(verse.strip()) for verse in verses if verse.strip()]
for verse in verses:
if verse.strip():
self.add_verse(verse.strip())
cursor.execute(u'-- types int')
cursor.execute(u'SELECT authorid FROM songauthors '
u'WHERE songid = %s' % song_id)
@ -137,12 +150,12 @@ class OpenLP1SongImport(SongImport):
"""
Detect character encoding of an openlp.org 1.x song database.
"""
# Connect to the database
# Connect to the database.
connection = sqlite.connect(self.import_source, mode=0444)
cursor = connection.cursor()
detector = UniversalDetector()
# detect charset by authors
# Detect charset by authors.
cursor.execute(u'SELECT authorname FROM authors')
authors = cursor.fetchall()
for author in authors:
@ -150,7 +163,7 @@ class OpenLP1SongImport(SongImport):
if detector.done:
detector.close()
return detector.result[u'encoding']
# detect charset by songs
# Detect charset by songs.
cursor.execute(u'SELECT songtitle, copyrightinfo, '
u'lyrics || \'\' AS lyrics FROM songs')
songs = cursor.fetchall()
@ -160,7 +173,7 @@ class OpenLP1SongImport(SongImport):
if detector.done:
detector.close()
return detector.result[u'encoding']
# detect charset by songs
# Detect charset by songs.
cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'tracks\'')
if len(cursor.fetchall()) > 0:

View File

@ -35,6 +35,7 @@ import re
from lxml import etree
from openlp.core.lib import check_directory_exists, Receiver, translate
from openlp.core.utils import clean_filename
from openlp.plugins.songs.lib import OpenLyrics
log = logging.getLogger(__name__)
@ -72,8 +73,7 @@ class OpenLyricsExport(object):
tree = etree.ElementTree(etree.fromstring(xml))
filename = u'%s (%s)' % (song.title,
u', '.join([author.display_name for author in song.authors]))
filename = re.sub(
r'[/\\?*|<>\[\]":<>+%]+', u'_', filename).strip(u'_')
filename = clean_filename(filename)
# Ensure the filename isn't too long for some filesystems
filename = u'%s.xml' % filename[0:250 - len(self.save_path)]
# Pass a file object, because lxml does not cope with some special

View File

@ -26,9 +26,7 @@
###############################################################################
import logging
import os
import re
from zipfile import ZipFile
from lxml import objectify
from lxml.etree import Error, LxmlError
@ -110,48 +108,13 @@ class OpenSongImport(SongImport):
SongImport.__init__(self, manager, **kwargs)
def do_import(self):
"""
Import either each of the files in self.import_source - each element of
which can be either a single opensong file, or a zipfile containing
multiple opensong files.
"""
numfiles = 0
for filename in self.import_source:
ext = os.path.splitext(filename)[1]
if ext.lower() == u'.zip':
z = ZipFile(filename, u'r')
numfiles += len(z.infolist())
z.close()
else:
numfiles += 1
log.debug(u'Total number of files: %d', numfiles)
self.import_wizard.progressBar.setMaximum(numfiles)
self.import_wizard.progressBar.setMaximum(len(self.import_source))
for filename in self.import_source:
if self.stop_import_flag:
return
ext = os.path.splitext(filename)[1]
if ext.lower() == u'.zip':
log.debug(u'Zipfile found %s', filename)
z = ZipFile(filename, u'r')
for song in z.infolist():
if self.stop_import_flag:
z.close()
return
parts = os.path.split(song.filename)
if parts[-1] == u'':
# No final part => directory
continue
log.info(u'Zip importing %s', parts[-1])
song_file = z.open(song)
self.do_import_file(song_file)
song_file.close()
z.close()
else:
# not a zipfile
log.info(u'Direct import %s', filename)
song_file = open(filename)
self.do_import_file(song_file)
song_file.close()
song_file = open(filename)
self.do_import_file(song_file)
song_file.close()
def do_import_file(self, file):
"""

View File

@ -36,7 +36,6 @@ import re
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__)

View File

@ -29,9 +29,8 @@ import re
from PyQt4 import QtCore
from openlp.core.lib import Receiver, translate, check_directory_exists
from openlp.core.lib import Receiver, translate
from openlp.core.ui.wizard import WizardStrings
from openlp.core.utils import AppLocation
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings

View File

@ -102,7 +102,6 @@ class SongShowPlusImport(SongImport):
if not isinstance(self.import_source, list):
return
self.import_wizard.progressBar.setMaximum(len(self.import_source))
for file in self.import_source:
self.sspVerseOrderList = []
otherCount = 0
@ -111,7 +110,6 @@ class SongShowPlusImport(SongImport):
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % file_name, 0)
songData = open(file, 'rb')
while True:
blockKey, = struct.unpack("I", songData.read(4))
# The file ends with 4 NUL's
@ -126,8 +124,9 @@ class SongShowPlusImport(SongImport):
songData.read(2))
verseName = songData.read(verseNameLength)
lengthDescriptorSize, = struct.unpack("B", songData.read(1))
log.debug(lengthDescriptorSize)
# Detect if/how long the length descriptor is
if lengthDescriptorSize == 12:
if lengthDescriptorSize == 12 or lengthDescriptorSize == 20:
lengthDescriptor, = struct.unpack("I", songData.read(4))
elif lengthDescriptorSize == 2:
lengthDescriptor = 1
@ -135,6 +134,7 @@ class SongShowPlusImport(SongImport):
lengthDescriptor = 0
else:
lengthDescriptor, = struct.unpack("B", songData.read(1))
log.debug(lengthDescriptorSize)
data = songData.read(lengthDescriptor)
if blockKey == TITLE:
self.title = unicode(data, u'cp1252')

View File

@ -31,7 +31,6 @@ Worship songs into the OpenLP database.
import os
import logging
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib.songimport import SongImport
BLOCK_TYPES = (u'V', u'C', u'B')

View File

@ -73,6 +73,8 @@ from openlp.core.utils import get_application_version
log = logging.getLogger(__name__)
CHORD_REGEX = re.compile(u'<chord name=".*?"/>')
class SongXML(object):
"""
This class builds and parses the XML used to describe songs.
@ -234,7 +236,6 @@ class OpenLyrics(object):
IMPLEMENTED_VERSION = u'0.7'
def __init__(self, manager):
self.manager = manager
self.chord_regex = re.compile(u'<chord name=".*?"/>')
def song_to_xml(self, song):
"""
@ -245,8 +246,9 @@ class OpenLyrics(object):
# Append the necessary meta data to the song.
song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song')
song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
song_xml.set(u'createdIn', get_application_version()[u'version'])
song_xml.set(u'modifiedIn', get_application_version()[u'version'])
application_name = u'OpenLP ' + get_application_version()[u'version']
song_xml.set(u'createdIn', application_name)
song_xml.set(u'modifiedIn', application_name)
song_xml.set(u'modifiedDate',
datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S'))
properties = etree.SubElement(song_xml, u'properties')
@ -319,7 +321,7 @@ class OpenLyrics(object):
if xml[:5] == u'<?xml':
xml = xml[38:]
# Remove chords from xml.
xml = self.chord_regex.sub(u'', xml)
xml = CHORD_REGEX.sub(u'', xml)
song_xml = objectify.fromstring(xml)
if hasattr(song_xml, u'properties'):
properties = song_xml.properties

View File

@ -57,7 +57,7 @@ class SongsPlugin(Plugin):
"""
Create and set up the Songs plugin.
"""
Plugin.__init__(self, u'Songs', plugin_helpers, SongMediaItem, SongsTab)
Plugin.__init__(self, u'songs', plugin_helpers, SongMediaItem, SongsTab)
self.weight = -10
self.manager = Manager(u'songs', init_schema)
self.icon_path = u':/plugins/plugin_songs.png'

View File

@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, StringContent, Receiver, build_icon, \
translate
from openlp.core.lib.db import Manager
from openlp.core.lib.ui import base_action, shortcut_action, UiStrings
from openlp.core.lib.ui import base_action, shortcut_action
from openlp.core.utils.actions import ActionList
from openlp.plugins.songusage.forms import SongUsageDetailForm, \
SongUsageDeleteForm
@ -45,11 +45,13 @@ class SongUsagePlugin(Plugin):
log.info(u'SongUsage Plugin loaded')
def __init__(self, plugin_helpers):
Plugin.__init__(self, u'SongUsage', plugin_helpers)
Plugin.__init__(self, u'songusage', plugin_helpers)
self.weight = -4
self.icon = build_icon(u':/plugins/plugin_songusage.png')
self.activeIcon = build_icon(u':/songusage/song_usage_active.png')
self.inactiveIcon = build_icon(u':/songusage/song_usage_inactive.png')
self.manager = None
self.songusageActive = False
self.songUsageActive = False
def addToolsMenuItem(self, tools_menu):
"""
@ -84,17 +86,29 @@ class SongUsagePlugin(Plugin):
self.songUsageStatus.setText(translate(
'SongUsagePlugin', 'Toggle Tracking'))
self.songUsageStatus.setStatusTip(translate('SongUsagePlugin',
'Toggle the tracking of song usage.'))
#Add Menus together
'Toggle the tracking of song usage.'))
# Add Menus together
self.toolsMenu.addAction(self.songUsageMenu.menuAction())
self.songUsageMenu.addAction(self.songUsageStatus)
self.songUsageMenu.addSeparator()
self.songUsageMenu.addAction(self.songUsageDelete)
self.songUsageMenu.addAction(self.songUsageReport)
self.songUsageMenu.addAction(self.songUsageDelete)
self.songUsageActiveButton = QtGui.QToolButton(
self.formparent.statusBar)
self.songUsageActiveButton.setCheckable(True)
self.songUsageActiveButton.setStatusTip(translate('SongUsagePlugin',
'Toggle the tracking of song usage.'))
self.songUsageActiveButton.setObjectName(u'songUsageActiveButton')
self.formparent.statusBar.insertPermanentWidget(1,
self.songUsageActiveButton)
self.songUsageActiveButton.hide()
# Signals and slots
QtCore.QObject.connect(self.songUsageStatus,
QtCore.SIGNAL(u'visibilityChanged(bool)'),
self.songUsageStatus.setChecked)
QtCore.QObject.connect(self.songUsageActiveButton,
QtCore.SIGNAL(u'toggled(bool)'),
self.toggleSongUsageState)
QtCore.QObject.connect(self.songUsageDelete,
QtCore.SIGNAL(u'triggered()'), self.onSongUsageDelete)
QtCore.QObject.connect(self.songUsageReport,
@ -107,23 +121,25 @@ class SongUsagePlugin(Plugin):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_started'),
self.onReceiveSongUsage)
self.SongUsageActive = QtCore.QSettings().value(
self.songUsageActive = QtCore.QSettings().value(
self.settingsSection + u'/active',
QtCore.QVariant(False)).toBool()
self.songUsageStatus.setChecked(self.SongUsageActive)
# Set the button and checkbox state
self.setButtonState()
action_list = ActionList.get_instance()
action_list.add_action(self.songUsageStatus,
translate('SongUsagePlugin', 'Song Usage'))
action_list.add_action(self.songUsageDelete,
translate('SongUsagePlugin', 'Song Usage'))
action_list.add_action(self.songUsageReport,
translate('SongUsagePlugin', 'Song Usage'))
action_list.add_action(self.songUsageStatus,
translate('SongUsagePlugin', 'Song Usage'))
if self.manager is None:
self.manager = Manager(u'songusage', init_schema)
self.songUsageDeleteForm = SongUsageDeleteForm(self.manager,
self.formparent)
self.songUsageDetailForm = SongUsageDetailForm(self, self.formparent)
self.songUsageMenu.menuAction().setVisible(True)
self.songUsageActiveButton.show()
def finalise(self):
"""
@ -134,26 +150,55 @@ class SongUsagePlugin(Plugin):
Plugin.finalise(self)
self.songUsageMenu.menuAction().setVisible(False)
action_list = ActionList.get_instance()
action_list.remove_action(self.songUsageStatus,
translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageDelete,
translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageReport,
translate('SongUsagePlugin', 'Song Usage'))
action_list.remove_action(self.songUsageStatus,
translate('SongUsagePlugin', 'Song Usage'))
#stop any events being processed
self.SongUsageActive = False
self.songUsageActiveButton.hide()
# stop any events being processed
self.songUsageActive = False
def toggleSongUsageState(self):
self.SongUsageActive = not self.SongUsageActive
"""
Manage the state of the audit collection and amend
the UI when necessary,
"""
self.songUsageActive = not self.songUsageActive
QtCore.QSettings().setValue(self.settingsSection + u'/active',
QtCore.QVariant(self.SongUsageActive))
QtCore.QVariant(self.songUsageActive))
self.setButtonState()
def setButtonState(self):
"""
Keep buttons inline. Turn of signals to stop dead loop but we need the
button and check box set correctly.
"""
self.songUsageActiveButton.blockSignals(True)
self.songUsageStatus.blockSignals(True)
if self.songUsageActive:
self.songUsageActiveButton.setIcon(self.activeIcon)
self.songUsageStatus.setChecked(True)
self.songUsageActiveButton.setChecked(True)
self.songUsageActiveButton.setToolTip(translate('SongUsagePlugin',
'Song usage tracking is active.'))
else:
self.songUsageActiveButton.setIcon(self.inactiveIcon)
self.songUsageStatus.setChecked(False)
self.songUsageActiveButton.setChecked(False)
self.songUsageActiveButton.setToolTip(translate('SongUsagePlugin',
'Song usage tracking is inactive.'))
self.songUsageActiveButton.blockSignals(False)
self.songUsageStatus.blockSignals(False)
def onReceiveSongUsage(self, item):
"""
Song Usage for live song from SlideController
"""
audit = item[0].audit
if self.SongUsageActive and audit:
if self.songUsageActive and audit:
song_usage_item = SongUsageItem()
song_usage_item.usagedate = datetime.today()
song_usage_item.usagetime = datetime.now().time()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -0,0 +1,6 @@
OpenLP.ico
This Windows icon contains several images with different resolution.
It can be recreated by command:
icotool -c -o OpenLP.ico openlp-logo-16x16.png openlp-logo-32x32.png openlp-logo-48x48.png openlp-logo-64x64.png openlp-logo-128x128.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

View File

@ -56,6 +56,7 @@
<file>general_save.png</file>
<file>general_email.png</file>
<file>general_revert.png</file>
<file>general_clone.png</file>
</qresource>
<qresource prefix="slides">
<file>slide_close.png</file>
@ -137,6 +138,10 @@
<file>messagebox_info.png</file>
<file>messagebox_warning.png</file>
</qresource>
<qresource prefix="songusage">
<file>song_usage_active.png</file>
<file>song_usage_inactive.png</file>
</qresource>
<qresource prefix="tools">
<file>tools_add.png</file>
<file>tools_alert.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

View File

@ -2,6 +2,99 @@
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtension</key>
<array>
<string>osz</string>
</array>
<key>CFBundleTypeIconFiles</key>
<array>
<string>openlp-logo-with-text.icns</string>
</array>
<key>CFBundleTypeName</key>
<string>OpenLP Service</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>org.openlp.osz</string>
</array>
</dict>
<dict>
<key>CFBundleTypeExtension</key>
<array>
<string>otz</string>
</array>
<key>CFBundleTypeIconFiles</key>
<array>
<string>openlp-logo-with-text.icns</string>
</array>
<key>CFBundleTypeName</key>
<string>OpenLP Theme</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>org.openlp.otz</string>
</array>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>org.openlp.osz</string>
<key>UTTypeDescription</key>
<string>OpenLP Service</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>osz</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/x-openlp-service</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.openlp.otz</string>
<key>UTTypeDescription</key>
<string>OpenLP Theme</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.content</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>otz</string>
</array>
<key>public.mime-type</key>
<array>
<string>application/x-openlp-theme</string>
</array>
</dict>
</dict>
</array>
<key>CFBundleIdentifier</key>
<string>org.openlp</string>
<key>CFBundleShortVersionString</key>

View File

@ -48,7 +48,7 @@ on run
set theViewOptions to the icon view options of container window
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 128
set background picture of theViewOptions to file ".installer-background.png"
set background picture of theViewOptions to file ".background:installer-background.png"
if not exists file "Applications" then
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
end if

View File

@ -49,15 +49,19 @@ on run
set theViewOptions to the icon view options of container window
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 128
set background picture of theViewOptions to file ".installer-background.png"
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
delay 5
set background picture of theViewOptions to file ".background:installer-background.png"
if not exists file "Applications" then
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
end if
delay 1
set position of item "%s" of container window to {160, 200}
set position of item ".Trashes" of container window to {100, 500}
set position of item ".installer-background.png" of container window to {200, 500}
set position of item ".background" of container window to {200, 500}
set position of item ".DS_Store" of container window to {400, 500}
set position of item "Applications" of container window to {550, 200}
set position of item ".VolumeIcon.icns" of container window to {500, 500}
if exists file ".VolumeIcon.icns" then
set position of item ".VolumeIcon.icns" of container window to {500, 500}
end if
set position of item ".fseventsd" of container window to {300, 500}
if exists POSIX file ".SymAVx86QSFile" then
set position of item ".SymAVx86QSFile" of container window to {600, 500}

View File

@ -93,8 +93,12 @@ script_name = "build"
def build_application(settings, app_name_lower, app_dir):
logging.info('[%s] now building the app with pyinstaller at "%s"...',
script_name, settings['pyinstaller_basedir'])
result = os.system('python %s/pyinstaller.py openlp.spec' \
% settings['pyinstaller_basedir'])
full_python_dir = os.path.join('/opt/local/Library/Frameworks',
'Python.framework/Versions/2.6/Resources/',
'Python.app/Contents/MacOS/Python')
result = os.system('arch -i386 %s %s/pyinstaller.py openlp.spec' \
% ( full_python_dir,
settings['pyinstaller_basedir']) )
if (result != 0):
logging.error('[%s] The pyinstaller build reported an error, cannot \
continue!', script_name)
@ -219,10 +223,10 @@ def create_dmg(settings):
sys.exit(1)
logging.info('[%s] copying the background image...', script_name)
# os.mkdir(volume_basedir + '/.background')
os.mkdir(volume_basedir + '/.background')
result = os.system('CpMac %s %s'
% (settings['installer_backgroundimage_file'],
volume_basedir + '/.installer-background.png'))
volume_basedir + '/.background/installer-background.png'))
if (result != 0):
logging.error('[%s] could not copy the background image, dmg creation\
failed!', script_name)

0
resources/osx/openlp-logo-with-text.icns Executable file → Normal file
View File

View File

@ -1,8 +1,8 @@
[openlp]
openlp_appname = OpenLP
openlp_dmgname = OpenLP-1.9.4-bzrXXXX
openlp_dmgname = OpenLP-1.9.6-bzrXXXX
openlp_version = XXXX
openlp_basedir = /Users/openlp/trunk
openlp_basedir = /Users/openlp/repo/trunk
openlp_icon_file = openlp-logo-with-text.icns
openlp_dmg_icon_file = openlp-logo-420x420.png
installer_backgroundimage_file = installation-background.png

View File

@ -1,5 +1,5 @@
# -*- mode: python -*-
a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), '%(openlp_basedir)s/openlp.pyw'],
a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(CONFIGDIR,'support/useUnicode.py'), '%(openlp_basedir)s/openlp.pyw'],
pathex=['%(pyinstaller_basedir)s'], hookspath=['%(openlp_basedir)s/resources/pyinstaller'])
pyz = PYZ(a.pure)
exe = EXE(pyz,

View File

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

185
scripts/check_dependencies.py Executable file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This script is used to check dependencies of OpenLP. It checks availability
of required python modules and their version. To verify availability of Python
modules, simply run this script::
@:~$ ./check_dependencies.py
"""
import os
import sys
is_win = sys.platform.startswith('win')
VERS = {
'Python': '2.6',
'PyQt4': '4.6',
'Qt4': '4.6',
'sqlalchemy': '0.5',
# pyenchant 1.6 required on Windows
'enchant': '1.6' if is_win else '1.3'
}
# pywin32
WIN32_MODULES = [
'win32com',
'win32ui',
'pywintypes',
]
MODULES = [
'PyQt4',
'PyQt4.QtCore',
'PyQt4.QtGui',
'PyQt4.QtNetwork',
'PyQt4.QtOpenGL',
'PyQt4.QtSvg',
'PyQt4.QtTest',
'PyQt4.QtWebKit',
'PyQt4.phonon',
'sqlalchemy',
'sqlite3',
'lxml',
'chardet',
'enchant',
'BeautifulSoup',
'mako',
]
OPTIONAL_MODULES = [
('sqlite', ' (SQLite 2 support)'),
('MySQLdb', ' (MySQL support)'),
('psycopg2', ' (PostgreSQL support)'),
]
w = sys.stdout.write
def check_vers(version, required, text):
if type(version) is str:
version = version.split('.')
version = map(int, version)
if type(required) is str:
required = required.split('.')
required = map(int, required)
w(' %s >= %s ... ' % (text, '.'.join(map(str, required))))
if version >= required:
w('.'.join(map(str, version)) + os.linesep)
return True
else:
w('FAIL' + os.linesep)
return False
def print_vers_fail(required, text):
print(' %s >= %s ... FAIL' % (text, required))
def verify_python():
if not check_vers(list(sys.version_info), VERS['Python'], text='Python'):
exit(1)
def verify_versions():
print('Verifying version of modules...')
try:
from PyQt4 import QtCore
check_vers(QtCore.PYQT_VERSION_STR, VERS['PyQt4'], 'PyQt4')
check_vers(QtCore.qVersion(), VERS['Qt4'], 'Qt4')
except ImportError:
print_vers_fail(VERS['PyQt4'], 'PyQt4')
print_vers_fail(VERS['Qt4'], 'Qt4')
try:
import sqlalchemy
check_vers(sqlalchemy.__version__, VERS['sqlalchemy'], 'sqlalchemy')
except ImportError:
print_vers_fail(VERS['sqlalchemy'], 'sqlalchemy')
try:
import enchant
check_vers(enchant.__version__, VERS['enchant'], 'enchant')
except ImportError:
print_vers_fail(VERS['enchant'], 'enchant')
def check_module(mod, text='', indent=' '):
space = (30 - len(mod) - len(text)) * ' '
w(indent + '%s%s... ' % (mod, text) + space)
try:
__import__(mod)
w('OK')
except ImportError:
w('FAIL')
w(os.linesep)
def verify_pyenchant():
w('Enchant (spell checker)... ')
try:
import enchant
w(os.linesep)
backends = ', '.join([x.name for x in enchant.Broker().describe()])
print(' available backends: %s' % backends)
langs = ', '.join(enchant.list_languages())
print(' available languages: %s' % langs)
except ImportError:
w('FAIL' + os.linesep)
def verify_pyqt():
w('Qt4 image formats... ')
try:
from PyQt4 import QtGui
read_f = ', '.join([unicode(format).lower()
for format in QtGui.QImageReader.supportedImageFormats()])
write_f = ', '.join([unicode(format).lower()
for format in QtGui.QImageWriter.supportedImageFormats()])
w(os.linesep)
print(' read: %s' % read_f)
print(' write: %s' % write_f)
except ImportError:
w('FAIL' + os.linesep)
def main():
verify_python()
print('Checking for modules...')
for m in MODULES:
check_module(m)
print('Checking for optional modules...')
for m in OPTIONAL_MODULES:
check_module(m[0], text=m[1])
if is_win:
print('Checking for Windows specific modules...')
for m in WIN32_MODULES:
check_module(m)
verify_versions()
verify_pyqt()
verify_pyenchant()
if __name__ == u'__main__':
main()

View File

@ -32,8 +32,7 @@ Windows Build Script
This script is used to build the Windows binary and the accompanying installer.
For this script to work out of the box, it depends on a number of things:
Python 2.6
This build script only works with Python 2.6.
Python 2.6/2.7
PyQt4
You should already have this installed, OpenLP doesn't work without it. The
@ -47,12 +46,6 @@ PyEnchant
Inno Setup 5
Inno Setup should be installed into "C:\%PROGRAMFILES%\Inno Setup 5"
UPX
This is used to compress DLLs and EXEs so that they take up less space, but
still function exactly the same. To install UPS, download it from
http://upx.sourceforge.net/, extract it into C:\%PROGRAMFILES%\UPX, and then
add that directory to your PATH environment variable.
Sphinx
This is used to build the documentation. The documentation trunk must be at
the same directory level as Openlp trunk and named "documentation"
@ -61,7 +54,7 @@ HTML Help Workshop
This is used to create the help file
PyInstaller
PyInstaller should be a checkout of revision 844 of trunk, and in a
PyInstaller should be a checkout of revision 1470 of trunk, and in a
directory called, "pyinstaller" on the same level as OpenLP's Bazaar shared
repository directory. The revision is very important as there is currently
a major regression in HEAD.
@ -73,13 +66,8 @@ PyInstaller
http://svn.pyinstaller.org/trunk
Then you need to copy the two hook-*.py files from the "pyinstaller"
subdirectory in OpenLP's "resources" directory into PyInstaller's "hooks"
directory.
Once you've done that, open a command prompt (DOS shell), navigate to the
PyInstaller directory and run::
C:\Projects\pyinstaller>python Configure.py
subdirectory in OpenLP's "resources" directory into PyInstaller's
"PyInstaller/hooks" directory.
Bazaar
You need the command line "bzr" client installed.
@ -102,7 +90,7 @@ psvince.dll
the install will fail. The dll can be obtained from here:
http://www.vincenzo.net/isxkb/index.php?title=PSVince)
Mako
Mako
Mako Templates for Python. This package is required for building the
remote plugin. It can be installed by going to your
python_directory\scripts\.. and running "easy_install Mako". If you do not
@ -137,9 +125,18 @@ site_packages = os.path.join(os.path.split(python_exe)[0], u'Lib',
# Files and executables
pyi_build = os.path.abspath(os.path.join(branch_path, u'..', u'..',
u'pyinstaller', u'Build.py'))
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'bin', u'lrelease.exe')
u'pyinstaller', u'pyinstaller.py'))
openlp_main_script = os.path.abspath(os.path.join(branch_path, 'openlp.pyw'))
if os.path.exists(os.path.join(site_packages, u'PyQt4', u'bin')):
# Older versions of the PyQt4 Windows installer put their binaries in the
# "bin" directory
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'bin', u'lrelease.exe')
else:
# Newer versions of the PyQt4 Windows installer put their binaries in the
# base directory of the installation
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'lrelease.exe')
i18n_utils = os.path.join(script_path, u'translation_utils.py')
win32_icon = os.path.join(branch_path, u'resources', u'images', 'OpenLP.ico')
# Paths
source_path = os.path.join(branch_path, u'openlp')
@ -148,9 +145,8 @@ manual_build_path = os.path.join(manual_path, u'build')
helpfile_path = os.path.join(manual_build_path, u'htmlhelp')
i18n_path = os.path.join(branch_path, u'resources', u'i18n')
winres_path = os.path.join(branch_path, u'resources', u'windows')
build_path = os.path.join(branch_path, u'build', u'pyi.win32', u'OpenLP')
build_path = os.path.join(branch_path, u'build')
dist_path = os.path.join(branch_path, u'dist', u'OpenLP')
enchant_path = os.path.join(site_packages, u'enchant')
pptviewlib_path = os.path.join(source_path, u'plugins', u'presentations',
u'lib', u'pptviewlib')
@ -174,8 +170,16 @@ def update_code():
def run_pyinstaller():
print u'Running PyInstaller...'
os.chdir(branch_path)
pyinstaller = Popen((python_exe, pyi_build, u'-y', u'-o', build_path,
os.path.join(winres_path, u'OpenLP.spec')), stdout=PIPE)
pyinstaller = Popen((python_exe, pyi_build,
u'--noconfirm',
u'--windowed',
u'--noupx',
u'-o', branch_path,
u'-i', win32_icon,
u'-p', branch_path,
u'-n', 'OpenLP',
openlp_main_script),
stdout=PIPE)
output, error = pyinstaller.communicate()
code = pyinstaller.wait()
if code != 0:
@ -208,19 +212,6 @@ def write_version_file():
f.write(versionstring)
f.close()
def copy_enchant():
print u'Copying enchant/pyenchant...'
source = enchant_path
dest = os.path.join(dist_path, u'enchant')
for root, dirs, files in os.walk(source):
for filename in files:
if not filename.endswith(u'.pyc') and not filename.endswith(u'.pyo'):
dest_path = os.path.join(dest, root[len(source) + 1:])
if not os.path.exists(dest_path):
os.makedirs(dest_path)
copy(os.path.join(root, filename),
os.path.join(dest_path, filename))
def copy_plugins():
print u'Copying plugins...'
source = os.path.join(source_path, u'plugins')
@ -242,10 +233,10 @@ def copy_windows_files():
os.path.join(dist_path, u'LICENSE.txt'))
copy(os.path.join(winres_path, u'psvince.dll'),
os.path.join(dist_path, u'psvince.dll'))
if os.path.isfile(os.path.join(helpfile_path, u'Openlp.chm')):
if os.path.isfile(os.path.join(helpfile_path, u'OpenLP.chm')):
print u' Windows help file found'
copy(os.path.join(helpfile_path, u'Openlp.chm'),
os.path.join(dist_path, u'Openlp.chm'))
copy(os.path.join(helpfile_path, u'OpenLP.chm'),
os.path.join(dist_path, u'OpenLP.chm'))
else:
print u' WARNING ---- Windows help file not found ---- WARNING'
@ -330,17 +321,19 @@ def main():
import sys
for arg in sys.argv:
if arg == u'-v' or arg == u'--verbose':
print "Script path:", script_path
print "Branch path:", branch_path
print "Source path:", source_path
print "\"dist\" path:", dist_path
print "PyInstaller:", pyi_build
print "OpenLP main script: ......", openlp_main_script
print "Script path: .............", script_path
print "Branch path: .............", branch_path
print "Source path: .............", source_path
print "\"dist\" path: .............", dist_path
print "PyInstaller: .............", pyi_build
print "Documentation branch path:", doc_branch_path
print "Help file build path;", helpfile_path
print "Inno Setup path:", innosetup_exe
print "Windows resources:", winres_path
print "VCBuild path:", vcbuild_exe
print "PPTVIEWLIB path:", pptviewlib_path
print "Help file build path: ....", helpfile_path
print "Inno Setup path: .........", innosetup_exe
print "Windows resources: .......", winres_path
print "VCBuild path: ............", vcbuild_exe
print "PPTVIEWLIB path: .........", pptviewlib_path
print ""
elif arg == u'--skip-update':
skip_update = True
elif arg == u'/?' or arg == u'-h' or arg == u'--help':
@ -353,7 +346,6 @@ def main():
build_pptviewlib()
run_pyinstaller()
write_version_file()
copy_enchant()
copy_plugins()
if os.path.exists(manual_path):
run_sphinx()