Merge lp:~oliwee/openlp/newtransition into lp:openlp

Proposed by Oliver Wieland
Status: Needs review
Proposed branch: lp:~oliwee/openlp/newtransition
Merge into: lp:openlp
Diff against target: 525 lines (+208/-34)
7 files modified
openlp/core/lib/htmlbuilder.py (+83/-17)
openlp/core/lib/json/theme.json (+1/-0)
openlp/core/lib/theme.py (+36/-2)
openlp/core/ui/themeform.py (+13/-1)
openlp/core/ui/themewizard.py (+14/-2)
tests/functional/openlp_core_lib/test_htmlbuilder.py (+59/-11)
tests/functional/openlp_core_lib/test_theme.py (+2/-1)
To merge this branch: bzr merge lp:~oliwee/openlp/newtransition
Reviewer Review Type Date Requested Status
OpenLP Core Pending
Review via email: mp+248188@code.launchpad.net

Description of the change

Added a second type of transition, which is more a crossfade than a fade-out-then-fade-in.
Added / adapted tests for htmlbuilder to match the new html code

To post a comment you must log in.
lp:~oliwee/openlp/newtransition updated
2494. By Oliver Wieland

fixed test_theme
adapted to new count of theme variables
added assert for new variable

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Hey Oliver,

Are you still interested in adding this feature to OpenLP? Trunk is open for code, and it would be great to have more transitions.

Revision history for this message
Oliver Wieland (oliwee) wrote :

> Hey Oliver,
>
> Are you still interested in adding this feature to OpenLP? Trunk is open for
> code, and it would be great to have more transitions.
Hey, Raoul,
sorry for delay...
I have a few private problems here at the moment, so I don't have any time for OpenLP. When I have more time, I would like to work on this again.

Be blessed

Unmerged revisions

2494. By Oliver Wieland

fixed test_theme
adapted to new count of theme variables
added assert for new variable

2493. By Oliver Wieland

merged actual trunk

2492. By Oliver Wieland

Fixed problems with vertical align
Fixed tests

2491. By Oliver Wieland

Inserted a combo box into theme manager to select transition type
Tests for htmlbuilder adapted (not ready)

2490. By Oliver Wieland

new Transition between slides

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/lib/htmlbuilder.py'
2--- openlp/core/lib/htmlbuilder.py 2015-01-18 13:39:21 +0000
3+++ openlp/core/lib/htmlbuilder.py 2015-01-31 10:17:29 +0000
4@@ -117,6 +117,9 @@
5 .lyricsmain {
6 -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; text-shadow: #000000 5px 5px;
7 }
8+ .lyricstext {
9+ -webkit-transition: opacity 0.4s ease;
10+ }
11
12 sup {
13 font-size: 0.6em;
14@@ -128,6 +131,7 @@
15 <script>
16 var timer = null;
17 var transition = false;
18+ var transition_type = '0';
19
20 function show_video(state, path, volume, loop, variable_value){
21 // Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
22@@ -342,16 +346,34 @@
23 /*
24 Show the text.
25 */
26- var text = document.getElementById(id);
27- if(text == null) return;
28+ var text1 = document.getElementById(id).childNodes[0];
29+ var text2 = document.getElementById(id).childNodes[1];
30+ if(text1 == null) return;
31+ if(text2 == null) return;
32 if(!transition){
33- text.innerHTML = new_text;
34+ text1.innerHTML = new_text;
35+ text2.innerHTML = new_text;
36 return;
37 }
38- // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
39- text.style.opacity = '0.1';
40- // Fade new text in after the old text has finished fading out.
41- timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
42+ if(transition_type == 0){
43+ // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
44+ text1.style.opacity = '0.1';
45+ // Fade new text in after the old text has finished fading out.
46+ timer = window.setTimeout(function(){_show_text(text1, new_text)}, 400);
47+ }else{
48+ // Toggle visible text element by fading out / in
49+ if (text1.style.opacity == '1'){
50+ text2.innerHTML = new_text;
51+ text1.style.opacity = '0';
52+ text2.style.opacity = '1';
53+ }
54+ else{
55+ text1.innerHTML = new_text;
56+ text2.style.opacity = '0';
57+ text1.style.opacity = '1';
58+ }
59+ timer = window.setTimeout(function(){timer = null;}, 400);
60+ }
61 }
62
63 function _show_text(text, new_text) {
64@@ -381,7 +403,7 @@
65
66 <div id="alert" style="visibility:hidden"></div>
67
68- <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
69+ <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"><div id="lyricstext1" class="lyricstext" style="opacity:1;"></div><div id="lyricstext2" class="lyricstext" style="opacity:0;position:absolute;top:0px;left:0px;"></div></div></div>
70 <div id="footer" class="footer"></div>
71 <div id="black" class="size"></div>
72 </body>
73@@ -448,6 +470,7 @@
74 <script>
75 var timer = null;
76 var transition = %s;
77+ var transition_type = %s;
78 %s
79
80 function show_image(src){
81@@ -474,6 +497,7 @@
82 }
83 document.getElementById('black').style.display = black;
84 document.getElementById('lyricsmain').style.visibility = lyrics;
85+ document.getElementById('lyricsmain2').style.visibility = lyrics;
86 document.getElementById('image').style.visibility = lyrics;
87 document.getElementById('footer').style.visibility = lyrics;
88 }
89@@ -501,23 +525,51 @@
90 new_text = '<span>' + new_text + '</span>';
91 }
92 }
93- text_fade('lyricsmain', new_text);
94+ txt = document.getElementById('lyricsmain2');
95+ if(window.getComputedStyle(txt).textAlign == 'justify'){
96+ if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
97+ new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
98+ function(match) {
99+ return '</span>' + match + '<span>';
100+ });
101+ new_text = '<span>' + new_text + '</span>';
102+ }
103+ }
104+ text_fade('lyricsmain', 'lyricsmain2', new_text);
105 }
106
107- function text_fade(id, new_text){
108+ function text_fade(id, id2, new_text){
109 /*
110 Show the text.
111 */
112- var text = document.getElementById(id);
113- if(text == null) return;
114+ var text1 = document.getElementById(id);
115+ var text2 = document.getElementById(id2);
116+ if(text1 == null) return;
117+ if(text2 == null) return;
118 if(!transition){
119- text.innerHTML = new_text;
120+ text1.innerHTML = new_text;
121+ text2.innerHTML = new_text;
122 return;
123 }
124- // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
125- text.style.opacity = '0.1';
126- // Fade new text in after the old text has finished fading out.
127- timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
128+ if(transition_type == 0){
129+ // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
130+ text1.style.opacity = '0.1';
131+ // Fade new text in after the old text has finished fading out.
132+ timer = window.setTimeout(function(){_show_text(text1, new_text)}, 400);
133+ }else{
134+ // Toggle visible text element by fading out / in
135+ if (text1.style.opacity == '1'){
136+ text2.innerHTML = new_text;
137+ text1.style.opacity = '0';
138+ text2.style.opacity = '1';
139+ }
140+ else{
141+ text1.innerHTML = new_text;
142+ text2.style.opacity = '0';
143+ text1.style.opacity = '1';
144+ }
145+ timer = window.setTimeout(function(){timer = null;}, 400);
146+ }
147 }
148
149 function _show_text(text, new_text) {
150@@ -541,6 +593,7 @@
151 <img id="image" class="size" %s />
152 %s
153 <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
154+<div class="lyricstable"><div id="lyricsmain2" style="opacity:0" class="lyricscell lyricsmain"></div></div>
155 <div id="footer" class="footer"></div>
156 <div id="black" class="size"></div>
157 </body>
158@@ -587,6 +640,7 @@
159 build_footer_css(item, height),
160 build_lyrics_css(item),
161 'true' if theme_data and theme_data.display_slide_transition and is_live else 'false',
162+ get_transition_type(item),
163 js_additions,
164 bgimage_src,
165 image_src,
166@@ -754,3 +808,15 @@
167 lyrics_html = style % (item.footer.x(), bottom, item.footer.width(),
168 theme.font_footer_name, theme.font_footer_size, theme.font_footer_color, whitespace)
169 return lyrics_html
170+
171+def get_transition_type(item):
172+ """
173+ Returns the transition type of the theme
174+
175+ :param item: Service Item to be processed.
176+ """
177+ theme= item.theme_data
178+ if not theme:
179+ return '0'
180+
181+ return theme.display_slide_transition_type
182\ No newline at end of file
183
184=== modified file 'openlp/core/lib/json/theme.json'
185--- openlp/core/lib/json/theme.json 2013-10-18 18:10:47 +0000
186+++ openlp/core/lib/json/theme.json 2015-01-31 10:17:29 +0000
187@@ -11,6 +11,7 @@
188 "display" :{
189 "horizontal_align": 0,
190 "slide_transition": false,
191+ "slide_transition_type": 0,
192 "vertical_align": 0
193 },
194 "font": {
195
196=== modified file 'openlp/core/lib/theme.py'
197--- openlp/core/lib/theme.py 2015-01-18 13:39:21 +0000
198+++ openlp/core/lib/theme.py 2015-01-31 10:17:29 +0000
199@@ -140,6 +140,33 @@
200 Names = ['top', 'middle', 'bottom']
201
202
203+class TransitionType(object):
204+ """
205+ Type enumeration for backgrounds.
206+ """
207+ Fadeout_fadein = 0
208+ Crossfade = 1
209+
210+ @staticmethod
211+ def to_string(transition_type):
212+ """
213+ Return a string representation of a transition type.
214+ """
215+ if transition_type == TransitionType.Fadeout_fadein:
216+ return 'Fade Out - Fade In'
217+ elif transition_type == TransitionType.Crossfade:
218+ return 'Crossfade'
219+
220+ @staticmethod
221+ def from_string(type_string):
222+ """
223+ Return a transition type for the given string.
224+ """
225+ if type_string == 'Fade Out - Fade In':
226+ return TransitionType.Fadeout_fadein
227+ elif type_string == 'Crossfade':
228+ return TransitionType.Crossfade
229+
230 BOOLEAN_LIST = ['bold', 'italics', 'override', 'outline', 'shadow', 'slide_transition']
231
232 INTEGER_LIST = ['size', 'line_adjustment', 'x', 'height', 'y', 'width', 'shadow_size', 'outline_size',
233@@ -318,13 +345,14 @@
234 element.appendChild(value)
235 background.appendChild(element)
236
237- def add_display(self, horizontal, vertical, transition):
238+ def add_display(self, horizontal, vertical, transition, transition_type):
239 """
240 Add a Display options.
241
242 :param horizontal: The horizontal alignment of the text.
243 :param vertical: The vertical alignment of the text.
244 :param transition: Whether the slide transition is active.
245+ _param transition_type: Type of the slide transition.
246 """
247 background = self.theme_xml.createElement('display')
248 self.theme.appendChild(background)
249@@ -343,6 +371,11 @@
250 value = self.theme_xml.createTextNode(str(transition))
251 element.appendChild(value)
252 background.appendChild(element)
253+ # Slide Transition Type
254+ element = self.theme_xml.createElement('slideTransitionType')
255+ value = self.theme_xml.createTextNode(str(transition_type))
256+ element.appendChild(value)
257+ background.appendChild(element)
258
259 def child_element(self, element, tag, value):
260 """
261@@ -555,5 +588,6 @@
262 self.add_display(
263 self.display_horizontal_align,
264 self.display_vertical_align,
265- self.display_slide_transition
266+ self.display_slide_transition,
267+ self.display_slide_transition_type
268 )
269
270=== modified file 'openlp/core/ui/themeform.py'
271--- openlp/core/ui/themeform.py 2015-01-18 13:39:21 +0000
272+++ openlp/core/ui/themeform.py 2015-01-31 10:17:29 +0000
273@@ -28,7 +28,7 @@
274 from PyQt4 import QtCore, QtGui
275
276 from openlp.core.common import Registry, RegistryProperties, UiStrings, translate
277-from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
278+from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, TransitionType
279 from openlp.core.lib.ui import critical_error_message_box
280 from openlp.core.ui import ThemeLayoutForm
281 from openlp.core.utils import get_images_filter, is_not_image_file
282@@ -88,6 +88,7 @@
283 self.main_font_combo_box.activated.connect(self.calculate_lines)
284 self.footer_font_combo_box.activated.connect(self.update_theme)
285 self.footer_size_spin_box.valueChanged.connect(self.update_theme)
286+ self.transitions_combo_box.currentIndexChanged.connect(self.on_transitions_combo_box_current_index_changed)
287
288 def set_defaults(self):
289 """
290@@ -134,6 +135,7 @@
291 self.background_page.registerField('horizontal', self.horizontal_combo_box)
292 self.background_page.registerField('vertical', self.vertical_combo_box)
293 self.background_page.registerField('slide_transition', self.transitions_check_box)
294+ self.background_page.registerField('slide_transition_type', self.transitions_combo_box)
295 self.background_page.registerField('name', self.theme_name_edit)
296
297 def calculate_lines(self):
298@@ -366,6 +368,7 @@
299 self.setField('horizontal', self.theme.display_horizontal_align)
300 self.setField('vertical', self.theme.display_vertical_align)
301 self.setField('slide_transition', self.theme.display_slide_transition)
302+ self.setField('slide_transition_type', self.theme.display_slide_transition_type)
303
304 def set_preview_page_values(self):
305 """
306@@ -464,6 +467,14 @@
307 """
308 self.theme.font_footer_color = color
309
310+ def on_transitions_combo_box_current_index_changed(self, index):
311+ """
312+ Transition style Combo box has changed.
313+ """
314+ # do not allow updates when screen is building for the first time.
315+ if self.update_theme_allowed:
316+ self.theme.transition_type = TransitionType.to_string(index)
317+
318 def update_theme(self):
319 """
320 Update the theme object from the UI for fields not already updated
321@@ -496,6 +507,7 @@
322 self.theme.display_horizontal_align = self.horizontal_combo_box.currentIndex()
323 self.theme.display_vertical_align = self.vertical_combo_box.currentIndex()
324 self.theme.display_slide_transition = self.field('slide_transition')
325+ self.theme.display_slide_transition_type = self.field('slide_transition_type')
326
327 def accept(self):
328 """
329
330=== modified file 'openlp/core/ui/themewizard.py'
331--- openlp/core/ui/themewizard.py 2015-01-18 13:39:21 +0000
332+++ openlp/core/ui/themewizard.py 2015-01-31 10:17:29 +0000
333@@ -26,7 +26,7 @@
334
335 from openlp.core.common import UiStrings, translate, is_macosx
336 from openlp.core.lib import build_icon, ColorButton
337-from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType
338+from openlp.core.lib.theme import HorizontalType, BackgroundType, BackgroundGradientType, TransitionType
339 from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets
340
341
342@@ -257,9 +257,17 @@
343 self.alignment_layout.addRow(self.vertical_label, self.vertical_combo_box)
344 self.transitions_label = QtGui.QLabel(self.alignment_page)
345 self.transitions_label.setObjectName('transitions_label')
346+ self.transitions_properties_layout = QtGui.QHBoxLayout()
347+ self.transitions_properties_layout.setObjectName('transitions_properties_layout')
348 self.transitions_check_box = QtGui.QCheckBox(self.alignment_page)
349 self.transitions_check_box.setObjectName('transitions_check_box')
350- self.alignment_layout.addRow(self.transitions_label, self.transitions_check_box)
351+ self.transitions_properties_layout.addWidget(self.transitions_check_box)
352+ self.transitions_properties_layout.addSpacing(20)
353+ self.transitions_combo_box = QtGui.QComboBox(self.alignment_page)
354+ self.transitions_combo_box.addItems(['', ''])
355+ self.transitions_combo_box.setObjectName('transitions_combo_box')
356+ self.transitions_properties_layout.addWidget(self.transitions_combo_box)
357+ self.alignment_layout.addRow(self.transitions_label, self.transitions_properties_layout)
358 self.alignment_layout.setItem(3, QtGui.QFormLayout.LabelRole, self.spacer)
359 theme_wizard.addPage(self.alignment_page)
360 # Area Position Page
361@@ -458,6 +466,10 @@
362 self.horizontal_combo_box.setItemText(HorizontalType.Center, translate('OpenLP.ThemeWizard', 'Center'))
363 self.horizontal_combo_box.setItemText(HorizontalType.Justify, translate('OpenLP.ThemeWizard', 'Justify'))
364 self.transitions_label.setText(translate('OpenLP.ThemeWizard', 'Transitions:'))
365+ self.transitions_combo_box.setItemText(TransitionType.Fadeout_fadein,
366+ translate('OpenLP.ThemeWizard', 'Fade out - Fade in'))
367+ self.transitions_combo_box.setItemText(TransitionType.Crossfade,
368+ translate('OpenLP.ThemeWizard', 'Crossfade'))
369 self.area_position_page.setTitle(translate('OpenLP.ThemeWizard', 'Output Area Locations'))
370 self.area_position_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Allows you to change and move the'
371 ' Main and Footer areas.'))
372
373=== modified file 'tests/functional/openlp_core_lib/test_htmlbuilder.py'
374--- tests/functional/openlp_core_lib/test_htmlbuilder.py 2014-07-24 21:57:16 +0000
375+++ tests/functional/openlp_core_lib/test_htmlbuilder.py 2015-01-31 10:17:29 +0000
376@@ -8,7 +8,7 @@
377
378 from openlp.core.common import Settings
379 from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \
380- build_lyrics_format_css, build_footer_css
381+ build_lyrics_format_css, build_footer_css, get_transition_type
382 from openlp.core.lib.theme import HorizontalType, VerticalType
383 from tests.functional import MagicMock, patch
384 from tests.helpers.testmixin import TestMixin
385@@ -65,6 +65,7 @@
386 <script>
387 var timer = null;
388 var transition = false;
389+ var transition_type = 0;
390 plugin JS
391
392 function show_image(src){
393@@ -91,6 +92,7 @@
394 }
395 document.getElementById('black').style.display = black;
396 document.getElementById('lyricsmain').style.visibility = lyrics;
397+ document.getElementById('lyricsmain2').style.visibility = lyrics;
398 document.getElementById('image').style.visibility = lyrics;
399 document.getElementById('footer').style.visibility = lyrics;
400 }
401@@ -100,7 +102,7 @@
402 }
403
404 function show_text(new_text){
405- var match = /-webkit-text-fill-color:[^;"]+/gi;
406+ var match = /-webkit-text-fill-color:[^;\"]+/gi;
407 if(timer != null)
408 clearTimeout(timer);
409 /*
410@@ -118,23 +120,51 @@
411 new_text = '<span>' + new_text + '</span>';
412 }
413 }
414- text_fade('lyricsmain', new_text);
415+ txt = document.getElementById('lyricsmain2');
416+ if(window.getComputedStyle(txt).textAlign == 'justify'){
417+ if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
418+ new_text = new_text.replace(/(\s|&nbsp;)+(?![^<]*>)/g,
419+ function(match) {
420+ return '</span>' + match + '<span>';
421+ });
422+ new_text = '<span>' + new_text + '</span>';
423+ }
424+ }
425+ text_fade('lyricsmain', 'lyricsmain2', new_text);
426 }
427
428- function text_fade(id, new_text){
429+ function text_fade(id, id2, new_text){
430 /*
431 Show the text.
432 */
433- var text = document.getElementById(id);
434- if(text == null) return;
435+ var text1 = document.getElementById(id);
436+ var text2 = document.getElementById(id2);
437+ if(text1 == null) return;
438+ if(text2 == null) return;
439 if(!transition){
440- text.innerHTML = new_text;
441+ text1.innerHTML = new_text;
442+ text2.innerHTML = new_text;
443 return;
444 }
445- // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
446- text.style.opacity = '0.1';
447- // Fade new text in after the old text has finished fading out.
448- timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
449+ if(transition_type == 0){
450+ // Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
451+ text1.style.opacity = '0.1';
452+ // Fade new text in after the old text has finished fading out.
453+ timer = window.setTimeout(function(){_show_text(text1, new_text)}, 400);
454+ }else{
455+ // Toggle visible text element by fading out / in
456+ if (text1.style.opacity == '1'){
457+ text2.innerHTML = new_text;
458+ text1.style.opacity = '0';
459+ text2.style.opacity = '1';
460+ }
461+ else{
462+ text1.innerHTML = new_text;
463+ text2.style.opacity = '0';
464+ text1.style.opacity = '1';
465+ }
466+ timer = window.setTimeout(function(){timer = null;}, 400);
467+ }
468 }
469
470 function _show_text(text, new_text) {
471@@ -158,6 +188,7 @@
472 <img id="image" class="size" style="display:none;" />
473 plugin HTML
474 <div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
475+<div class="lyricstable"><div id="lyricsmain2" style="opacity:0" class="lyricscell lyricsmain"></div></div>
476 <div id="footer" class="footer"></div>
477 <div id="black" class="size"></div>
478 </body>
479@@ -230,6 +261,7 @@
480 # Mocked arguments.
481 item = MagicMock()
482 item.bg_image_bytes = None
483+ item.theme_data.display_slide_transition_type = 0
484 screen = MagicMock()
485 is_live = False
486 background = None
487@@ -238,6 +270,7 @@
488 plugin.get_display_javascript.return_value = 'plugin JS'
489 plugin.get_display_html.return_value = 'plugin HTML'
490 plugins = [plugin]
491+ self.maxDiff = None
492
493 # WHEN: Create the html.
494 html = build_html(item, screen, is_live, background, plugins=plugins)
495@@ -358,3 +391,18 @@
496
497 # THEN: Footer should wrap
498 self.assertEqual(FOOTER_CSS_WRAP, css, 'The footer strings should be equal.')
499+
500+ def get_transition_type_test(self):
501+ """
502+ Test the get_transition_type() function
503+ """
504+ # GIVEN: Create a theme.
505+ item = MagicMock()
506+ item.theme_data.display_slide_transition_type = 1
507+
508+ #WHEN: get transition type
509+ transition_type = get_transition_type(item);
510+
511+ #THEN: transition type should be 1
512+ self.assertEqual(1, transition_type, 'The transition type should be 1.');
513+
514\ No newline at end of file
515
516=== modified file 'tests/functional/openlp_core_lib/test_theme.py'
517--- tests/functional/openlp_core_lib/test_theme.py 2015-01-18 13:39:21 +0000
518+++ tests/functional/openlp_core_lib/test_theme.py 2015-01-31 10:17:29 +0000
519@@ -61,4 +61,5 @@
520 self.assertTrue(default_theme.font_footer_name == "Arial",
521 'The theme should have a font_footer_name of Arial')
522 self.assertTrue(default_theme.font_main_bold is False, 'The theme should have a font_main_bold of false')
523- self.assertTrue(len(default_theme.__dict__) == 47, 'The theme should have 47 variables')
524+ self.assertTrue(default_theme.display_slide_transition_type is 0, 'The theme should have a slide_transition_type of 0')
525+ self.assertTrue(len(default_theme.__dict__) == 48, 'The theme should have 48 variables')