forked from openlp/openlp
Readded the Renderer class for calculating slide sizes.
This commit is contained in:
parent
393823c0ab
commit
12a2a90ea6
@ -291,6 +291,13 @@ var Display = {
|
|||||||
$(".slides")[0].innerHTML = "";
|
$(".slides")[0].innerHTML = "";
|
||||||
this._slides = {};
|
this._slides = {};
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Checks if the present slide content fits within the slide
|
||||||
|
*/
|
||||||
|
doesContentFit: function () {
|
||||||
|
console.debug("scrollHeight: " + $(".slides")[0].scrollHeight);
|
||||||
|
return $(".slides")[0].clientHeight >= $(".slides")[0].scrollHeight;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Add a slides. If the slide exists but the HTML is different, update the slide.
|
* Add a slides. If the slide exists but the HTML is different, update the slide.
|
||||||
* @param {string} verse - The verse number, e.g. "v1"
|
* @param {string} verse - The verse number, e.g. "v1"
|
||||||
|
@ -27,7 +27,14 @@ import logging
|
|||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from PyQt5 import QtWidgets
|
||||||
|
|
||||||
from openlp.core.lib.formattingtags import FormattingTags
|
from openlp.core.lib.formattingtags import FormattingTags
|
||||||
|
from openlp.core.common.registry import Registry, RegistryBase
|
||||||
|
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||||
|
from openlp.core.display.window import DisplayWindow
|
||||||
|
from openlp.core.display.screens import ScreenList
|
||||||
|
from openlp.core.lib import ItemCapabilities
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -354,3 +361,318 @@ def render_tags(text, can_render_chords=False, is_printing=False):
|
|||||||
text = text.replace(tag['start tag'], tag['start html'])
|
text = text.replace(tag['start tag'], tag['start html'])
|
||||||
text = text.replace(tag['end tag'], tag['end html'])
|
text = text.replace(tag['end tag'], tag['end html'])
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def words_split(line):
|
||||||
|
"""
|
||||||
|
Split the slide up by word so can wrap better
|
||||||
|
|
||||||
|
:param line: Line to be split
|
||||||
|
"""
|
||||||
|
# this parse we are to be wordy
|
||||||
|
return re.split(r'\s+', line)
|
||||||
|
|
||||||
|
|
||||||
|
def get_start_tags(raw_text):
|
||||||
|
"""
|
||||||
|
Tests the given text for not closed formatting tags and returns a tuple consisting of three unicode strings::
|
||||||
|
|
||||||
|
('{st}{r}Text text text{/r}{/st}', '{st}{r}', '<strong><span style="-webkit-text-fill-color:red">')
|
||||||
|
|
||||||
|
The first unicode string is the text, with correct closing tags. The second unicode string are OpenLP's opening
|
||||||
|
formatting tags and the third unicode string the html opening formatting tags.
|
||||||
|
|
||||||
|
:param raw_text: The text to test. The text must **not** contain html tags, only OpenLP formatting tags
|
||||||
|
are allowed::
|
||||||
|
{st}{r}Text text text
|
||||||
|
"""
|
||||||
|
raw_tags = []
|
||||||
|
html_tags = []
|
||||||
|
for tag in FormattingTags.get_html_tags():
|
||||||
|
if tag['start tag'] == '{br}':
|
||||||
|
continue
|
||||||
|
if raw_text.count(tag['start tag']) != raw_text.count(tag['end tag']):
|
||||||
|
raw_tags.append((raw_text.find(tag['start tag']), tag['start tag'], tag['end tag']))
|
||||||
|
html_tags.append((raw_text.find(tag['start tag']), tag['start html']))
|
||||||
|
# Sort the lists, so that the tags which were opened first on the first slide (the text we are checking) will be
|
||||||
|
# opened first on the next slide as well.
|
||||||
|
raw_tags.sort(key=lambda tag: tag[0])
|
||||||
|
html_tags.sort(key=lambda tag: tag[0])
|
||||||
|
# Create a list with closing tags for the raw_text.
|
||||||
|
end_tags = []
|
||||||
|
start_tags = []
|
||||||
|
for tag in raw_tags:
|
||||||
|
start_tags.append(tag[1])
|
||||||
|
end_tags.append(tag[2])
|
||||||
|
end_tags.reverse()
|
||||||
|
# Remove the indexes.
|
||||||
|
html_tags = [tag[1] for tag in html_tags]
|
||||||
|
return raw_text + ''.join(end_tags), ''.join(start_tags), ''.join(html_tags)
|
||||||
|
|
||||||
|
|
||||||
|
class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow):
|
||||||
|
"""
|
||||||
|
A virtual display used for rendering thumbnails and other offscreen tasks
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Constructor
|
||||||
|
"""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.force_page = False
|
||||||
|
for screen in ScreenList():
|
||||||
|
if screen.is_display:
|
||||||
|
self.setGeometry(screen.geometry.x(), screen.geometry.y(),
|
||||||
|
screen.geometry.width(), screen.geometry.height())
|
||||||
|
break
|
||||||
|
# If the display is not show'ed and hidden like this webegine will not render
|
||||||
|
self.show()
|
||||||
|
self.hide()
|
||||||
|
self.theme_height = 0
|
||||||
|
|
||||||
|
def calculate_line_count(self):
|
||||||
|
"""
|
||||||
|
Calculate the number of lines that fits on one slide
|
||||||
|
"""
|
||||||
|
return self.run_javascript('Display.calculateLineCount();', is_sync=True)
|
||||||
|
|
||||||
|
def clear_slides(self):
|
||||||
|
"""
|
||||||
|
Clear slides
|
||||||
|
"""
|
||||||
|
return self.run_javascript('Display.clearSlides();')
|
||||||
|
|
||||||
|
def format_slide(self, text, item):
|
||||||
|
"""
|
||||||
|
Calculate how much text can fit on a slide.
|
||||||
|
|
||||||
|
:param text: The words to go on the slides.
|
||||||
|
:param item: The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
while not self._is_initialised:
|
||||||
|
QtWidgets.QApplication.instance().processEvents()
|
||||||
|
self.log_debug('format slide')
|
||||||
|
theme_name = item.theme if item.theme else Registry().get('theme_manager').global_theme
|
||||||
|
theme_data = Registry().get('theme_manager').get_theme_data(theme_name)
|
||||||
|
self.theme_height = theme_data.font_main_height
|
||||||
|
|
||||||
|
# Set theme for preview
|
||||||
|
self.set_theme(theme_data)
|
||||||
|
# Add line endings after each line of text used for bibles.
|
||||||
|
line_end = '<br>'
|
||||||
|
if item.is_capable(ItemCapabilities.NoLineBreaks):
|
||||||
|
line_end = ' '
|
||||||
|
# Bibles
|
||||||
|
if item.is_capable(ItemCapabilities.CanWordSplit):
|
||||||
|
pages = self._paginate_slide_words(text.split('\n'), line_end)
|
||||||
|
# Songs and Custom
|
||||||
|
elif item.is_capable(ItemCapabilities.CanSoftBreak):
|
||||||
|
pages = []
|
||||||
|
if '[---]' in text:
|
||||||
|
# Remove Overflow split if at start of the text
|
||||||
|
if text.startswith('[---]'):
|
||||||
|
text = text[5:]
|
||||||
|
# Remove two or more option slide breaks next to each other (causing infinite loop).
|
||||||
|
while '\n[---]\n[---]\n' in text:
|
||||||
|
text = text.replace('\n[---]\n[---]\n', '\n[---]\n')
|
||||||
|
while ' [---]' in text:
|
||||||
|
text = text.replace(' [---]', '[---]')
|
||||||
|
while '[---] ' in text:
|
||||||
|
text = text.replace('[---] ', '[---]')
|
||||||
|
count = 0
|
||||||
|
# only loop 5 times as there will never be more than 5 incorrect logical splits on a single slide.
|
||||||
|
while True and count < 5:
|
||||||
|
slides = text.split('\n[---]\n', 2)
|
||||||
|
# If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last
|
||||||
|
# for now).
|
||||||
|
if len(slides) == 3:
|
||||||
|
html_text = expand_tags('\n'.join(slides[:2]))
|
||||||
|
# We check both slides to determine if the optional split is needed (there is only one optional
|
||||||
|
# split).
|
||||||
|
else:
|
||||||
|
html_text = expand_tags('\n'.join(slides))
|
||||||
|
html_text = html_text.replace('\n', '<br>')
|
||||||
|
if self._text_fits_on_slide(html_text):
|
||||||
|
# The first two optional slides fit (as a whole) on one slide. Replace the first occurrence
|
||||||
|
# of [---].
|
||||||
|
text = text.replace('\n[---]', '', 1)
|
||||||
|
else:
|
||||||
|
# The first optional slide fits, which means we have to render the first optional slide.
|
||||||
|
text_contains_split = '[---]' in text
|
||||||
|
if text_contains_split:
|
||||||
|
try:
|
||||||
|
text_to_render, text = text.split('\n[---]\n', 1)
|
||||||
|
except ValueError:
|
||||||
|
text_to_render = text.split('\n[---]\n')[0]
|
||||||
|
text = ''
|
||||||
|
text_to_render, raw_tags, html_tags = get_start_tags(text_to_render)
|
||||||
|
if text:
|
||||||
|
text = raw_tags + text
|
||||||
|
else:
|
||||||
|
text_to_render = text
|
||||||
|
text = ''
|
||||||
|
lines = text_to_render.strip('\n').split('\n')
|
||||||
|
slides = self._paginate_slide(lines, line_end)
|
||||||
|
if len(slides) > 1 and text:
|
||||||
|
# Add all slides apart from the last one the list.
|
||||||
|
pages.extend(slides[:-1])
|
||||||
|
if text_contains_split:
|
||||||
|
text = slides[-1] + '\n[---]\n' + text
|
||||||
|
else:
|
||||||
|
text = slides[-1] + '\n' + text
|
||||||
|
text = text.replace('<br>', '\n')
|
||||||
|
else:
|
||||||
|
pages.extend(slides)
|
||||||
|
if '[---]' not in text:
|
||||||
|
lines = text.strip('\n').split('\n')
|
||||||
|
pages.extend(self._paginate_slide(lines, line_end))
|
||||||
|
break
|
||||||
|
count += 1
|
||||||
|
else:
|
||||||
|
# Clean up line endings.
|
||||||
|
pages = self._paginate_slide(text.split('\n'), line_end)
|
||||||
|
else:
|
||||||
|
pages = self._paginate_slide(text.split('\n'), line_end)
|
||||||
|
new_pages = []
|
||||||
|
for page in pages:
|
||||||
|
while page.endswith('<br>'):
|
||||||
|
page = page[:-4]
|
||||||
|
new_pages.append(page)
|
||||||
|
return new_pages
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
:param lines: The text to be fitted on the slide split into lines.
|
||||||
|
:param line_end: The text added after each line. Either ``' '`` or ``'<br>``.
|
||||||
|
"""
|
||||||
|
formatted = []
|
||||||
|
previous_html = ''
|
||||||
|
previous_raw = ''
|
||||||
|
separator = '<br>'
|
||||||
|
html_lines = list(map(render_tags, lines))
|
||||||
|
# Text too long so go to next page.
|
||||||
|
if not 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, '')
|
||||||
|
else:
|
||||||
|
previous_raw = separator.join(lines)
|
||||||
|
formatted.append(previous_raw)
|
||||||
|
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.
|
||||||
|
|
||||||
|
:param lines: The text to be fitted on the slide split into lines.
|
||||||
|
:param line_end: The text added after each line. Either ``' '`` or ``'<br>``. This is needed for **bibles**.
|
||||||
|
"""
|
||||||
|
formatted = []
|
||||||
|
previous_html = ''
|
||||||
|
previous_raw = ''
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
html_line = expand_tags(line)
|
||||||
|
# Text too long so go to next page.
|
||||||
|
if not 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:
|
||||||
|
if self._text_fits_on_slide(previous_html):
|
||||||
|
formatted.append(previous_raw)
|
||||||
|
previous_html = ''
|
||||||
|
previous_raw = ''
|
||||||
|
# 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._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 as the line will not fit as a whole.
|
||||||
|
raw_words = words_split(line)
|
||||||
|
html_words = list(map(expand_tags, raw_words))
|
||||||
|
previous_html, previous_raw = \
|
||||||
|
self._binary_chop(formatted, previous_html, previous_raw, html_words, raw_words, ' ', line_end)
|
||||||
|
else:
|
||||||
|
previous_html += html_line + line_end
|
||||||
|
previous_raw += line + line_end
|
||||||
|
formatted.append(previous_raw)
|
||||||
|
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.
|
||||||
|
|
||||||
|
:param formatted: The list to append any slides.
|
||||||
|
:param 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)
|
||||||
|
:param 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)
|
||||||
|
:param html_list: The elements which do not fit on a slide and needs to be processed using the binary chop.
|
||||||
|
The text contains html.
|
||||||
|
:param 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.
|
||||||
|
:param separator: The separator for the elements. For lines this is ``'<br>'`` and for words this is ``' '``.
|
||||||
|
:param line_end: The text added after each "element line". Either ``' '`` or ``'<br>``. This is needed for
|
||||||
|
bibles.
|
||||||
|
"""
|
||||||
|
smallest_index = 0
|
||||||
|
highest_index = len(html_list) - 1
|
||||||
|
index = highest_index // 2
|
||||||
|
while True:
|
||||||
|
if not self._text_fits_on_slide(previous_html + separator.join(html_list[:index + 1]).strip()):
|
||||||
|
# We know that it does not fit, so change/calculate the new index and highest_index accordingly.
|
||||||
|
highest_index = index
|
||||||
|
index = index - (index - smallest_index) // 2
|
||||||
|
else:
|
||||||
|
smallest_index = index
|
||||||
|
index = index + (highest_index - index) // 2
|
||||||
|
# We found the number of words which will fit.
|
||||||
|
if smallest_index == index or highest_index == index:
|
||||||
|
index = smallest_index
|
||||||
|
text = previous_raw.rstrip('<br>') + separator.join(raw_list[:index + 1])
|
||||||
|
text, raw_tags, html_tags = get_start_tags(text)
|
||||||
|
formatted.append(text)
|
||||||
|
previous_html = ''
|
||||||
|
previous_raw = ''
|
||||||
|
# Stop here as the theme line count was requested.
|
||||||
|
if self.force_page:
|
||||||
|
Registry().execute('theme_line_count', index + 1)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
# Check if the remaining elements fit on the slide.
|
||||||
|
if self._text_fits_on_slide(html_tags + separator.join(html_list[index + 1:]).strip()):
|
||||||
|
previous_html = html_tags + separator.join(html_list[index + 1:]).strip() + line_end
|
||||||
|
previous_raw = raw_tags + 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:]
|
||||||
|
raw_list[0] = raw_tags + raw_list[0]
|
||||||
|
html_list = html_list[index + 1:]
|
||||||
|
html_list[0] = html_tags + html_list[0]
|
||||||
|
smallest_index = 0
|
||||||
|
highest_index = len(html_list) - 1
|
||||||
|
index = 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``.
|
||||||
|
|
||||||
|
:param text: The text to check. It may contain HTML tags.
|
||||||
|
"""
|
||||||
|
self.clear_slides()
|
||||||
|
self.run_javascript('Display.addTextSlide("v1", "{text}");'.format(text=text), is_sync=True)
|
||||||
|
does_text_fits = self.run_javascript('Display.doesContentFit();', is_sync=True)
|
||||||
|
return does_text_fits
|
||||||
|
@ -81,9 +81,13 @@ class Screen(object):
|
|||||||
:return: A Screen object with the values from screen_dict
|
:return: A Screen object with the values from screen_dict
|
||||||
:rtype: openlp.core.display.screens.Screen
|
:rtype: openlp.core.display.screens.Screen
|
||||||
"""
|
"""
|
||||||
screen_dict['geometry'] = QtCore.QRect(**screen_dict['geometry'])
|
screen_dict['geometry'] = QtCore.QRect(screen_dict['geometry']['x'], screen_dict['geometry']['y'],
|
||||||
|
screen_dict['geometry']['width'], screen_dict['geometry']['height'])
|
||||||
if 'display_geometry' in screen_dict:
|
if 'display_geometry' in screen_dict:
|
||||||
screen_dict['display_geometry'] = QtCore.QRect(**screen_dict['display_geometry'])
|
screen_dict['display_geometry'] = QtCore.QRect(screen_dict['display_geometry']['x'],
|
||||||
|
screen_dict['display_geometry']['y'],
|
||||||
|
screen_dict['display_geometry']['width'],
|
||||||
|
screen_dict['display_geometry']['height'])
|
||||||
return cls(**screen_dict)
|
return cls(**screen_dict)
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
|
@ -154,6 +154,7 @@ class DisplayWindow(QtWidgets.QWidget):
|
|||||||
Add stuff after page initialisation
|
Add stuff after page initialisation
|
||||||
"""
|
"""
|
||||||
self.run_javascript('Display.init();')
|
self.run_javascript('Display.init();')
|
||||||
|
self._is_initialised = True
|
||||||
|
|
||||||
def run_javascript(self, script, is_sync=False):
|
def run_javascript(self, script, is_sync=False):
|
||||||
"""
|
"""
|
||||||
|
@ -80,6 +80,103 @@ class ServiceItemAction(object):
|
|||||||
Next = 3
|
Next = 3
|
||||||
|
|
||||||
|
|
||||||
|
class ItemCapabilities(object):
|
||||||
|
"""
|
||||||
|
Provides an enumeration of a service item's capabilities
|
||||||
|
|
||||||
|
``CanPreview``
|
||||||
|
The capability to allow the ServiceManager to add to the preview tab when making the previous item live.
|
||||||
|
|
||||||
|
``CanEdit``
|
||||||
|
The capability to allow the ServiceManager to allow the item to be edited
|
||||||
|
|
||||||
|
``CanMaintain``
|
||||||
|
The capability to allow the ServiceManager to allow the item to be reordered.
|
||||||
|
|
||||||
|
``RequiresMedia``
|
||||||
|
Determines is the service_item needs a Media Player
|
||||||
|
|
||||||
|
``CanLoop``
|
||||||
|
The capability to allow the SlideController to allow the loop processing.
|
||||||
|
|
||||||
|
``CanAppend``
|
||||||
|
The capability to allow the ServiceManager to add leaves to the
|
||||||
|
item
|
||||||
|
|
||||||
|
``NoLineBreaks``
|
||||||
|
The capability to remove lines breaks in the renderer
|
||||||
|
|
||||||
|
``OnLoadUpdate``
|
||||||
|
The capability to update MediaManager when a service Item is loaded.
|
||||||
|
|
||||||
|
``AddIfNewItem``
|
||||||
|
Not Used
|
||||||
|
|
||||||
|
``ProvidesOwnDisplay``
|
||||||
|
The capability to tell the SlideController the service Item has a different display.
|
||||||
|
|
||||||
|
``HasDetailedTitleDisplay``
|
||||||
|
Being Removed and decommissioned.
|
||||||
|
|
||||||
|
``HasVariableStartTime``
|
||||||
|
The capability to tell the ServiceManager that a change to start time is possible.
|
||||||
|
|
||||||
|
``CanSoftBreak``
|
||||||
|
The capability to tell the renderer that Soft Break is allowed
|
||||||
|
|
||||||
|
``CanWordSplit``
|
||||||
|
The capability to tell the renderer that it can split words is
|
||||||
|
allowed
|
||||||
|
|
||||||
|
``HasBackgroundAudio``
|
||||||
|
That a audio file is present with the text.
|
||||||
|
|
||||||
|
``CanAutoStartForLive``
|
||||||
|
The capability to ignore the do not play if display blank flag.
|
||||||
|
|
||||||
|
``CanEditTitle``
|
||||||
|
The capability to edit the title of the item
|
||||||
|
|
||||||
|
``IsOptical``
|
||||||
|
Determines is the service_item is based on an optical device
|
||||||
|
|
||||||
|
``HasDisplayTitle``
|
||||||
|
The item contains 'displaytitle' on every frame which should be
|
||||||
|
preferred over 'title' when displaying the item
|
||||||
|
|
||||||
|
``HasNotes``
|
||||||
|
The item contains 'notes'
|
||||||
|
|
||||||
|
``HasThumbnails``
|
||||||
|
The item has related thumbnails available
|
||||||
|
|
||||||
|
``HasMetaData``
|
||||||
|
The item has Meta Data about item
|
||||||
|
"""
|
||||||
|
CanPreview = 1
|
||||||
|
CanEdit = 2
|
||||||
|
CanMaintain = 3
|
||||||
|
RequiresMedia = 4
|
||||||
|
CanLoop = 5
|
||||||
|
CanAppend = 6
|
||||||
|
NoLineBreaks = 7
|
||||||
|
OnLoadUpdate = 8
|
||||||
|
AddIfNewItem = 9
|
||||||
|
ProvidesOwnDisplay = 10
|
||||||
|
# HasDetailedTitleDisplay = 11
|
||||||
|
HasVariableStartTime = 12
|
||||||
|
CanSoftBreak = 13
|
||||||
|
CanWordSplit = 14
|
||||||
|
HasBackgroundAudio = 15
|
||||||
|
CanAutoStartForLive = 16
|
||||||
|
CanEditTitle = 17
|
||||||
|
IsOptical = 18
|
||||||
|
HasDisplayTitle = 19
|
||||||
|
HasNotes = 20
|
||||||
|
HasThumbnails = 21
|
||||||
|
HasMetaData = 22
|
||||||
|
|
||||||
|
|
||||||
def get_text_file_string(text_file_path):
|
def get_text_file_string(text_file_path):
|
||||||
"""
|
"""
|
||||||
Open a file and return its content as a string. If the supplied file path is not a file then the function
|
Open a file and return its content as a string. If the supplied file path is not a file then the function
|
||||||
|
@ -40,7 +40,7 @@ from openlp.core.common.mixins import RegistryProperties
|
|||||||
from openlp.core.common.path import Path
|
from openlp.core.common.path import Path
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.core.display.render import remove_tags, render_tags
|
from openlp.core.display.render import remove_tags, render_tags
|
||||||
from openlp.core.lib import ImageSource, build_icon
|
from openlp.core.lib import ImageSource, ItemCapabilities, build_icon
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -54,103 +54,6 @@ class ServiceItemType(object):
|
|||||||
Command = 3
|
Command = 3
|
||||||
|
|
||||||
|
|
||||||
class ItemCapabilities(object):
|
|
||||||
"""
|
|
||||||
Provides an enumeration of a service item's capabilities
|
|
||||||
|
|
||||||
``CanPreview``
|
|
||||||
The capability to allow the ServiceManager to add to the preview tab when making the previous item live.
|
|
||||||
|
|
||||||
``CanEdit``
|
|
||||||
The capability to allow the ServiceManager to allow the item to be edited
|
|
||||||
|
|
||||||
``CanMaintain``
|
|
||||||
The capability to allow the ServiceManager to allow the item to be reordered.
|
|
||||||
|
|
||||||
``RequiresMedia``
|
|
||||||
Determines is the service_item needs a Media Player
|
|
||||||
|
|
||||||
``CanLoop``
|
|
||||||
The capability to allow the SlideController to allow the loop processing.
|
|
||||||
|
|
||||||
``CanAppend``
|
|
||||||
The capability to allow the ServiceManager to add leaves to the
|
|
||||||
item
|
|
||||||
|
|
||||||
``NoLineBreaks``
|
|
||||||
The capability to remove lines breaks in the renderer
|
|
||||||
|
|
||||||
``OnLoadUpdate``
|
|
||||||
The capability to update MediaManager when a service Item is loaded.
|
|
||||||
|
|
||||||
``AddIfNewItem``
|
|
||||||
Not Used
|
|
||||||
|
|
||||||
``ProvidesOwnDisplay``
|
|
||||||
The capability to tell the SlideController the service Item has a different display.
|
|
||||||
|
|
||||||
``HasDetailedTitleDisplay``
|
|
||||||
Being Removed and decommissioned.
|
|
||||||
|
|
||||||
``HasVariableStartTime``
|
|
||||||
The capability to tell the ServiceManager that a change to start time is possible.
|
|
||||||
|
|
||||||
``CanSoftBreak``
|
|
||||||
The capability to tell the renderer that Soft Break is allowed
|
|
||||||
|
|
||||||
``CanWordSplit``
|
|
||||||
The capability to tell the renderer that it can split words is
|
|
||||||
allowed
|
|
||||||
|
|
||||||
``HasBackgroundAudio``
|
|
||||||
That a audio file is present with the text.
|
|
||||||
|
|
||||||
``CanAutoStartForLive``
|
|
||||||
The capability to ignore the do not play if display blank flag.
|
|
||||||
|
|
||||||
``CanEditTitle``
|
|
||||||
The capability to edit the title of the item
|
|
||||||
|
|
||||||
``IsOptical``
|
|
||||||
Determines is the service_item is based on an optical device
|
|
||||||
|
|
||||||
``HasDisplayTitle``
|
|
||||||
The item contains 'displaytitle' on every frame which should be
|
|
||||||
preferred over 'title' when displaying the item
|
|
||||||
|
|
||||||
``HasNotes``
|
|
||||||
The item contains 'notes'
|
|
||||||
|
|
||||||
``HasThumbnails``
|
|
||||||
The item has related thumbnails available
|
|
||||||
|
|
||||||
``HasMetaData``
|
|
||||||
The item has Meta Data about item
|
|
||||||
"""
|
|
||||||
CanPreview = 1
|
|
||||||
CanEdit = 2
|
|
||||||
CanMaintain = 3
|
|
||||||
RequiresMedia = 4
|
|
||||||
CanLoop = 5
|
|
||||||
CanAppend = 6
|
|
||||||
NoLineBreaks = 7
|
|
||||||
OnLoadUpdate = 8
|
|
||||||
AddIfNewItem = 9
|
|
||||||
ProvidesOwnDisplay = 10
|
|
||||||
# HasDetailedTitleDisplay = 11
|
|
||||||
HasVariableStartTime = 12
|
|
||||||
CanSoftBreak = 13
|
|
||||||
CanWordSplit = 14
|
|
||||||
HasBackgroundAudio = 15
|
|
||||||
CanAutoStartForLive = 16
|
|
||||||
CanEditTitle = 17
|
|
||||||
IsOptical = 18
|
|
||||||
HasDisplayTitle = 19
|
|
||||||
HasNotes = 20
|
|
||||||
HasThumbnails = 21
|
|
||||||
HasMetaData = 22
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceItem(RegistryProperties):
|
class ServiceItem(RegistryProperties):
|
||||||
"""
|
"""
|
||||||
The service item is a base class for the plugins to use to interact with
|
The service item is a base class for the plugins to use to interact with
|
||||||
@ -249,17 +152,45 @@ class ServiceItem(RegistryProperties):
|
|||||||
else:
|
else:
|
||||||
self.icon = UiIcons().clone
|
self.icon = UiIcons().clone
|
||||||
|
|
||||||
|
def _create_slides(self):
|
||||||
|
"""
|
||||||
|
Create frames for rendering and display
|
||||||
|
"""
|
||||||
|
self._rendered_slides = []
|
||||||
|
self._display_slides = []
|
||||||
|
|
||||||
|
# Save rendered pages to this dict. In the case that a slide is used twice we can use the pages saved to
|
||||||
|
# the dict instead of rendering them again.
|
||||||
|
previous_pages = {}
|
||||||
|
#for slide in self._raw_frames:
|
||||||
|
for raw_slide in self.slides:
|
||||||
|
verse_tag = raw_slide['verse']
|
||||||
|
if verse_tag in previous_pages and previous_pages[verse_tag][0] == raw_slide:
|
||||||
|
pages = previous_pages[verse_tag][1]
|
||||||
|
else:
|
||||||
|
pages = self.renderer.format_slide(raw_slide['text'], self)
|
||||||
|
previous_pages[verse_tag] = (raw_slide, pages)
|
||||||
|
for page in pages:
|
||||||
|
rendered_slide = {
|
||||||
|
'title': raw_slide['title'],
|
||||||
|
'text': render_tags(page),
|
||||||
|
'verse': verse_tag,
|
||||||
|
}
|
||||||
|
self._rendered_slides.append(rendered_slide)
|
||||||
|
display_slide = {
|
||||||
|
'title': raw_slide['title'],
|
||||||
|
'text': remove_tags(page),
|
||||||
|
'verse': verse_tag,
|
||||||
|
}
|
||||||
|
self._display_slides.append(display_slide)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rendered_slides(self):
|
def rendered_slides(self):
|
||||||
"""
|
"""
|
||||||
Render the frames and return them
|
Render the frames and return them
|
||||||
"""
|
"""
|
||||||
if not self._rendered_slides:
|
if not self._rendered_slides:
|
||||||
self._rendered_slides = []
|
self._create_slides()
|
||||||
for raw_slide in self.slides:
|
|
||||||
rendered_slide = deepcopy(raw_slide)
|
|
||||||
rendered_slide['text'] = render_tags(rendered_slide['text'])
|
|
||||||
self._rendered_slides.append(rendered_slide)
|
|
||||||
return self._rendered_slides
|
return self._rendered_slides
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -268,11 +199,7 @@ class ServiceItem(RegistryProperties):
|
|||||||
Render the frames and return them
|
Render the frames and return them
|
||||||
"""
|
"""
|
||||||
if not self._display_slides:
|
if not self._display_slides:
|
||||||
self._display_slides = []
|
self._create_slides()
|
||||||
for raw_slide in self.slides:
|
|
||||||
display_slide = deepcopy(raw_slide)
|
|
||||||
display_slide['text'] = remove_tags(display_slide['text'])
|
|
||||||
self._display_slides.append(display_slide)
|
|
||||||
return self._display_slides
|
return self._display_slides
|
||||||
|
|
||||||
# def render(self, provides_own_theme_data=False):
|
# def render(self, provides_own_theme_data=False):
|
||||||
|
@ -43,6 +43,7 @@ from openlp.core.common.registry import Registry
|
|||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
from openlp.core.lib.imagemanager import ImageManager
|
from openlp.core.lib.imagemanager import ImageManager
|
||||||
|
from openlp.core.display.render import Renderer
|
||||||
from openlp.core.lib.plugin import PluginStatus
|
from openlp.core.lib.plugin import PluginStatus
|
||||||
from openlp.core.lib.pluginmanager import PluginManager
|
from openlp.core.lib.pluginmanager import PluginManager
|
||||||
from openlp.core.lib.ui import create_action
|
from openlp.core.lib.ui import create_action
|
||||||
@ -509,6 +510,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
|
|||||||
# Set up the path with plugins
|
# Set up the path with plugins
|
||||||
PluginManager(self)
|
PluginManager(self)
|
||||||
ImageManager()
|
ImageManager()
|
||||||
|
Renderer()
|
||||||
# Set up the interface
|
# Set up the interface
|
||||||
self.setup_ui(self)
|
self.setup_ui(self)
|
||||||
# Define the media Dock Manager
|
# Define the media Dock Manager
|
||||||
|
Loading…
Reference in New Issue
Block a user