This commit is contained in:
Andreas Preikschat 2013-06-30 19:57:21 +02:00
commit e99ba8a400
6 changed files with 136 additions and 119 deletions

View File

@ -34,8 +34,8 @@ import re
import os import os
import platform import platform
import bs4
import sqlalchemy import sqlalchemy
from bs4 import BeautifulSoup
from lxml import etree from lxml import etree
from PyQt4 import Qt, QtCore, QtGui, QtWebKit from PyQt4 import Qt, QtCore, QtGui, QtWebKit
@ -145,7 +145,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
u'QtWebkit: %s\n' % WEBKIT_VERSION + \ u'QtWebkit: %s\n' % WEBKIT_VERSION + \
u'SQLAlchemy: %s\n' % sqlalchemy.__version__ + \ u'SQLAlchemy: %s\n' % sqlalchemy.__version__ + \
u'SQLAlchemy Migrate: %s\n' % MIGRATE_VERSION + \ u'SQLAlchemy Migrate: %s\n' % MIGRATE_VERSION + \
u'BeautifulSoup: %s\n' % BeautifulSoup.__version__ + \ u'BeautifulSoup: %s\n' % bs4.__version__ + \
u'lxml: %s\n' % etree.__version__ + \ u'lxml: %s\n' % etree.__version__ + \
u'Chardet: %s\n' % CHARDET_VERSION + \ u'Chardet: %s\n' % CHARDET_VERSION + \
u'PyEnchant: %s\n' % ENCHANT_VERSION + \ u'PyEnchant: %s\n' % ENCHANT_VERSION + \

View File

@ -44,113 +44,57 @@ VIDEO_CSS = u"""
z-index:3; z-index:3;
background-color: %(bgcolor)s; background-color: %(bgcolor)s;
} }
#video1 { #video {
background-color: %(bgcolor)s;
z-index:4;
}
#video2 {
background-color: %(bgcolor)s; background-color: %(bgcolor)s;
z-index:4; z-index:4;
} }
""" """
VIDEO_JS = u""" VIDEO_JS = u"""
var video_timer = null; function show_video(state, path, volume, loop, variable_value){
var current_video = '1'; // Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
function show_video(state, path, volume, loop, varVal){ var video = document.getElementById('video');
// Note, the preferred method for looping would be to use the
// video tag loop attribute.
// But QtWebKit doesn't support this. Neither does it support the
// onended event, hence the setInterval()
// In addition, setting the currentTime attribute to zero to restart
// the video raises an INDEX_SIZE_ERROR: DOM Exception 1
// To complicate it further, sometimes vid.currentTime stops
// slightly short of vid.duration and vid.ended is intermittent!
//
// Note, currently the background may go black between loops. Not
// desirable. Need to investigate using two <video>'s, and hiding/
// preloading one, and toggle between the two when looping.
if(current_video=='1'){
var vid = document.getElementById('video1');
var vid2 = document.getElementById('video2');
} else {
var vid = document.getElementById('video2');
var vid2 = document.getElementById('video1');
}
if(volume != null){ if(volume != null){
vid.volume = volume; video.volume = volume;
vid2.volume = volume;
} }
switch(state){ switch(state){
case 'init':
vid.src = 'file:///' + path;
vid2.src = 'file:///' + path;
if(loop == null) loop = false;
vid.looping = loop;
vid2.looping = loop;
vid.load();
break;
case 'load': case 'load':
vid2.style.visibility = 'hidden'; video.src = 'file:///' + path;
vid2.load(); if(loop == true) {
video.loop = true;
}
video.load();
break; break;
case 'play': case 'play':
vid.play(); video.play();
if(vid.looping){
video_timer = setInterval(
function() {
show_video('poll');
}, 200);
}
break; break;
case 'pause': case 'pause':
if(video_timer!=null){ video.pause();
clearInterval(video_timer);
video_timer = null;
}
vid.pause();
break; break;
case 'stop': case 'stop':
show_video('pause'); show_video('pause');
vid.currentTime = 0; video.currentTime = 0;
break;
case 'poll':
if(vid.ended||vid.currentTime+0.2>vid.duration)
show_video('swap');
break;
case 'swap':
show_video('pause');
if(current_video=='1')
current_video = '2';
else
current_video = '1';
show_video('load');
show_video('play');
show_video('setVisible',null,null,null,'visible');
break; break;
case 'close': case 'close':
show_video('stop'); show_video('stop');
vid.src = ''; video.src = '';
vid2.src = '';
break; break;
case 'length': case 'length':
return vid.duration; return video.duration;
case 'currentTime': case 'current_time':
return vid.currentTime; return video.currentTime;
case 'seek': case 'seek':
// doesnt work currently video.currentTime = variable_value;
vid.currentTime = varVal;
break; break;
case 'isEnded': case 'isEnded':
return vid.ended; return video.ended;
case 'setVisible': case 'setVisible':
vid.style.visibility = varVal; video.style.visibility = variable_value;
break; break;
case 'setBackBoard': case 'setBackBoard':
var back = document.getElementById('videobackboard'); var back = document.getElementById('videobackboard');
back.style.visibility = varVal; back.style.visibility = variable_value;
break; break;
} }
} }
@ -158,10 +102,7 @@ VIDEO_JS = u"""
VIDEO_HTML = u""" VIDEO_HTML = u"""
<div id="videobackboard" class="size" style="visibility:hidden"></div> <div id="videobackboard" class="size" style="visibility:hidden"></div>
<video id="video1" class="size" style="visibility:hidden" autobuffer preload> <video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
</video>
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
</video>
""" """
FLASH_CSS = u""" FLASH_CSS = u"""
@ -173,25 +114,21 @@ FLASH_CSS = u"""
FLASH_JS = u""" FLASH_JS = u"""
function getFlashMovieObject(movieName) function getFlashMovieObject(movieName)
{ {
if (window.document[movieName]) if (window.document[movieName]){
{
return window.document[movieName]; return window.document[movieName];
} }
if (document.embeds && document.embeds[movieName]) if (document.embeds && document.embeds[movieName]){
return document.embeds[movieName]; return document.embeds[movieName];
}
} }
function show_flash(state, path, volume, varVal){ function show_flash(state, path, volume, variable_value){
var text = document.getElementById('flash'); var text = document.getElementById('flash');
var flashMovie = getFlashMovieObject("OpenLPFlashMovie"); var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
var src = "src = 'file:///" + path + "'"; var src = "src = 'file:///" + path + "'";
var view_parm = " wmode='opaque'" + var view_parm = " wmode='opaque'" + " width='100%%'" + " height='100%%'";
" width='100%%'" + var swf_parm = " name='OpenLPFlashMovie'" + " autostart='true' loop='false' play='true'" +
" height='100%%'"; " hidden='false' swliveconnect='true' allowscriptaccess='always'" + " volume='" + volume + "'";
var swf_parm = " name='OpenLPFlashMovie'" +
" autostart='true' loop='false' play='true'" +
" hidden='false' swliveconnect='true' allowscriptaccess='always'" +
" volume='" + volume + "'";
switch(state){ switch(state){
case 'load': case 'load':
@ -217,15 +154,16 @@ FLASH_JS = u"""
break; break;
case 'length': case 'length':
return flashMovie.TotalFrames(); return flashMovie.TotalFrames();
case 'currentTime': case 'current_time':
return flashMovie.CurrentFrame(); return flashMovie.CurrentFrame();
case 'seek': case 'seek':
// flashMovie.GotoFrame(varVal); // flashMovie.GotoFrame(variable_value);
break; break;
case 'isEnded': case 'isEnded':
return false;//TODO check flash end //TODO check flash end
return false;
case 'setVisible': case 'setVisible':
text.style.visibility = varVal; text.style.visibility = variable_value;
break; break;
} }
} }
@ -338,7 +276,7 @@ class WebkitPlayer(MediaPlayer):
controller.media_info.is_flash = True controller.media_info.is_flash = True
js = u'show_flash("load","%s");' % (path.replace(u'\\', u'\\\\')) js = u'show_flash("load","%s");' % (path.replace(u'\\', u'\\\\'))
else: else:
js = u'show_video("init", "%s", %s, %s);' % (path.replace(u'\\', u'\\\\'), str(vol), loop) js = u'show_video("load", "%s", %s, %s);' % (path.replace(u'\\', u'\\\\'), str(vol), loop)
display.frame.evaluateJavaScript(js) display.frame.evaluateJavaScript(js)
return True return True
@ -447,25 +385,25 @@ class WebkitPlayer(MediaPlayer):
""" """
controller = display.controller controller = display.controller
if controller.media_info.is_flash: if controller.media_info.is_flash:
currentTime = display.frame.evaluateJavaScript(u'show_flash("currentTime");') current_time = display.frame.evaluateJavaScript(u'show_flash("current_time");')
length = display.frame.evaluateJavaScript(u'show_flash("length");') length = display.frame.evaluateJavaScript(u'show_flash("length");')
else: else:
if display.frame.evaluateJavaScript(u'show_video("isEnded");'): if display.frame.evaluateJavaScript(u'show_video("isEnded");'):
self.stop(display) self.stop(display)
currentTime = display.frame.evaluateJavaScript(u'show_video("currentTime");') current_time = display.frame.evaluateJavaScript(u'show_video("current_time");')
# check if conversion was ok and value is not 'NaN' # check if conversion was ok and value is not 'NaN'
if currentTime and currentTime != float('inf'): if current_time and current_time != float('inf'):
currentTime = int(currentTime * 1000) current_time = int(current_time * 1000)
length = display.frame.evaluateJavaScript(u'show_video("length");') length = display.frame.evaluateJavaScript(u'show_video("length");')
# check if conversion was ok and value is not 'NaN' # check if conversion was ok and value is not 'NaN'
if length and length != float('inf'): if length and length != float('inf'):
length = int(length * 1000) length = int(length * 1000)
if currentTime > 0: if current_time:
controller.media_info.length = length controller.media_info.length = length
controller.seek_slider.setMaximum(length) controller.seek_slider.setMaximum(length)
if not controller.seek_slider.isSliderDown(): if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True) controller.seek_slider.blockSignals(True)
controller.seek_slider.setSliderPosition(currentTime) controller.seek_slider.setSliderPosition(current_time)
controller.seek_slider.blockSignals(False) controller.seek_slider.blockSignals(False)
def get_info(self): def get_info(self):

View File

@ -275,7 +275,6 @@ class Ui_EditSongDialog(object):
self.bottom_layout.setObjectName(u'bottom_layout') self.bottom_layout.setObjectName(u'bottom_layout')
self.warning_label = QtGui.QLabel(edit_song_dialog) self.warning_label = QtGui.QLabel(edit_song_dialog)
self.warning_label.setObjectName(u'warning_label') self.warning_label.setObjectName(u'warning_label')
self.warning_label.setVisible(False)
self.bottom_layout.addWidget(self.warning_label) self.bottom_layout.addWidget(self.warning_label)
self.button_box = create_button_box(edit_song_dialog, u'button_box', [u'cancel', u'save']) self.button_box = create_button_box(edit_song_dialog, u'button_box', [u'cancel', u'save'])
self.bottom_layout.addWidget(self.button_box) self.bottom_layout.addWidget(self.button_box)
@ -323,8 +322,10 @@ class Ui_EditSongDialog(object):
self.from_media_button.setText(translate('SongsPlugin.EditSongForm', 'Add &Media')) self.from_media_button.setText(translate('SongsPlugin.EditSongForm', 'Add &Media'))
self.audio_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove')) self.audio_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
self.audio_remove_all_button.setText(translate('SongsPlugin.EditSongForm', 'Remove &All')) self.audio_remove_all_button.setText(translate('SongsPlugin.EditSongForm', 'Remove &All'))
self.warning_label.setText( self.not_all_verses_used_warning = \
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.')) translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.')
self.no_verse_order_entered_warning = \
translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> You have not entered a verse order.')
def create_combo_box(parent, name): def create_combo_box(parent, name):

View File

@ -456,6 +456,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.title_edit.setFocus() self.title_edit.setFocus()
# Hide or show the preview button. # Hide or show the preview button.
self.preview_button.setVisible(preview) self.preview_button.setVisible(preview)
# Check if all verse tags are used.
self.on_verse_order_text_changed(self.verse_order_edit.text())
def tag_rows(self): def tag_rows(self):
""" """
@ -683,21 +685,33 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.verse_edit_button.setEnabled(False) self.verse_edit_button.setEnabled(False)
self.verse_delete_button.setEnabled(False) self.verse_delete_button.setEnabled(False)
def on_verse_order_text_changed(self, text): def on_verse_order_text_changed(self, text):
verses = [] """
verse_names = [] Checks if the verse order is complete or missing. Shows a error message according to the state of the verse
order = self._extract_verse_order(text) order.
``text``
The text of the verse order edit (ignored).
"""
# Extract all verses which were used in the order.
verses_in_order = self._extract_verse_order(self.verse_order_edit.text())
# Find the verses which were not used in the order.
verses_not_used = []
for index in range(self.verse_list_widget.rowCount()): for index in range(self.verse_list_widget.rowCount()):
verse = self.verse_list_widget.item(index, 0) verse = self.verse_list_widget.item(index, 0)
verse = verse.data(QtCore.Qt.UserRole) verse = verse.data(QtCore.Qt.UserRole)
if verse not in verse_names: if verse not in verses_in_order:
verses.append(verse)
verse_names.append(u'%s%s' % (VerseType.translated_tag(verse[0]), verse[1:]))
verses_not_used = []
for verse in verses:
if not verse in order:
verses_not_used.append(verse) verses_not_used.append(verse)
self.warning_label.setVisible(len(verses_not_used) > 0) # Set the label text.
label_text = u''
# No verse order was entered.
if not verses_in_order:
label_text = self.no_verse_order_entered_warning
# The verse order does not contain all verses.
elif verses_not_used:
label_text = self.not_all_verses_used_warning
self.warning_label.setText(label_text)
def on_copyright_insert_button_triggered(self): def on_copyright_insert_button_triggered(self):
text = self.copyright_edit.text() text = self.copyright_edit.text()

View File

@ -85,6 +85,7 @@ MODULES = [
'migrate', 'migrate',
'uno', 'uno',
'icu', 'icu',
'bs4',
] ]

View File

@ -45,3 +45,66 @@ class TestEditSongForm(TestCase):
def is_verse_edit_form_executed_test(self): def is_verse_edit_form_executed_test(self):
pass pass
def verse_order_no_warning_test(self):
"""
Test if the verse order warning is not shown
"""
# GIVEN: Mocked methods.
given_verse_order = u'V1 V2'
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
# Mock out the verse.
first_verse = MagicMock()
first_verse.data = MagicMock(return_value=u'V1')
second_verse = MagicMock()
second_verse.data = MagicMock(return_value= u'V2')
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
self.form._extract_verse_order = MagicMock(return_value=given_verse_order.split())
# WHEN: Call the method.
self.form.on_verse_order_text_changed(given_verse_order)
# THEN: No text should be shown.
assert self.form.warning_label.text() == u'', u'There should be no warning.'
def verse_order_incomplete_warning_test(self):
"""
Test if the verse-order-incomple warning is shown
"""
# GIVEN: Mocked methods.
given_verse_order = u'V1'
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
# Mock out the verse.
first_verse = MagicMock()
first_verse.data = MagicMock(return_value=u'V1')
second_verse = MagicMock()
second_verse.data = MagicMock(return_value= u'V2')
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
self.form._extract_verse_order = MagicMock(return_value=[given_verse_order])
# WHEN: Call the method.
self.form.on_verse_order_text_changed(given_verse_order)
# THEN: The verse-order-incomplete text should be shown.
assert self.form.warning_label.text() == self.form.not_all_verses_used_warning, \
u'The verse-order-incomplete warning should be shown.'
def bug_1170435_test(self):
"""
Regression test for bug 1170435 (test if "no verse order" message is shown)
"""
# GIVEN: Mocked methods.
given_verse_order = u''
self.form.verse_list_widget.rowCount = MagicMock(return_value=1)
# Mock out the verse. (We want a verse type to be returned).
mocked_verse = MagicMock()
mocked_verse.data = MagicMock(return_value=u'V1')
self.form.verse_list_widget.item = MagicMock(return_value=mocked_verse)
self.form._extract_verse_order = MagicMock(return_value=[])
self.form.verse_order_edit.text = MagicMock(return_value=given_verse_order)
# WHEN: Call the method.
self.form.on_verse_order_text_changed(given_verse_order)
# THEN: The no-verse-order message should be shown.
assert self.form.warning_label.text() == self.form.no_verse_order_entered_warning, \
u'The no-verse-order message should be shown.'