-- General code cleanup to better match the architecture

-- Added thumbnails to the remote display
-- Modify the service list to use the displaytitle
This commit is contained in:
Felipe Polo-Wood 2013-10-19 01:27:25 -04:00
parent 3bbaff362d
commit 00fd7f01f7
9 changed files with 105 additions and 76 deletions

View File

@ -107,6 +107,16 @@ class ItemCapabilities(object):
``CanAutoStartForLive`` ``CanAutoStartForLive``
The capability to ignore the do not play if display blank flag. The capability to ignore the do not play if display blank flag.
``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
""" """
CanPreview = 1 CanPreview = 1
CanEdit = 2 CanEdit = 2
@ -124,6 +134,9 @@ class ItemCapabilities(object):
CanWordSplit = 14 CanWordSplit = 14
HasBackgroundAudio = 15 HasBackgroundAudio = 15
CanAutoStartForLive = 16 CanAutoStartForLive = 16
HasDisplayTitle = 17
HasNotes = 18
HasThumbnails = 19
class ServiceItem(object): class ServiceItem(object):
@ -303,7 +316,7 @@ class ServiceItem(object):
self._raw_frames.append({'title': title, 'raw_slide': raw_slide, 'verseTag': verse_tag}) self._raw_frames.append({'title': title, 'raw_slide': raw_slide, 'verseTag': verse_tag})
self._new_item() self._new_item()
def add_from_command(self, path, file_name, image): def add_from_command(self, path, file_name, image, displaytitle=None, notes=None):
""" """
Add a slide from a command. Add a slide from a command.
@ -317,27 +330,8 @@ class ServiceItem(object):
The command of/for the slide. The command of/for the slide.
""" """
self.service_item_type = ServiceItemType.Command self.service_item_type = ServiceItemType.Command
self._raw_frames.append({'title': file_name, 'image': image, 'path': path}) self._raw_frames.append({'title': file_name, 'image': image, 'path': path,
self._new_item() 'displaytitle': displaytitle, 'notes': notes})
def add_from_presentation(self, path, file_name, image, displaytitle, notes):
"""
Add a slide from a presentation.
``path``
The path of the presentation
``file_name``
The filename of the presentation
``image``
Full path (including file name) to the thumbnail
``displaytitle``
The title to display on the list and remote
"""
self.service_item_type = ServiceItemType.Command
self._raw_frames.append({'title': file_name, 'image': image, 'path': path, 'displaytitle': displaytitle, 'notes': notes})
self._new_item() self._new_item()
def get_service_repr(self, lite_save): def get_service_repr(self, lite_save):
@ -382,11 +376,8 @@ class ServiceItem(object):
service_data = [slide['title'] for slide in self._raw_frames] service_data = [slide['title'] for slide in self._raw_frames]
elif self.service_item_type == ServiceItemType.Command: elif self.service_item_type == ServiceItemType.Command:
for slide in self._raw_frames: for slide in self._raw_frames:
#if len(slide['displaytitle'])>0:
service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'], service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'],
'displaytitle': slide['displaytitle'], 'notes': slide['notes']}) 'displaytitle': slide['displaytitle'], 'notes': slide['notes']})
#else:
# service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path']})
return {'header': service_header, 'data': service_data} return {'header': service_header, 'data': service_data}
def set_from_service(self, serviceitem, path=None): def set_from_service(self, serviceitem, path=None):
@ -458,10 +449,8 @@ class ServiceItem(object):
self.title = text_image['title'] self.title = text_image['title']
if path: if path:
self.has_original_files = False self.has_original_files = False
if serviceitem['serviceitem']['header']['plugin']=='presentations': self.add_from_command(path, text_image['title'], text_image['image'],
self.add_from_presentation(path, text_image['title'], text_image['image'], text_image['displaytitle'], text_image['notes']) text_image['displaytitle'], text_image['notes'])
else:
self.add_from_command(path, text_image['title'], text_image['image'])
else: else:
self.add_from_command(text_image['path'], text_image['title'], text_image['image']) self.add_from_command(text_image['path'], text_image['title'], text_image['image'])
self._new_item() self._new_item()

View File

@ -1185,6 +1185,13 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
# Add the children to their parent treewidgetitem. # Add the children to their parent treewidgetitem.
for count, frame in enumerate(serviceitem.get_frames()): for count, frame in enumerate(serviceitem.get_frames()):
child = QtGui.QTreeWidgetItem(treewidgetitem) child = QtGui.QTreeWidgetItem(treewidgetitem)
# prefer to use a displaytitle
if serviceitem.is_capable(ItemCapabilities.HasDisplayTitle):
text = frame['displaytitle'].replace('\n',' ')
# oops, it is missing, let's make one up
if len(text.strip()) == 0:
text = '[slide ' + str(count+1) + ']'
else:
text = frame['title'].replace('\n', ' ') text = frame['title'].replace('\n', ' ')
child.setText(0, text[:40]) child.setText(0, text[:40])
child.setData(0, QtCore.Qt.UserRole, count) child.setData(0, QtCore.Qt.UserRole, count)

View File

@ -466,6 +466,7 @@ class ImpressDocument(PresentationDocument):
shape = page.getByIndex(index) shape = page.getByIndex(index)
shapeType = shape.getShapetype() shapeType = shape.getShapetype()
if shape.supportsService("com.sun.star.drawing.Text"): if shape.supportsService("com.sun.star.drawing.Text"):
# if they requested title, make sure it is the title
if text_type!=TextType.Title or shapeType == "com.sun.star.presentation.TitleTextShape": if text_type!=TextType.Title or shapeType == "com.sun.star.presentation.TitleTextShape":
text += shape.getString() + '\n' text += shape.getString() + '\n'
return text return text
@ -492,8 +493,3 @@ class ImpressDocument(PresentationDocument):
fo.writelines(titles) fo.writelines(titles)
return return
def get_titles_and_notes(self):
"""
let the super class handle it
"""
return super().get_titles_and_notes()

View File

@ -250,6 +250,7 @@ class PresentationMediaItem(MediaManagerItem):
return False return False
service_item.processor = self.display_type_combo_box.currentText() service_item.processor = self.display_type_combo_box.currentText()
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
service_item.add_capability(ItemCapabilities.HasThumbnails)
if not self.display_type_combo_box.currentText(): if not self.display_type_combo_box.currentText():
return False return False
for bitem in items: for bitem in items:
@ -264,6 +265,10 @@ class PresentationMediaItem(MediaManagerItem):
controller = self.controllers[service_item.processor] controller = self.controllers[service_item.processor]
doc = controller.add_document(filename) doc = controller.add_document(filename)
titles, notes = doc.get_titles_and_notes() titles, notes = doc.get_titles_and_notes()
if len(titles) > 0:
service_item.add_capability(ItemCapabilities.HasDisplayTitle)
if len(notes) > 0:
service_item.add_capability(ItemCapabilities.HasNotes)
if doc.get_thumbnail_path(1, True) is None: if doc.get_thumbnail_path(1, True) is None:
doc.load_presentation() doc.load_presentation()
i = 1 i = 1
@ -276,7 +281,7 @@ class PresentationMediaItem(MediaManagerItem):
note = '' note = ''
if i <= len(notes): if i <= len(notes):
note = notes[i-1] note = notes[i-1]
service_item.add_from_presentation(path, name, img, title, note) service_item.add_from_command(path, name, img, title, note)
i += 1 i += 1
img = doc.get_thumbnail_path(i, True) img = doc.get_thumbnail_path(i, True)
doc.close_presentation() doc.close_presentation()

View File

@ -344,12 +344,6 @@ class PowerpointDocument(PresentationDocument):
fo.writelines(titles) fo.writelines(titles)
return return
def get_titles_and_notes(self):
"""
let the super class handle it
"""
return super().get_titles_and_notes()
def _get_text_from_shapes(shapes): def _get_text_from_shapes(shapes):
""" """
Returns any text extracted from the shapes on a presentation slide. Returns any text extracted from the shapes on a presentation slide.

View File

@ -179,12 +179,14 @@ class PptviewDocument(PresentationDocument):
index = -1 index = -1
listToAdd = None listToAdd = None
# check if it is a slide
match = re.search("slides/slide(.+)\.xml", zip_info.filename) match = re.search("slides/slide(.+)\.xml", zip_info.filename)
if match: if match:
index = int(match.group(1))-1 index = int(match.group(1))-1
nodeType = 'ctrTitle' nodeType = 'ctrTitle'
listToAdd = titles listToAdd = titles
# or a note
match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename) match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename)
if match: if match:
index = int(match.group(1))-1 index = int(match.group(1))-1
@ -197,7 +199,9 @@ class PptviewDocument(PresentationDocument):
tree = ElementTree.parse(zipped_file) tree = ElementTree.parse(zipped_file)
text = '' text = ''
nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", namespaces=namespaces) nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t",
namespaces=namespaces)
# if we found any content
if nodes and len(nodes)>0: if nodes and len(nodes)>0:
for node in nodes: for node in nodes:
if len(text) > 0: if len(text) > 0:
@ -205,7 +209,7 @@ class PptviewDocument(PresentationDocument):
text += node.text text += node.text
print( 'slide file: ' + zip_info.filename + ' ' + text ) print( 'slide file: ' + zip_info.filename + ' ' + text )
# let's remove the nl from the titles and just add one at the end # let's remove the \n from the titles and just add one at the end
if nodeType == 'ctrTitle': if nodeType == 'ctrTitle':
text = text.replace('\n',' ').replace('\x0b', ' ') + '\n' text = text.replace('\n',' ').replace('\x0b', ' ') + '\n'
listToAdd[index] = text listToAdd[index] = text
@ -319,9 +323,3 @@ class PptviewDocument(PresentationDocument):
Triggers the previous slide on the running presentation. Triggers the previous slide on the running presentation.
""" """
self.controller.process.PrevStep(self.ppt_id) self.controller.process.PrevStep(self.ppt_id)
def get_titles_and_notes(self):
"""
let the super class handle it
"""
return super().get_titles_and_notes()

View File

@ -209,7 +209,8 @@ class PPTViewer(QtGui.QWidget):
tree = ElementTree.parse(zipped_file) tree = ElementTree.parse(zipped_file)
text = '' text = ''
nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", namespaces=namespaces) nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t",
namespaces=namespaces)
if nodes and len(nodes)>0: if nodes and len(nodes)>0:
for node in nodes: for node in nodes:
if len(text) > 0: if len(text) > 0:

View File

@ -87,13 +87,21 @@ window.OpenLP = {
var ul = $("#slide-controller > div[data-role=content] > ul[data-role=listview]"); var ul = $("#slide-controller > div[data-role=content] > ul[data-role=listview]");
ul.html(""); ul.html("");
for (idx in data.results.slides) { for (idx in data.results.slides) {
var text = data.results.slides[idx]["tag"]; var slide = data.results.slides[idx];
var text = slide["tag"];
if (text != "") text = text + ": "; if (text != "") text = text + ": ";
text = text + data.results.slides[idx]["text"]; if (slide["title"])
text += slide["title"]
else
text += slide["text"];
if (slide["notes"])
text += ("<div style='font-size:smaller;font-weight:normal'>" + slide["notes"] + "</div>");
text = text.replace(/\n/g, '<br />'); text = text.replace(/\n/g, '<br />');
if (slide["img"])
text += "<img src='" + slide["img"] + "'>";
var li = $("<li data-icon=\"false\">").append( var li = $("<li data-icon=\"false\">").append(
$("<a href=\"#\">").attr("value", parseInt(idx, 10)).html(text)); $("<a href=\"#\">").attr("value", parseInt(idx, 10)).html(text));
if (data.results.slides[idx]["selected"]) { if (slide["selected"]) {
li.attr("data-theme", "e"); li.attr("data-theme", "e");
} }
li.children("a").click(OpenLP.setSlide); li.children("a").click(OpenLP.setSlide);

View File

@ -125,7 +125,7 @@ from urllib.parse import urlparse, parse_qs
from mako.template import Template from mako.template import Template
from PyQt4 import QtCore from PyQt4 import QtCore
from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte, resize_image, ItemCapabilities
from openlp.core.utils import AppLocation, translate from openlp.core.utils import AppLocation, translate
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -151,6 +151,7 @@ class HttpRouter(object):
('^/(stage)$', {'function': self.serve_file, 'secure': False}), ('^/(stage)$', {'function': self.serve_file, 'secure': False}),
('^/(main)$', {'function': self.serve_file, 'secure': False}), ('^/(main)$', {'function': self.serve_file, 'secure': False}),
(r'^/files/(.*)$', {'function': self.serve_file, 'secure': False}), (r'^/files/(.*)$', {'function': self.serve_file, 'secure': False}),
(r'^/(.*)/thumbnails/(.*)$', {'function': self.serve_thumbnail, 'secure': False}),
(r'^/api/poll$', {'function': self.poll, 'secure': False}), (r'^/api/poll$', {'function': self.poll, 'secure': False}),
(r'^/main/poll$', {'function': self.main_poll, 'secure': False}), (r'^/main/poll$', {'function': self.main_poll, 'secure': False}),
(r'^/main/image$', {'function': self.main_image, 'secure': False}), (r'^/main/image$', {'function': self.main_image, 'secure': False}),
@ -347,26 +348,10 @@ class HttpRouter(object):
path = os.path.normpath(os.path.join(self.html_dir, file_name)) path = os.path.normpath(os.path.join(self.html_dir, file_name))
if not path.startswith(self.html_dir): if not path.startswith(self.html_dir):
return self.do_not_found() return self.do_not_found()
ext = os.path.splitext(file_name)[1]
html = None html = None
if ext == '.html': if self.send_appropriate_header(file_name) == '.html':
self.send_header('Content-type', 'text/html')
variables = self.template_vars variables = self.template_vars
html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables) html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables)
elif ext == '.css':
self.send_header('Content-type', 'text/css')
elif ext == '.js':
self.send_header('Content-type', 'application/javascript')
elif ext == '.jpg':
self.send_header('Content-type', 'image/jpeg')
elif ext == '.gif':
self.send_header('Content-type', 'image/gif')
elif ext == '.ico':
self.send_header('Content-type', 'image/x-icon')
elif ext == '.png':
self.send_header('Content-type', 'image/png')
else:
self.send_header('Content-type', 'text/plain')
file_handle = None file_handle = None
try: try:
if html: if html:
@ -383,6 +368,44 @@ class HttpRouter(object):
file_handle.close() file_handle.close()
return content return content
def send_appropriate_header(self, file_name):
ext = os.path.splitext(file_name)[1]
if ext == '.html':
self.send_header('Content-type', 'text/html')
elif ext == '.css':
self.send_header('Content-type', 'text/css')
elif ext == '.js':
self.send_header('Content-type', 'application/javascript')
elif ext == '.jpg':
self.send_header('Content-type', 'image/jpeg')
elif ext == '.gif':
self.send_header('Content-type', 'image/gif')
elif ext == '.ico':
self.send_header('Content-type', 'image/x-icon')
elif ext == '.png':
self.send_header('Content-type', 'image/png')
else:
self.send_header('Content-type', 'text/plain')
return ext
def serve_thumbnail(self, controller_name=None, file_name=None):
"""
Serve an image file. If not found return 404.
"""
log.debug('serve thumbnail %s/thumbnails/%s' % (controller_name, file_name))
content = ''
full_path = os.path.join(AppLocation.get_section_data_path(controller_name),
'thumbnails/' + file_name.replace('/','\\') )
full_path = urllib.parse.unquote(full_path)
if os.path.exists(full_path):
self.send_appropriate_header(full_path)
file_handle = open(full_path, 'rb')
content = file_handle.read()
else:
content = self.do_not_found()
return content
def poll(self): def poll(self):
""" """
Poll OpenLP to determine the current slide number and item name. Poll OpenLP to determine the current slide number and item name.
@ -470,12 +493,20 @@ class HttpRouter(object):
item['html'] = str(frame['html']) item['html'] = str(frame['html'])
else: else:
item['tag'] = str(index + 1) item['tag'] = str(index + 1)
if current_item.name == 'presentations': if current_item.is_capable(ItemCapabilities.HasDisplayTitle):
item['text'] = str(frame['displaytitle']) + '\n' + str(frame['notes']) item['title'] = str(frame['displaytitle'])
else: if current_item.is_capable(ItemCapabilities.HasNotes):
item['notes'] = str(frame['notes'])
if current_item.is_capable(ItemCapabilities.HasThumbnails):
# if the file is under our app directory tree send the portion after the match
if frame['image'][0:len(AppLocation.get_data_path())] == AppLocation.get_data_path():
item['img'] = frame['image'][len(AppLocation.get_data_path()):]
#'data:image/png;base64,' + str(image_to_byte(resize_image(frame['image'],80,80)))
item['text'] = str(frame['title']) item['text'] = str(frame['title'])
item['html'] = str(frame['title']) item['html'] = str(frame['title'])
item['selected'] = (self.live_controller.selected_row == index) item['selected'] = (self.live_controller.selected_row == index)
if current_item.notes:
item['notes'] = item.get('notes','') + '\n' + current_item.notes
data.append(item) data.append(item)
json_data = {'results': {'slides': data}} json_data = {'results': {'slides': data}}
if current_item: if current_item: