Status: | Merged |
---|---|
Merged at revision: | 2771 |
Proposed branch: | lp:~phill-ridout/openlp/pathlib7 |
Merge into: | lp:openlp |
Diff against target: |
1282 lines (+304/-310) 13 files modified
openlp/core/lib/json/theme.json (+1/-1) openlp/core/lib/renderer.py (+4/-3) openlp/core/lib/theme.py (+19/-15) openlp/core/ui/maindisplay.py (+4/-4) openlp/core/ui/themeform.py (+31/-27) openlp/core/ui/thememanager.py (+184/-209) openlp/core/ui/themestab.py (+2/-2) openlp/plugins/images/lib/upgrade.py (+0/-1) tests/functional/openlp_core_lib/test_theme.py (+6/-5) tests/functional/openlp_core_ui/test_maindisplay.py (+9/-9) tests/functional/openlp_core_ui/test_themeform.py (+1/-1) tests/functional/openlp_core_ui/test_thememanager.py (+22/-29) tests/interfaces/openlp_core_ui/test_thememanager.py (+21/-4) |
To merge this branch: | bzr merge lp:~phill-ridout/openlp/pathlib7 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tomas Groth | Approve | ||
Tim Bentley | Approve | ||
Review via email: mp+331364@code.launchpad.net |
Commit message
Description of the change
pathlib changes to the theme code
lp:~phill-ridout/openlp/pathlib7 (revision 2773)
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
To post a comment you must log in.
Revision history for this message
Phill (phill-ridout) wrote : | # |
Revision history for this message
Tim Bentley (trb143) wrote : | # |
Looks good and finishes my changes from XML to json. Thanks
review:
Approve
Revision history for this message
Tomas Groth (tomasgroth) wrote : | # |
Seems legit ;-)
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'openlp/core/lib/json/theme.json' |
2 | --- openlp/core/lib/json/theme.json 2013-10-18 18:10:47 +0000 |
3 | +++ openlp/core/lib/json/theme.json 2017-09-26 17:09:04 +0000 |
4 | @@ -4,7 +4,7 @@ |
5 | "color": "#000000", |
6 | "direction": "vertical", |
7 | "end_color": "#000000", |
8 | - "filename": "", |
9 | + "filename": null, |
10 | "start_color": "#000000", |
11 | "type": "solid" |
12 | }, |
13 | |
14 | === modified file 'openlp/core/lib/renderer.py' |
15 | --- openlp/core/lib/renderer.py 2017-01-25 21:17:27 +0000 |
16 | +++ openlp/core/lib/renderer.py 2017-09-26 17:09:04 +0000 |
17 | @@ -26,6 +26,7 @@ |
18 | from PyQt5 import QtGui, QtCore, QtWebKitWidgets |
19 | |
20 | from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, RegistryMixin, Settings |
21 | +from openlp.core.common.path import path_to_str |
22 | from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \ |
23 | build_lyrics_format_css, build_lyrics_outline_css, build_chords_css |
24 | from openlp.core.common import ThemeLevel |
25 | @@ -118,7 +119,7 @@ |
26 | theme_data, main_rect, footer_rect = self._theme_dimensions[theme_name] |
27 | # if No file do not update cache |
28 | if theme_data.background_filename: |
29 | - self.image_manager.add_image(theme_data.background_filename, |
30 | + self.image_manager.add_image(path_to_str(theme_data.background_filename), |
31 | ImageSource.Theme, QtGui.QColor(theme_data.background_border_color)) |
32 | |
33 | def pre_render(self, override_theme_data=None): |
34 | @@ -207,8 +208,8 @@ |
35 | service_item.raw_footer = FOOTER |
36 | # if No file do not update cache |
37 | if theme_data.background_filename: |
38 | - self.image_manager.add_image( |
39 | - theme_data.background_filename, ImageSource.Theme, QtGui.QColor(theme_data.background_border_color)) |
40 | + self.image_manager.add_image(path_to_str(theme_data.background_filename), |
41 | + ImageSource.Theme, QtGui.QColor(theme_data.background_border_color)) |
42 | theme_data, main, footer = self.pre_render(theme_data) |
43 | service_item.theme_data = theme_data |
44 | service_item.main = main |
45 | |
46 | === modified file 'openlp/core/lib/theme.py' |
47 | --- openlp/core/lib/theme.py 2017-08-12 17:45:56 +0000 |
48 | +++ openlp/core/lib/theme.py 2017-09-26 17:09:04 +0000 |
49 | @@ -22,13 +22,13 @@ |
50 | """ |
51 | Provide the theme XML and handling functions for OpenLP v2 themes. |
52 | """ |
53 | -import os |
54 | +import json |
55 | import logging |
56 | -import json |
57 | |
58 | from lxml import etree, objectify |
59 | from openlp.core.common import AppLocation, de_hump |
60 | - |
61 | +from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder |
62 | +from openlp.core.common.path import Path, str_to_path |
63 | from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string |
64 | |
65 | log = logging.getLogger(__name__) |
66 | @@ -160,9 +160,8 @@ |
67 | # basic theme object with defaults |
68 | json_path = AppLocation.get_directory(AppLocation.AppDir) / 'core' / 'lib' / 'json' / 'theme.json' |
69 | jsn = get_text_file_string(json_path) |
70 | - jsn = json.loads(jsn) |
71 | - self.expand_json(jsn) |
72 | - self.background_filename = '' |
73 | + self.load_theme(jsn) |
74 | + self.background_filename = None |
75 | |
76 | def expand_json(self, var, prev=None): |
77 | """ |
78 | @@ -174,8 +173,6 @@ |
79 | for key, value in var.items(): |
80 | if prev: |
81 | key = prev + "_" + key |
82 | - else: |
83 | - key = key |
84 | if isinstance(value, dict): |
85 | self.expand_json(value, key) |
86 | else: |
87 | @@ -185,13 +182,13 @@ |
88 | """ |
89 | Add the path name to the image name so the background can be rendered. |
90 | |
91 | - :param path: The path name to be added. |
92 | + :param openlp.core.common.path.Path path: The path name to be added. |
93 | + :rtype: None |
94 | """ |
95 | if self.background_type == 'image' or self.background_type == 'video': |
96 | if self.background_filename and path: |
97 | self.theme_name = self.theme_name.strip() |
98 | - self.background_filename = self.background_filename.strip() |
99 | - self.background_filename = os.path.join(path, self.theme_name, self.background_filename) |
100 | + self.background_filename = path / self.theme_name / self.background_filename |
101 | |
102 | def set_default_header_footer(self): |
103 | """ |
104 | @@ -206,16 +203,21 @@ |
105 | self.font_footer_y = current_screen['size'].height() * 9 / 10 |
106 | self.font_footer_height = current_screen['size'].height() / 10 |
107 | |
108 | - def load_theme(self, theme): |
109 | + def load_theme(self, theme, theme_path=None): |
110 | """ |
111 | Convert the JSON file and expand it. |
112 | |
113 | :param theme: the theme string |
114 | + :param openlp.core.common.path.Path theme_path: The path to the theme |
115 | + :rtype: None |
116 | """ |
117 | - jsn = json.loads(theme) |
118 | + if theme_path: |
119 | + jsn = json.loads(theme, cls=OpenLPJsonDecoder, base_path=theme_path) |
120 | + else: |
121 | + jsn = json.loads(theme, cls=OpenLPJsonDecoder) |
122 | self.expand_json(jsn) |
123 | |
124 | - def export_theme(self): |
125 | + def export_theme(self, theme_path=None): |
126 | """ |
127 | Loop through the fields and build a dictionary of them |
128 | |
129 | @@ -223,7 +225,9 @@ |
130 | theme_data = {} |
131 | for attr, value in self.__dict__.items(): |
132 | theme_data["{attr}".format(attr=attr)] = value |
133 | - return json.dumps(theme_data) |
134 | + if theme_path: |
135 | + return json.dumps(theme_data, cls=OpenLPJsonEncoder, base_path=theme_path) |
136 | + return json.dumps(theme_data, cls=OpenLPJsonEncoder) |
137 | |
138 | def parse(self, xml): |
139 | """ |
140 | |
141 | === modified file 'openlp/core/ui/maindisplay.py' |
142 | --- openlp/core/ui/maindisplay.py 2017-09-05 04:28:50 +0000 |
143 | +++ openlp/core/ui/maindisplay.py 2017-09-26 17:09:04 +0000 |
144 | @@ -346,7 +346,7 @@ |
145 | if not hasattr(self, 'service_item'): |
146 | return False |
147 | self.override['image'] = path |
148 | - self.override['theme'] = self.service_item.theme_data.background_filename |
149 | + self.override['theme'] = path_to_str(self.service_item.theme_data.background_filename) |
150 | self.image(path) |
151 | # Update the preview frame. |
152 | if self.is_live: |
153 | @@ -454,7 +454,7 @@ |
154 | Registry().execute('video_background_replaced') |
155 | self.override = {} |
156 | # We have a different theme. |
157 | - elif self.override['theme'] != service_item.theme_data.background_filename: |
158 | + elif self.override['theme'] != path_to_str(service_item.theme_data.background_filename): |
159 | Registry().execute('live_theme_changed') |
160 | self.override = {} |
161 | else: |
162 | @@ -466,7 +466,7 @@ |
163 | if self.service_item.theme_data.background_type == 'image': |
164 | if self.service_item.theme_data.background_filename: |
165 | self.service_item.bg_image_bytes = self.image_manager.get_image_bytes( |
166 | - self.service_item.theme_data.background_filename, ImageSource.Theme) |
167 | + path_to_str(self.service_item.theme_data.background_filename), ImageSource.Theme) |
168 | if image_path: |
169 | image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin) |
170 | created_html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes, |
171 | @@ -488,7 +488,7 @@ |
172 | path = os.path.join(str(AppLocation.get_section_data_path('themes')), |
173 | self.service_item.theme_data.theme_name) |
174 | service_item.add_from_command(path, |
175 | - self.service_item.theme_data.background_filename, |
176 | + path_to_str(self.service_item.theme_data.background_filename), |
177 | ':/media/slidecontroller_multimedia.png') |
178 | self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True) |
179 | self._hide_mouse() |
180 | |
181 | === modified file 'openlp/core/ui/themeform.py' |
182 | --- openlp/core/ui/themeform.py 2017-08-25 20:03:25 +0000 |
183 | +++ openlp/core/ui/themeform.py 2017-09-26 17:09:04 +0000 |
184 | @@ -28,7 +28,6 @@ |
185 | from PyQt5 import QtCore, QtGui, QtWidgets |
186 | |
187 | from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file |
188 | -from openlp.core.common.path import Path, path_to_str, str_to_path |
189 | from openlp.core.lib.theme import BackgroundType, BackgroundGradientType |
190 | from openlp.core.lib.ui import critical_error_message_box |
191 | from openlp.core.ui import ThemeLayoutForm |
192 | @@ -61,7 +60,7 @@ |
193 | self.setupUi(self) |
194 | self.registerFields() |
195 | self.update_theme_allowed = True |
196 | - self.temp_background_filename = '' |
197 | + self.temp_background_filename = None |
198 | self.theme_layout_form = ThemeLayoutForm(self) |
199 | self.background_combo_box.currentIndexChanged.connect(self.on_background_combo_box_current_index_changed) |
200 | self.gradient_combo_box.currentIndexChanged.connect(self.on_gradient_combo_box_current_index_changed) |
201 | @@ -188,8 +187,7 @@ |
202 | """ |
203 | background_image = BackgroundType.to_string(BackgroundType.Image) |
204 | if self.page(self.currentId()) == self.background_page and \ |
205 | - self.theme.background_type == background_image and \ |
206 | - is_not_image_file(Path(self.theme.background_filename)): |
207 | + self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename): |
208 | QtWidgets.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'), |
209 | translate('OpenLP.ThemeWizard', 'You have not selected a ' |
210 | 'background image. Please select one before continuing.')) |
211 | @@ -273,7 +271,7 @@ |
212 | Run the wizard. |
213 | """ |
214 | log.debug('Editing theme {name}'.format(name=self.theme.theme_name)) |
215 | - self.temp_background_filename = '' |
216 | + self.temp_background_filename = None |
217 | self.update_theme_allowed = False |
218 | self.set_defaults() |
219 | self.update_theme_allowed = True |
220 | @@ -318,11 +316,11 @@ |
221 | self.setField('background_type', 1) |
222 | elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image): |
223 | self.image_color_button.color = self.theme.background_border_color |
224 | - self.image_path_edit.path = str_to_path(self.theme.background_filename) |
225 | + self.image_path_edit.path = self.theme.background_filename |
226 | self.setField('background_type', 2) |
227 | elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): |
228 | self.video_color_button.color = self.theme.background_border_color |
229 | - self.video_path_edit.path = str_to_path(self.theme.background_filename) |
230 | + self.video_path_edit.path = self.theme.background_filename |
231 | self.setField('background_type', 4) |
232 | elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent): |
233 | self.setField('background_type', 3) |
234 | @@ -402,14 +400,14 @@ |
235 | self.theme.background_type = BackgroundType.to_string(index) |
236 | if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \ |
237 | self.theme.background_type != BackgroundType.to_string(BackgroundType.Video) and \ |
238 | - self.temp_background_filename == '': |
239 | + self.temp_background_filename is None: |
240 | self.temp_background_filename = self.theme.background_filename |
241 | - self.theme.background_filename = '' |
242 | + self.theme.background_filename = None |
243 | if (self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or |
244 | self.theme.background_type != BackgroundType.to_string(BackgroundType.Video)) and \ |
245 | - self.temp_background_filename != '': |
246 | + self.temp_background_filename is not None: |
247 | self.theme.background_filename = self.temp_background_filename |
248 | - self.temp_background_filename = '' |
249 | + self.temp_background_filename = None |
250 | self.set_background_page_values() |
251 | |
252 | def on_gradient_combo_box_current_index_changed(self, index): |
253 | @@ -450,18 +448,24 @@ |
254 | """ |
255 | self.theme.background_end_color = color |
256 | |
257 | - def on_image_path_edit_path_changed(self, file_path): |
258 | - """ |
259 | - Background Image button pushed. |
260 | - """ |
261 | - self.theme.background_filename = path_to_str(file_path) |
262 | + def on_image_path_edit_path_changed(self, new_path): |
263 | + """ |
264 | + Handle the `pathEditChanged` signal from image_path_edit |
265 | + |
266 | + :param openlp.core.common.path.Path new_path: Path to the new image |
267 | + :rtype: None |
268 | + """ |
269 | + self.theme.background_filename = new_path |
270 | self.set_background_page_values() |
271 | |
272 | - def on_video_path_edit_path_changed(self, file_path): |
273 | - """ |
274 | - Background video button pushed. |
275 | - """ |
276 | - self.theme.background_filename = path_to_str(file_path) |
277 | + def on_video_path_edit_path_changed(self, new_path): |
278 | + """ |
279 | + Handle the `pathEditChanged` signal from video_path_edit |
280 | + |
281 | + :param openlp.core.common.path.Path new_path: Path to the new video |
282 | + :rtype: None |
283 | + """ |
284 | + self.theme.background_filename = new_path |
285 | self.set_background_page_values() |
286 | |
287 | def on_main_color_changed(self, color): |
288 | @@ -537,14 +541,14 @@ |
289 | translate('OpenLP.ThemeWizard', 'Theme Name Invalid'), |
290 | translate('OpenLP.ThemeWizard', 'Invalid theme name. Please enter one.')) |
291 | return |
292 | - save_from = None |
293 | - save_to = None |
294 | + source_path = None |
295 | + destination_path = None |
296 | if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \ |
297 | self.theme.background_type == BackgroundType.to_string(BackgroundType.Video): |
298 | - filename = os.path.split(str(self.theme.background_filename))[1] |
299 | - save_to = os.path.join(self.path, self.theme.theme_name, filename) |
300 | - save_from = self.theme.background_filename |
301 | + file_name = self.theme.background_filename.name |
302 | + destination_path = self.path / self.theme.theme_name / file_name |
303 | + source_path = self.theme.background_filename |
304 | if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name): |
305 | return |
306 | - self.theme_manager.save_theme(self.theme, save_from, save_to) |
307 | + self.theme_manager.save_theme(self.theme, source_path, destination_path) |
308 | return QtWidgets.QDialog.accept(self) |
309 | |
310 | === modified file 'openlp/core/ui/thememanager.py' |
311 | --- openlp/core/ui/thememanager.py 2017-09-17 19:43:15 +0000 |
312 | +++ openlp/core/ui/thememanager.py 2017-09-26 17:09:04 +0000 |
313 | @@ -24,14 +24,14 @@ |
314 | """ |
315 | import os |
316 | import zipfile |
317 | -import shutil |
318 | - |
319 | from xml.etree.ElementTree import ElementTree, XML |
320 | + |
321 | from PyQt5 import QtCore, QtGui, QtWidgets |
322 | |
323 | from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \ |
324 | - UiStrings, check_directory_exists, translate, is_win, get_filesystem_encoding, delete_file |
325 | -from openlp.core.common.path import Path, path_to_str, str_to_path |
326 | + UiStrings, check_directory_exists, translate, delete_file |
327 | +from openlp.core.common.languagemanager import get_locale_key |
328 | +from openlp.core.common.path import Path, copyfile, path_to_str, rmtree |
329 | from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \ |
330 | check_item_selected, create_thumb, validate_thumb |
331 | from openlp.core.lib.theme import Theme, BackgroundType |
332 | @@ -39,7 +39,6 @@ |
333 | from openlp.core.ui import FileRenameForm, ThemeForm |
334 | from openlp.core.ui.lib import OpenLPToolbar |
335 | from openlp.core.ui.lib.filedialog import FileDialog |
336 | -from openlp.core.common.languagemanager import get_locale_key |
337 | |
338 | |
339 | class Ui_ThemeManager(object): |
340 | @@ -135,7 +134,7 @@ |
341 | self.settings_section = 'themes' |
342 | # Variables |
343 | self.theme_list = [] |
344 | - self.old_background_image = None |
345 | + self.old_background_image_path = None |
346 | |
347 | def bootstrap_initialise(self): |
348 | """ |
349 | @@ -145,25 +144,41 @@ |
350 | self.global_theme = Settings().value(self.settings_section + '/global theme') |
351 | self.build_theme_path() |
352 | self.load_first_time_themes() |
353 | + self.upgrade_themes() |
354 | |
355 | def bootstrap_post_set_up(self): |
356 | """ |
357 | process the bootstrap post setup request |
358 | """ |
359 | self.theme_form = ThemeForm(self) |
360 | - self.theme_form.path = self.path |
361 | + self.theme_form.path = self.theme_path |
362 | self.file_rename_form = FileRenameForm() |
363 | Registry().register_function('theme_update_global', self.change_global_from_tab) |
364 | self.load_themes() |
365 | |
366 | + def upgrade_themes(self): |
367 | + """ |
368 | + Upgrade the xml files to json. |
369 | + |
370 | + :rtype: None |
371 | + """ |
372 | + xml_file_paths = AppLocation.get_section_data_path('themes').glob('*/*.xml') |
373 | + for xml_file_path in xml_file_paths: |
374 | + theme_data = get_text_file_string(xml_file_path) |
375 | + theme = self._create_theme_from_xml(theme_data, self.theme_path) |
376 | + self._write_theme(theme) |
377 | + xml_file_path.unlink() |
378 | + |
379 | def build_theme_path(self): |
380 | """ |
381 | Set up the theme path variables |
382 | + |
383 | + :rtype: None |
384 | """ |
385 | - self.path = str(AppLocation.get_section_data_path(self.settings_section)) |
386 | - check_directory_exists(Path(self.path)) |
387 | - self.thumb_path = os.path.join(self.path, 'thumbnails') |
388 | - check_directory_exists(Path(self.thumb_path)) |
389 | + self.theme_path = AppLocation.get_section_data_path(self.settings_section) |
390 | + check_directory_exists(self.theme_path) |
391 | + self.thumb_path = self.theme_path / 'thumbnails' |
392 | + check_directory_exists(self.thumb_path) |
393 | |
394 | def check_list_state(self, item, field=None): |
395 | """ |
396 | @@ -298,17 +313,18 @@ |
397 | """ |
398 | Takes a theme and makes a new copy of it as well as saving it. |
399 | |
400 | - :param theme_data: The theme to be used |
401 | - :param new_theme_name: The new theme name to save the data to |
402 | + :param Theme theme_data: The theme to be used |
403 | + :param str new_theme_name: The new theme name of the theme |
404 | + :rtype: None |
405 | """ |
406 | - save_to = None |
407 | - save_from = None |
408 | + destination_path = None |
409 | + source_path = None |
410 | if theme_data.background_type == 'image' or theme_data.background_type == 'video': |
411 | - save_to = os.path.join(self.path, new_theme_name, os.path.split(str(theme_data.background_filename))[1]) |
412 | - save_from = theme_data.background_filename |
413 | + destination_path = self.theme_path / new_theme_name / theme_data.background_filename.name |
414 | + source_path = theme_data.background_filename |
415 | theme_data.theme_name = new_theme_name |
416 | - theme_data.extend_image_filename(self.path) |
417 | - self.save_theme(theme_data, save_from, save_to) |
418 | + theme_data.extend_image_filename(self.theme_path) |
419 | + self.save_theme(theme_data, source_path, destination_path) |
420 | self.load_themes() |
421 | |
422 | def on_edit_theme(self, field=None): |
423 | @@ -322,10 +338,10 @@ |
424 | item = self.theme_list_widget.currentItem() |
425 | theme = self.get_theme_data(item.data(QtCore.Qt.UserRole)) |
426 | if theme.background_type == 'image' or theme.background_type == 'video': |
427 | - self.old_background_image = theme.background_filename |
428 | + self.old_background_image_path = theme.background_filename |
429 | self.theme_form.theme = theme |
430 | self.theme_form.exec(True) |
431 | - self.old_background_image = None |
432 | + self.old_background_image_path = None |
433 | self.renderer.update_theme(theme.theme_name) |
434 | self.load_themes() |
435 | |
436 | @@ -355,77 +371,76 @@ |
437 | """ |
438 | self.theme_list.remove(theme) |
439 | thumb = '{name}.png'.format(name=theme) |
440 | - delete_file(Path(self.path, thumb)) |
441 | - delete_file(Path(self.thumb_path, thumb)) |
442 | + delete_file(self.theme_path / thumb) |
443 | + delete_file(self.thumb_path / thumb) |
444 | try: |
445 | - # Windows is always unicode, so no need to encode filenames |
446 | - if is_win(): |
447 | - shutil.rmtree(os.path.join(self.path, theme)) |
448 | - else: |
449 | - encoding = get_filesystem_encoding() |
450 | - shutil.rmtree(os.path.join(self.path, theme).encode(encoding)) |
451 | - except OSError as os_error: |
452 | - shutil.Error = os_error |
453 | + rmtree(self.theme_path / theme) |
454 | + except OSError: |
455 | self.log_exception('Error deleting theme {name}'.format(name=theme)) |
456 | |
457 | - def on_export_theme(self, field=None): |
458 | + def on_export_theme(self, checked=None): |
459 | """ |
460 | - Export the theme in a zip file |
461 | - :param field: |
462 | + Export the theme to a zip file |
463 | + |
464 | + :param bool checked: Sent by the QAction.triggered signal. It's not used in this method. |
465 | + :rtype: None |
466 | """ |
467 | item = self.theme_list_widget.currentItem() |
468 | if item is None: |
469 | critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.')) |
470 | return |
471 | - theme = item.data(QtCore.Qt.UserRole) |
472 | + theme_name = item.data(QtCore.Qt.UserRole) |
473 | export_path, filter_used = \ |
474 | FileDialog.getSaveFileName(self.main_window, |
475 | - translate('OpenLP.ThemeManager', 'Save Theme - ({name})'). |
476 | - format(name=theme), |
477 | + translate('OpenLP.ThemeManager', |
478 | + 'Save Theme - ({name})').format(name=theme_name), |
479 | Settings().value(self.settings_section + '/last directory export'), |
480 | + translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'), |
481 | translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)')) |
482 | self.application.set_busy_cursor() |
483 | if export_path: |
484 | Settings().setValue(self.settings_section + '/last directory export', export_path.parent) |
485 | - if self._export_theme(str(export_path), theme): |
486 | + if self._export_theme(export_path.with_suffix('.otz'), theme_name): |
487 | QtWidgets.QMessageBox.information(self, |
488 | translate('OpenLP.ThemeManager', 'Theme Exported'), |
489 | translate('OpenLP.ThemeManager', |
490 | 'Your theme has been successfully exported.')) |
491 | self.application.set_normal_cursor() |
492 | |
493 | - def _export_theme(self, theme_path, theme): |
494 | + def _export_theme(self, theme_path, theme_name): |
495 | """ |
496 | Create the zipfile with the theme contents. |
497 | - :param theme_path: Location where the zip file will be placed |
498 | - :param theme: The name of the theme to be exported |
499 | + |
500 | + :param openlp.core.common.path.Path theme_path: Location where the zip file will be placed |
501 | + :param str theme_name: The name of the theme to be exported |
502 | + :return: The success of creating the zip file |
503 | + :rtype: bool |
504 | """ |
505 | - theme_zip = None |
506 | try: |
507 | - theme_zip = zipfile.ZipFile(theme_path, 'w') |
508 | - source = os.path.join(self.path, theme) |
509 | - for files in os.walk(source): |
510 | - for name in files[2]: |
511 | - theme_zip.write(os.path.join(source, name), os.path.join(theme, name)) |
512 | - theme_zip.close() |
513 | + with zipfile.ZipFile(str(theme_path), 'w') as theme_zip: |
514 | + source_path = self.theme_path / theme_name |
515 | + for file_path in source_path.iterdir(): |
516 | + theme_zip.write(str(file_path), os.path.join(theme_name, file_path.name)) |
517 | return True |
518 | except OSError as ose: |
519 | self.log_exception('Export Theme Failed') |
520 | critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'), |
521 | - translate('OpenLP.ThemeManager', 'The theme export failed because this error ' |
522 | - 'occurred: {err}').format(err=ose.strerror)) |
523 | - if theme_zip: |
524 | - theme_zip.close() |
525 | - shutil.rmtree(theme_path, True) |
526 | + translate('OpenLP.ThemeManager', |
527 | + 'The theme_name export failed because this error occurred: {err}') |
528 | + .format(err=ose.strerror)) |
529 | + if theme_path.exists(): |
530 | + rmtree(theme_path, True) |
531 | return False |
532 | |
533 | - def on_import_theme(self, field=None): |
534 | + def on_import_theme(self, checked=None): |
535 | """ |
536 | Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from |
537 | those files. This process will only load version 2 themes. |
538 | - :param field: |
539 | + |
540 | + :param bool checked: Sent by the QAction.triggered signal. It's not used in this method. |
541 | + :rtype: None |
542 | """ |
543 | - file_paths, selected_filter = FileDialog.getOpenFileNames( |
544 | + file_paths, filter_used = FileDialog.getOpenFileNames( |
545 | self, |
546 | translate('OpenLP.ThemeManager', 'Select Theme Import File'), |
547 | Settings().value(self.settings_section + '/last directory import'), |
548 | @@ -435,8 +450,8 @@ |
549 | return |
550 | self.application.set_busy_cursor() |
551 | for file_path in file_paths: |
552 | - self.unzip_theme(path_to_str(file_path), self.path) |
553 | - Settings().setValue(self.settings_section + '/last directory import', file_path) |
554 | + self.unzip_theme(file_path, self.theme_path) |
555 | + Settings().setValue(self.settings_section + '/last directory import', file_path.parent) |
556 | self.load_themes() |
557 | self.application.set_normal_cursor() |
558 | |
559 | @@ -445,17 +460,17 @@ |
560 | Imports any themes on start up and makes sure there is at least one theme |
561 | """ |
562 | self.application.set_busy_cursor() |
563 | - files = AppLocation.get_files(self.settings_section, '.otz') |
564 | - for theme_file in files: |
565 | - theme_file = os.path.join(self.path, str(theme_file)) |
566 | - self.unzip_theme(theme_file, self.path) |
567 | - delete_file(Path(theme_file)) |
568 | - files = AppLocation.get_files(self.settings_section, '.png') |
569 | + theme_paths = AppLocation.get_files(self.settings_section, '.otz') |
570 | + for theme_path in theme_paths: |
571 | + theme_path = self.theme_path / theme_path |
572 | + self.unzip_theme(theme_path, self.theme_path) |
573 | + delete_file(theme_path) |
574 | + theme_paths = AppLocation.get_files(self.settings_section, '.png') |
575 | # No themes have been found so create one |
576 | - if not files: |
577 | + if not theme_paths: |
578 | theme = Theme() |
579 | theme.theme_name = UiStrings().Default |
580 | - self._write_theme(theme, None, None) |
581 | + self._write_theme(theme) |
582 | Settings().setValue(self.settings_section + '/global theme', theme.theme_name) |
583 | self.application.set_normal_cursor() |
584 | |
585 | @@ -471,22 +486,21 @@ |
586 | # Sort the themes by its name considering language specific |
587 | files.sort(key=lambda file_name: get_locale_key(str(file_name))) |
588 | # now process the file list of png files |
589 | - for name in files: |
590 | - name = str(name) |
591 | + for file in files: |
592 | # check to see file is in theme root directory |
593 | - theme = os.path.join(self.path, name) |
594 | - if os.path.exists(theme): |
595 | - text_name = os.path.splitext(name)[0] |
596 | + theme_path = self.theme_path / file |
597 | + if theme_path.exists(): |
598 | + text_name = theme_path.stem |
599 | if text_name == self.global_theme: |
600 | name = translate('OpenLP.ThemeManager', '{name} (default)').format(name=text_name) |
601 | else: |
602 | name = text_name |
603 | - thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=text_name)) |
604 | + thumb = self.thumb_path / '{name}.png'.format(name=text_name) |
605 | item_name = QtWidgets.QListWidgetItem(name) |
606 | - if validate_thumb(Path(theme), Path(thumb)): |
607 | + if validate_thumb(theme_path, thumb): |
608 | icon = build_icon(thumb) |
609 | else: |
610 | - icon = create_thumb(theme, thumb) |
611 | + icon = create_thumb(str(theme_path), str(thumb)) |
612 | item_name.setIcon(icon) |
613 | item_name.setData(QtCore.Qt.UserRole, text_name) |
614 | self.theme_list_widget.addItem(item_name) |
615 | @@ -507,27 +521,19 @@ |
616 | |
617 | def get_theme_data(self, theme_name): |
618 | """ |
619 | - Returns a theme object from an XML or JSON file |
620 | + Returns a theme object from a JSON file |
621 | |
622 | - :param theme_name: Name of the theme to load from file |
623 | - :return: The theme object. |
624 | + :param str theme_name: Name of the theme to load from file |
625 | + :return: The theme object. |
626 | + :rtype: Theme |
627 | """ |
628 | - self.log_debug('get theme data for theme {name}'.format(name=theme_name)) |
629 | - theme_file_path = Path(self.path, str(theme_name), '{file_name}.json'.format(file_name=theme_name)) |
630 | + theme_name = str(theme_name) |
631 | + theme_file_path = self.theme_path / theme_name / '{file_name}.json'.format(file_name=theme_name) |
632 | theme_data = get_text_file_string(theme_file_path) |
633 | - jsn = True |
634 | - if not theme_data: |
635 | - theme_file_path = theme_file_path.with_suffix('.xml') |
636 | - theme_data = get_text_file_string(theme_file_path) |
637 | - jsn = False |
638 | if not theme_data: |
639 | self.log_debug('No theme data - using default theme') |
640 | return Theme() |
641 | - else: |
642 | - if jsn: |
643 | - return self._create_theme_from_json(theme_data, self.path) |
644 | - else: |
645 | - return self._create_theme_from_xml(theme_data, self.path) |
646 | + return self._create_theme_from_json(theme_data, self.theme_path) |
647 | |
648 | def over_write_message_box(self, theme_name): |
649 | """ |
650 | @@ -543,172 +549,148 @@ |
651 | defaultButton=QtWidgets.QMessageBox.No) |
652 | return ret == QtWidgets.QMessageBox.Yes |
653 | |
654 | - def unzip_theme(self, file_name, directory): |
655 | + def unzip_theme(self, file_path, directory_path): |
656 | """ |
657 | Unzip the theme, remove the preview file if stored. Generate a new preview file. Check the XML theme version |
658 | and upgrade if necessary. |
659 | - :param file_name: |
660 | - :param directory: |
661 | + :param openlp.core.common.path.Path file_path: |
662 | + :param openlp.core.common.path.Path directory_path: |
663 | """ |
664 | - self.log_debug('Unzipping theme {name}'.format(name=file_name)) |
665 | - theme_zip = None |
666 | - out_file = None |
667 | + self.log_debug('Unzipping theme {name}'.format(name=file_path)) |
668 | file_xml = None |
669 | abort_import = True |
670 | json_theme = False |
671 | theme_name = "" |
672 | try: |
673 | - theme_zip = zipfile.ZipFile(file_name) |
674 | - json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json'] |
675 | - if len(json_file) != 1: |
676 | - # TODO: remove XML handling at some point but would need a auto conversion to run first. |
677 | - xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml'] |
678 | - if len(xml_file) != 1: |
679 | - self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file))) |
680 | - raise ValidationError |
681 | - xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot() |
682 | - theme_version = xml_tree.get('version', default=None) |
683 | - if not theme_version or float(theme_version) < 2.0: |
684 | - self.log_error('Theme version is less than 2.0') |
685 | - raise ValidationError |
686 | - theme_name = xml_tree.find('name').text.strip() |
687 | - else: |
688 | - new_theme = Theme() |
689 | - new_theme.load_theme(theme_zip.read(json_file[0]).decode("utf-8")) |
690 | - theme_name = new_theme.theme_name |
691 | - json_theme = True |
692 | - theme_folder = os.path.join(directory, theme_name) |
693 | - theme_exists = os.path.exists(theme_folder) |
694 | - if theme_exists and not self.over_write_message_box(theme_name): |
695 | - abort_import = True |
696 | - return |
697 | - else: |
698 | - abort_import = False |
699 | - for name in theme_zip.namelist(): |
700 | - out_name = name.replace('/', os.path.sep) |
701 | - split_name = out_name.split(os.path.sep) |
702 | - if split_name[-1] == '' or len(split_name) == 1: |
703 | - # is directory or preview file |
704 | - continue |
705 | - full_name = os.path.join(directory, out_name) |
706 | - check_directory_exists(Path(os.path.dirname(full_name))) |
707 | - if os.path.splitext(name)[1].lower() == '.xml' or os.path.splitext(name)[1].lower() == '.json': |
708 | - file_xml = str(theme_zip.read(name), 'utf-8') |
709 | - out_file = open(full_name, 'w', encoding='utf-8') |
710 | - out_file.write(file_xml) |
711 | - else: |
712 | - out_file = open(full_name, 'wb') |
713 | - out_file.write(theme_zip.read(name)) |
714 | - out_file.close() |
715 | + with zipfile.ZipFile(str(file_path)) as theme_zip: |
716 | + json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json'] |
717 | + if len(json_file) != 1: |
718 | + # TODO: remove XML handling after the 2.6 release. |
719 | + xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml'] |
720 | + if len(xml_file) != 1: |
721 | + self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file))) |
722 | + raise ValidationError |
723 | + xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot() |
724 | + theme_version = xml_tree.get('version', default=None) |
725 | + if not theme_version or float(theme_version) < 2.0: |
726 | + self.log_error('Theme version is less than 2.0') |
727 | + raise ValidationError |
728 | + theme_name = xml_tree.find('name').text.strip() |
729 | + else: |
730 | + new_theme = Theme() |
731 | + new_theme.load_theme(theme_zip.read(json_file[0]).decode("utf-8")) |
732 | + theme_name = new_theme.theme_name |
733 | + json_theme = True |
734 | + theme_folder = directory_path / theme_name |
735 | + if theme_folder.exists() and not self.over_write_message_box(theme_name): |
736 | + abort_import = True |
737 | + return |
738 | + else: |
739 | + abort_import = False |
740 | + for zipped_file in theme_zip.namelist(): |
741 | + zipped_file_rel_path = Path(zipped_file) |
742 | + split_name = zipped_file_rel_path.parts |
743 | + if split_name[-1] == '' or len(split_name) == 1: |
744 | + # is directory or preview file |
745 | + continue |
746 | + full_name = directory_path / zipped_file_rel_path |
747 | + check_directory_exists(full_name.parent) |
748 | + if zipped_file_rel_path.suffix.lower() == '.xml' or zipped_file_rel_path.suffix.lower() == '.json': |
749 | + file_xml = str(theme_zip.read(zipped_file), 'utf-8') |
750 | + with full_name.open('w', encoding='utf-8') as out_file: |
751 | + out_file.write(file_xml) |
752 | + else: |
753 | + with full_name.open('wb') as out_file: |
754 | + out_file.write(theme_zip.read(zipped_file)) |
755 | except (IOError, zipfile.BadZipfile): |
756 | - self.log_exception('Importing theme from zip failed {name}'.format(name=file_name)) |
757 | + self.log_exception('Importing theme from zip failed {name}'.format(name=file_path)) |
758 | raise ValidationError |
759 | except ValidationError: |
760 | critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'), |
761 | translate('OpenLP.ThemeManager', 'File is not a valid theme.')) |
762 | finally: |
763 | - # Close the files, to be able to continue creating the theme. |
764 | - if theme_zip: |
765 | - theme_zip.close() |
766 | - if out_file: |
767 | - out_file.close() |
768 | if not abort_import: |
769 | # As all files are closed, we can create the Theme. |
770 | if file_xml: |
771 | if json_theme: |
772 | - theme = self._create_theme_from_json(file_xml, self.path) |
773 | + theme = self._create_theme_from_json(file_xml, self.theme_path) |
774 | else: |
775 | - theme = self._create_theme_from_xml(file_xml, self.path) |
776 | + theme = self._create_theme_from_xml(file_xml, self.theme_path) |
777 | self.generate_and_save_image(theme_name, theme) |
778 | - # Only show the error message, when IOError was not raised (in |
779 | - # this case the error message has already been shown). |
780 | - elif theme_zip is not None: |
781 | - critical_error_message_box( |
782 | - translate('OpenLP.ThemeManager', 'Validation Error'), |
783 | - translate('OpenLP.ThemeManager', 'File is not a valid theme.')) |
784 | - self.log_error('Theme file does not contain XML data {name}'.format(name=file_name)) |
785 | |
786 | def check_if_theme_exists(self, theme_name): |
787 | """ |
788 | Check if theme already exists and displays error message |
789 | |
790 | - :param theme_name: Name of the Theme to test |
791 | + :param str theme_name: Name of the Theme to test |
792 | :return: True or False if theme exists |
793 | + :rtype: bool |
794 | """ |
795 | - theme_dir = os.path.join(self.path, theme_name) |
796 | - if os.path.exists(theme_dir): |
797 | + if (self.theme_path / theme_name).exists(): |
798 | critical_error_message_box( |
799 | translate('OpenLP.ThemeManager', 'Validation Error'), |
800 | translate('OpenLP.ThemeManager', 'A theme with this name already exists.')) |
801 | return False |
802 | return True |
803 | |
804 | - def save_theme(self, theme, image_from, image_to): |
805 | + def save_theme(self, theme, image_source_path, image_destination_path): |
806 | """ |
807 | Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list |
808 | |
809 | - :param theme: The theme data object. |
810 | - :param image_from: Where the theme image is currently located. |
811 | - :param image_to: Where the Theme Image is to be saved to |
812 | + :param Theme theme: The theme data object. |
813 | + :param openlp.core.common.path.Path image_source_path: Where the theme image is currently located. |
814 | + :param openlp.core.common.path.Path image_destination_path: Where the Theme Image is to be saved to |
815 | + :rtype: None |
816 | """ |
817 | - self._write_theme(theme, image_from, image_to) |
818 | + self._write_theme(theme, image_source_path, image_destination_path) |
819 | if theme.background_type == BackgroundType.to_string(BackgroundType.Image): |
820 | - self.image_manager.update_image_border(theme.background_filename, |
821 | + self.image_manager.update_image_border(path_to_str(theme.background_filename), |
822 | ImageSource.Theme, |
823 | QtGui.QColor(theme.background_border_color)) |
824 | self.image_manager.process_updates() |
825 | |
826 | - def _write_theme(self, theme, image_from, image_to): |
827 | + def _write_theme(self, theme, image_source_path=None, image_destination_path=None): |
828 | """ |
829 | Writes the theme to the disk and handles the background image if necessary |
830 | |
831 | - :param theme: The theme data object. |
832 | - :param image_from: Where the theme image is currently located. |
833 | - :param image_to: Where the Theme Image is to be saved to |
834 | + :param Theme theme: The theme data object. |
835 | + :param openlp.core.common.path.Path image_source_path: Where the theme image is currently located. |
836 | + :param openlp.core.common.path.Path image_destination_path: Where the Theme Image is to be saved to |
837 | + :rtype: None |
838 | """ |
839 | name = theme.theme_name |
840 | - theme_pretty = theme.export_theme() |
841 | - theme_dir = os.path.join(self.path, name) |
842 | - check_directory_exists(Path(theme_dir)) |
843 | - theme_file = os.path.join(theme_dir, name + '.json') |
844 | - if self.old_background_image and image_to != self.old_background_image: |
845 | - delete_file(Path(self.old_background_image)) |
846 | - out_file = None |
847 | + theme_pretty = theme.export_theme(self.theme_path) |
848 | + theme_dir = self.theme_path / name |
849 | + check_directory_exists(theme_dir) |
850 | + theme_path = theme_dir / '{file_name}.json'.format(file_name=name) |
851 | try: |
852 | - out_file = open(theme_file, 'w', encoding='utf-8') |
853 | - out_file.write(theme_pretty) |
854 | + theme_path.write_text(theme_pretty) |
855 | except IOError: |
856 | self.log_exception('Saving theme to file failed') |
857 | - finally: |
858 | - if out_file: |
859 | - out_file.close() |
860 | - if image_from and os.path.abspath(image_from) != os.path.abspath(image_to): |
861 | - try: |
862 | - # Windows is always unicode, so no need to encode filenames |
863 | - if is_win(): |
864 | - shutil.copyfile(image_from, image_to) |
865 | - else: |
866 | - encoding = get_filesystem_encoding() |
867 | - shutil.copyfile(image_from.encode(encoding), image_to.encode(encoding)) |
868 | - except IOError as xxx_todo_changeme: |
869 | - shutil.Error = xxx_todo_changeme |
870 | - self.log_exception('Failed to save theme image') |
871 | + if image_source_path and image_destination_path: |
872 | + if self.old_background_image_path and image_destination_path != self.old_background_image_path: |
873 | + delete_file(self.old_background_image_path) |
874 | + if image_source_path != image_destination_path: |
875 | + try: |
876 | + copyfile(image_source_path, image_destination_path) |
877 | + except IOError: |
878 | + self.log_exception('Failed to save theme image') |
879 | self.generate_and_save_image(name, theme) |
880 | |
881 | - def generate_and_save_image(self, name, theme): |
882 | + def generate_and_save_image(self, theme_name, theme): |
883 | """ |
884 | Generate and save a preview image |
885 | |
886 | - :param name: The name of the theme. |
887 | + :param str theme_name: The name of the theme. |
888 | :param theme: The theme data object. |
889 | """ |
890 | frame = self.generate_image(theme) |
891 | - sample_path_name = os.path.join(self.path, name + '.png') |
892 | - if os.path.exists(sample_path_name): |
893 | - os.unlink(sample_path_name) |
894 | - frame.save(sample_path_name, 'png') |
895 | - thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=name)) |
896 | - create_thumb(sample_path_name, thumb, False) |
897 | + sample_path_name = self.theme_path / '{file_name}.png'.format(file_name=theme_name) |
898 | + if sample_path_name.exists(): |
899 | + sample_path_name.unlink() |
900 | + frame.save(str(sample_path_name), 'png') |
901 | + thumb_path = self.thumb_path / '{name}.png'.format(name=theme_name) |
902 | + create_thumb(str(sample_path_name), str(thumb_path), False) |
903 | |
904 | def update_preview_images(self): |
905 | """ |
906 | @@ -730,39 +712,32 @@ |
907 | """ |
908 | return self.renderer.generate_preview(theme_data, force_page) |
909 | |
910 | - def get_preview_image(self, theme): |
911 | - """ |
912 | - Return an image representing the look of the theme |
913 | - |
914 | - :param theme: The theme to return the image for. |
915 | - """ |
916 | - return os.path.join(self.path, theme + '.png') |
917 | - |
918 | @staticmethod |
919 | def _create_theme_from_xml(theme_xml, image_path): |
920 | """ |
921 | Return a theme object using information parsed from XML |
922 | |
923 | :param theme_xml: The Theme data object. |
924 | - :param image_path: Where the theme image is stored |
925 | + :param openlp.core.common.path.Path image_path: Where the theme image is stored |
926 | :return: Theme data. |
927 | + :rtype: Theme |
928 | """ |
929 | theme = Theme() |
930 | theme.parse(theme_xml) |
931 | theme.extend_image_filename(image_path) |
932 | return theme |
933 | |
934 | - @staticmethod |
935 | - def _create_theme_from_json(theme_json, image_path): |
936 | + def _create_theme_from_json(self, theme_json, image_path): |
937 | """ |
938 | Return a theme object using information parsed from JSON |
939 | |
940 | :param theme_json: The Theme data object. |
941 | - :param image_path: Where the theme image is stored |
942 | + :param openlp.core.common.path.Path image_path: Where the theme image is stored |
943 | :return: Theme data. |
944 | + :rtype: Theme |
945 | """ |
946 | theme = Theme() |
947 | - theme.load_theme(theme_json) |
948 | + theme.load_theme(theme_json, self.theme_path) |
949 | theme.extend_image_filename(image_path) |
950 | return theme |
951 | |
952 | |
953 | === modified file 'openlp/core/ui/themestab.py' |
954 | --- openlp/core/ui/themestab.py 2016-12-31 11:01:36 +0000 |
955 | +++ openlp/core/ui/themestab.py 2017-09-26 17:09:04 +0000 |
956 | @@ -211,8 +211,8 @@ |
957 | """ |
958 | Utility method to update the global theme preview image. |
959 | """ |
960 | - image = self.theme_manager.get_preview_image(self.global_theme) |
961 | - preview = QtGui.QPixmap(str(image)) |
962 | + image_path = self.theme_manager.theme_path / '{file_name}.png'.format(file_name=self.global_theme) |
963 | + preview = QtGui.QPixmap(str(image_path)) |
964 | if not preview.isNull(): |
965 | preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) |
966 | self.default_list_view.setPixmap(preview) |
967 | |
968 | === modified file 'openlp/plugins/images/lib/upgrade.py' |
969 | --- openlp/plugins/images/lib/upgrade.py 2017-09-23 13:06:42 +0000 |
970 | +++ openlp/plugins/images/lib/upgrade.py 2017-09-26 17:09:04 +0000 |
971 | @@ -48,7 +48,6 @@ |
972 | """ |
973 | Version 2 upgrade - Move file path from old db to JSON encoded path to new db. Added during 2.5 dev |
974 | """ |
975 | - # TODO: Update tests |
976 | log.debug('Starting upgrade_2 for file_path to JSON') |
977 | old_table = Table('image_filenames', metadata, autoload=True) |
978 | if 'file_path' not in [col.name for col in old_table.c.values()]: |
979 | |
980 | === modified file 'tests/functional/openlp_core_lib/test_theme.py' |
981 | --- tests/functional/openlp_core_lib/test_theme.py 2017-05-24 20:04:48 +0000 |
982 | +++ tests/functional/openlp_core_lib/test_theme.py 2017-09-26 17:09:04 +0000 |
983 | @@ -22,8 +22,9 @@ |
984 | """ |
985 | Package to test the openlp.core.lib.theme package. |
986 | """ |
987 | +import os |
988 | +from pathlib import Path |
989 | from unittest import TestCase |
990 | -import os |
991 | |
992 | from openlp.core.lib.theme import Theme |
993 | |
994 | @@ -79,16 +80,16 @@ |
995 | """ |
996 | # GIVEN: A theme object |
997 | theme = Theme() |
998 | - theme.theme_name = 'MyBeautifulTheme ' |
999 | - theme.background_filename = ' video.mp4' |
1000 | + theme.theme_name = 'MyBeautifulTheme' |
1001 | + theme.background_filename = Path('video.mp4') |
1002 | theme.background_type = 'video' |
1003 | - path = os.path.expanduser('~') |
1004 | + path = Path.home() |
1005 | |
1006 | # WHEN: Theme.extend_image_filename is run |
1007 | theme.extend_image_filename(path) |
1008 | |
1009 | # THEN: The filename of the background should be correct |
1010 | - expected_filename = os.path.join(path, 'MyBeautifulTheme', 'video.mp4') |
1011 | + expected_filename = path / 'MyBeautifulTheme' / 'video.mp4' |
1012 | self.assertEqual(expected_filename, theme.background_filename) |
1013 | self.assertEqual('MyBeautifulTheme', theme.theme_name) |
1014 | |
1015 | |
1016 | === modified file 'tests/functional/openlp_core_ui/test_maindisplay.py' |
1017 | --- tests/functional/openlp_core_ui/test_maindisplay.py 2017-07-08 13:12:31 +0000 |
1018 | +++ tests/functional/openlp_core_ui/test_maindisplay.py 2017-09-26 17:09:04 +0000 |
1019 | @@ -27,10 +27,10 @@ |
1020 | |
1021 | from PyQt5 import QtCore |
1022 | |
1023 | -from openlp.core.common import Registry, is_macosx, Settings |
1024 | +from openlp.core.common import Registry, is_macosx |
1025 | +from openlp.core.common.path import Path |
1026 | from openlp.core.lib import ScreenList, PluginManager |
1027 | from openlp.core.ui import MainDisplay, AudioPlayer |
1028 | -from openlp.core.ui.media import MediaController |
1029 | from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET |
1030 | |
1031 | from tests.helpers.testmixin import TestMixin |
1032 | @@ -184,7 +184,7 @@ |
1033 | self.assertEqual(pyobjc_nsview.window().collectionBehavior(), NSWindowCollectionBehaviorManaged, |
1034 | 'Window collection behavior should be NSWindowCollectionBehaviorManaged') |
1035 | |
1036 | - @patch(u'openlp.core.ui.maindisplay.Settings') |
1037 | + @patch('openlp.core.ui.maindisplay.Settings') |
1038 | def test_show_display_startup_logo(self, MockedSettings): |
1039 | # GIVEN: Mocked show_display, setting for logo visibility |
1040 | display = MagicMock() |
1041 | @@ -204,7 +204,7 @@ |
1042 | # THEN: setVisible should had been called with "True" |
1043 | main_display.setVisible.assert_called_once_with(True) |
1044 | |
1045 | - @patch(u'openlp.core.ui.maindisplay.Settings') |
1046 | + @patch('openlp.core.ui.maindisplay.Settings') |
1047 | def test_show_display_hide_startup_logo(self, MockedSettings): |
1048 | # GIVEN: Mocked show_display, setting for logo visibility |
1049 | display = MagicMock() |
1050 | @@ -224,8 +224,8 @@ |
1051 | # THEN: setVisible should had not been called |
1052 | main_display.setVisible.assert_not_called() |
1053 | |
1054 | - @patch(u'openlp.core.ui.maindisplay.Settings') |
1055 | - @patch(u'openlp.core.ui.maindisplay.build_html') |
1056 | + @patch('openlp.core.ui.maindisplay.Settings') |
1057 | + @patch('openlp.core.ui.maindisplay.build_html') |
1058 | def test_build_html_no_video(self, MockedSettings, Mocked_build_html): |
1059 | # GIVEN: Mocked display |
1060 | display = MagicMock() |
1061 | @@ -252,8 +252,8 @@ |
1062 | self.assertEquals(main_display.media_controller.video.call_count, 0, |
1063 | 'Media Controller video should not have been called') |
1064 | |
1065 | - @patch(u'openlp.core.ui.maindisplay.Settings') |
1066 | - @patch(u'openlp.core.ui.maindisplay.build_html') |
1067 | + @patch('openlp.core.ui.maindisplay.Settings') |
1068 | + @patch('openlp.core.ui.maindisplay.build_html') |
1069 | def test_build_html_video(self, MockedSettings, Mocked_build_html): |
1070 | # GIVEN: Mocked display |
1071 | display = MagicMock() |
1072 | @@ -270,7 +270,7 @@ |
1073 | service_item.theme_data = MagicMock() |
1074 | service_item.theme_data.background_type = 'video' |
1075 | service_item.theme_data.theme_name = 'name' |
1076 | - service_item.theme_data.background_filename = 'background_filename' |
1077 | + service_item.theme_data.background_filename = Path('background_filename') |
1078 | mocked_plugin = MagicMock() |
1079 | display.plugin_manager = PluginManager() |
1080 | display.plugin_manager.plugins = [mocked_plugin] |
1081 | |
1082 | === modified file 'tests/functional/openlp_core_ui/test_themeform.py' |
1083 | --- tests/functional/openlp_core_ui/test_themeform.py 2017-08-25 20:03:25 +0000 |
1084 | +++ tests/functional/openlp_core_ui/test_themeform.py 2017-09-26 17:09:04 +0000 |
1085 | @@ -49,5 +49,5 @@ |
1086 | self.instance.on_image_path_edit_path_changed(Path('/', 'new', 'pat.h')) |
1087 | |
1088 | # THEN: The theme background file should be set and `set_background_page_values` should have been called |
1089 | - self.assertEqual(self.instance.theme.background_filename, '/new/pat.h') |
1090 | + self.assertEqual(self.instance.theme.background_filename, Path('/', 'new', 'pat.h')) |
1091 | mocked_set_background_page_values.assert_called_once_with() |
1092 | |
1093 | === modified file 'tests/functional/openlp_core_ui/test_thememanager.py' |
1094 | --- tests/functional/openlp_core_ui/test_thememanager.py 2017-08-12 17:45:56 +0000 |
1095 | +++ tests/functional/openlp_core_ui/test_thememanager.py 2017-09-26 17:09:04 +0000 |
1096 | @@ -30,8 +30,9 @@ |
1097 | |
1098 | from PyQt5 import QtWidgets |
1099 | |
1100 | +from openlp.core.common import Registry |
1101 | +from openlp.core.common.path import Path |
1102 | from openlp.core.ui import ThemeManager |
1103 | -from openlp.core.common import Registry |
1104 | |
1105 | from tests.utils.constants import TEST_RESOURCES_PATH |
1106 | |
1107 | @@ -57,13 +58,13 @@ |
1108 | """ |
1109 | # GIVEN: A new ThemeManager instance. |
1110 | theme_manager = ThemeManager() |
1111 | - theme_manager.path = os.path.join(TEST_RESOURCES_PATH, 'themes') |
1112 | + theme_manager.theme_path = Path(TEST_RESOURCES_PATH, 'themes') |
1113 | with patch('zipfile.ZipFile.__init__') as mocked_zipfile_init, \ |
1114 | patch('zipfile.ZipFile.write') as mocked_zipfile_write: |
1115 | mocked_zipfile_init.return_value = None |
1116 | |
1117 | # WHEN: The theme is exported |
1118 | - theme_manager._export_theme(os.path.join('some', 'path', 'Default.otz'), 'Default') |
1119 | + theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default') |
1120 | |
1121 | # THEN: The zipfile should be created at the given path |
1122 | mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w') |
1123 | @@ -86,57 +87,49 @@ |
1124 | """ |
1125 | Test that we don't try to overwrite a theme background image with itself |
1126 | """ |
1127 | - # GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile, |
1128 | + # GIVEN: A new theme manager instance, with mocked builtins.open, copyfile, |
1129 | # theme, check_directory_exists and thememanager-attributes. |
1130 | - with patch('builtins.open') as mocked_open, \ |
1131 | - patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \ |
1132 | + with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \ |
1133 | patch('openlp.core.ui.thememanager.check_directory_exists'): |
1134 | - mocked_open.return_value = MagicMock() |
1135 | theme_manager = ThemeManager(None) |
1136 | theme_manager.old_background_image = None |
1137 | theme_manager.generate_and_save_image = MagicMock() |
1138 | - theme_manager.path = '' |
1139 | + theme_manager.theme_path = MagicMock() |
1140 | mocked_theme = MagicMock() |
1141 | mocked_theme.theme_name = 'themename' |
1142 | mocked_theme.extract_formatted_xml = MagicMock() |
1143 | mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode() |
1144 | |
1145 | # WHEN: Calling _write_theme with path to the same image, but the path written slightly different |
1146 | - file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg') |
1147 | - # Do replacement from end of string to avoid problems with path start |
1148 | - file_name2 = file_name1[::-1].replace(os.sep, os.sep + os.sep, 2)[::-1] |
1149 | - theme_manager._write_theme(mocked_theme, file_name1, file_name2) |
1150 | + file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg') |
1151 | + theme_manager._write_theme(mocked_theme, file_name1, file_name1) |
1152 | |
1153 | # THEN: The mocked_copyfile should not have been called |
1154 | - self.assertFalse(mocked_copyfile.called, 'shutil.copyfile should not be called') |
1155 | + self.assertFalse(mocked_copyfile.called, 'copyfile should not be called') |
1156 | |
1157 | def test_write_theme_diff_images(self): |
1158 | """ |
1159 | Test that we do overwrite a theme background image when a new is submitted |
1160 | """ |
1161 | - # GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile, |
1162 | + # GIVEN: A new theme manager instance, with mocked builtins.open, copyfile, |
1163 | # theme, check_directory_exists and thememanager-attributes. |
1164 | - with patch('builtins.open') as mocked_open, \ |
1165 | - patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \ |
1166 | + with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \ |
1167 | patch('openlp.core.ui.thememanager.check_directory_exists'): |
1168 | - mocked_open.return_value = MagicMock() |
1169 | theme_manager = ThemeManager(None) |
1170 | theme_manager.old_background_image = None |
1171 | theme_manager.generate_and_save_image = MagicMock() |
1172 | - theme_manager.path = '' |
1173 | + theme_manager.theme_path = MagicMock() |
1174 | mocked_theme = MagicMock() |
1175 | mocked_theme.theme_name = 'themename' |
1176 | mocked_theme.filename = "filename" |
1177 | - # mocked_theme.extract_formatted_xml = MagicMock() |
1178 | - # mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode() |
1179 | |
1180 | # WHEN: Calling _write_theme with path to different images |
1181 | - file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg') |
1182 | - file_name2 = os.path.join(TEST_RESOURCES_PATH, 'church2.jpg') |
1183 | + file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg') |
1184 | + file_name2 = Path(TEST_RESOURCES_PATH, 'church2.jpg') |
1185 | theme_manager._write_theme(mocked_theme, file_name1, file_name2) |
1186 | |
1187 | # THEN: The mocked_copyfile should not have been called |
1188 | - self.assertTrue(mocked_copyfile.called, 'shutil.copyfile should be called') |
1189 | + self.assertTrue(mocked_copyfile.called, 'copyfile should be called') |
1190 | |
1191 | def test_write_theme_special_char_name(self): |
1192 | """ |
1193 | @@ -146,7 +139,7 @@ |
1194 | theme_manager = ThemeManager(None) |
1195 | theme_manager.old_background_image = None |
1196 | theme_manager.generate_and_save_image = MagicMock() |
1197 | - theme_manager.path = self.temp_folder |
1198 | + theme_manager.theme_path = Path(self.temp_folder) |
1199 | mocked_theme = MagicMock() |
1200 | mocked_theme.theme_name = 'theme 愛 name' |
1201 | mocked_theme.export_theme.return_value = "{}" |
1202 | @@ -208,17 +201,17 @@ |
1203 | theme_manager = ThemeManager(None) |
1204 | theme_manager._create_theme_from_xml = MagicMock() |
1205 | theme_manager.generate_and_save_image = MagicMock() |
1206 | - theme_manager.path = '' |
1207 | - folder = mkdtemp() |
1208 | - theme_file = os.path.join(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz') |
1209 | + theme_manager.theme_path = None |
1210 | + folder = Path(mkdtemp()) |
1211 | + theme_file = Path(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz') |
1212 | |
1213 | # WHEN: We try to unzip it |
1214 | theme_manager.unzip_theme(theme_file, folder) |
1215 | |
1216 | # THEN: Files should be unpacked |
1217 | - self.assertTrue(os.path.exists(os.path.join(folder, 'Moss on tree', 'Moss on tree.xml'))) |
1218 | + self.assertTrue((folder / 'Moss on tree' / 'Moss on tree.xml').exists()) |
1219 | self.assertEqual(mocked_critical_error_message_box.call_count, 0, 'No errors should have happened') |
1220 | - shutil.rmtree(folder) |
1221 | + shutil.rmtree(str(folder)) |
1222 | |
1223 | def test_unzip_theme_invalid_version(self): |
1224 | """ |
1225 | |
1226 | === modified file 'tests/interfaces/openlp_core_ui/test_thememanager.py' |
1227 | --- tests/interfaces/openlp_core_ui/test_thememanager.py 2017-04-24 05:17:55 +0000 |
1228 | +++ tests/interfaces/openlp_core_ui/test_thememanager.py 2017-09-26 17:09:04 +0000 |
1229 | @@ -26,7 +26,8 @@ |
1230 | from unittest.mock import patch, MagicMock |
1231 | |
1232 | from openlp.core.common import Registry, Settings |
1233 | -from openlp.core.ui import ThemeManager, ThemeForm, FileRenameForm |
1234 | +from openlp.core.common.path import Path |
1235 | +from openlp.core.ui import ThemeManager |
1236 | |
1237 | from tests.helpers.testmixin import TestMixin |
1238 | |
1239 | @@ -91,6 +92,23 @@ |
1240 | assert self.theme_manager.thumb_path.startswith(self.theme_manager.path) is True, \ |
1241 | 'The thumb path and the main path should start with the same value' |
1242 | |
1243 | + def test_build_theme_path(self): |
1244 | + """ |
1245 | + Test the thememanager build_theme_path - basic test |
1246 | + """ |
1247 | + # GIVEN: A new a call to initialise |
1248 | + with patch('openlp.core.common.AppLocation.get_section_data_path', return_value=Path('test/path')): |
1249 | + Settings().setValue('themes/global theme', 'my_theme') |
1250 | + |
1251 | + self.theme_manager.theme_form = MagicMock() |
1252 | + self.theme_manager.load_first_time_themes = MagicMock() |
1253 | + |
1254 | + # WHEN: the build_theme_path is run |
1255 | + self.theme_manager.build_theme_path() |
1256 | + |
1257 | + # THEN: The thumbnail path should be a sub path of the test path |
1258 | + self.assertEqual(self.theme_manager.thumb_path, Path('test/path/thumbnails')) |
1259 | + |
1260 | def test_click_on_new_theme(self): |
1261 | """ |
1262 | Test the on_add_theme event handler is called by the UI |
1263 | @@ -109,17 +127,16 @@ |
1264 | |
1265 | @patch('openlp.core.ui.themeform.ThemeForm._setup') |
1266 | @patch('openlp.core.ui.filerenameform.FileRenameForm._setup') |
1267 | - def test_bootstrap_post(self, mocked_theme_form, mocked_rename_form): |
1268 | + def test_bootstrap_post(self, mocked_rename_form, mocked_theme_form): |
1269 | """ |
1270 | Test the functions of bootstrap_post_setup are called. |
1271 | """ |
1272 | # GIVEN: |
1273 | self.theme_manager.load_themes = MagicMock() |
1274 | - self.theme_manager.path = MagicMock() |
1275 | + self.theme_manager.theme_path = MagicMock() |
1276 | |
1277 | # WHEN: |
1278 | self.theme_manager.bootstrap_post_set_up() |
1279 | |
1280 | # THEN: |
1281 | - self.assertEqual(self.theme_manager.path, self.theme_manager.theme_form.path) |
1282 | self.assertEqual(1, self.theme_manager.load_themes.call_count, "load_themes should have been called once") |
Also automatically upgrades themes from XML to json.