Merge lp:~phill-ridout/openlp/ftw-refactors into lp:openlp
- ftw-refactors
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 2853 |
Proposed branch: | lp:~phill-ridout/openlp/ftw-refactors |
Merge into: | lp:openlp |
Diff against target: |
1109 lines (+341/-229) 14 files modified
openlp/core/app.py (+1/-1) openlp/core/common/httputils.py (+16/-8) openlp/core/display/render.py (+2/-2) openlp/core/threading.py (+2/-2) openlp/core/ui/firsttimeform.py (+85/-94) openlp/core/ui/firsttimewizard.py (+64/-63) openlp/core/ui/mainwindow.py (+1/-2) openlp/core/widgets/widgets.py (+28/-0) openlp/plugins/songs/lib/importers/openlp.py (+1/-1) openlp/plugins/songs/reporting.py (+0/-7) tests/functional/openlp_core/common/test_httputils.py (+5/-9) tests/functional/openlp_core/test_threading.py (+5/-9) tests/functional/openlp_core/ui/test_firsttimeform.py (+125/-25) tests/functional/openlp_plugins/songs/test_openlpimporter.py (+6/-6) |
To merge this branch: | bzr merge lp:~phill-ridout/openlp/ftw-refactors |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tomas Groth | Approve | ||
Tim Bentley | Pending | ||
Review via email: mp+364604@code.launchpad.net |
This proposal supersedes a proposal from 2019-03-10.
Commit message
Add proxy settings to ftw. Option to skip sample data
Description of the change
Add option to skip downloading.
Add dialog for proxy settings to FTW
FTW refactors.
General fixes.
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linux tests failed, please see https:/
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linux tests passed!
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linting failed, please see https:/
Tomas Groth (tomasgroth) wrote : Posted in a previous version of this proposal | # |
Some code style needs fixing - see link above
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linux tests passed!
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linting failed, please see https:/
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linux tests passed!
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linting failed, please see https:/
Tim Bentley (trb143) : Posted in a previous version of this proposal | # |
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linux tests passed!
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linting passed!
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
macOS tests failed, please see https:/
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linux tests passed!
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal | # |
Linting failed, please see https:/
Tomas Groth (tomasgroth) wrote : Posted in a previous version of this proposal | # |
Still some lint issues...
Raoul Snyman (raoul-snyman) wrote : | # |
Linux tests passed!
Raoul Snyman (raoul-snyman) wrote : | # |
Linting passed!
Raoul Snyman (raoul-snyman) wrote : | # |
macOS tests failed, please see https:/
Tomas Groth (tomasgroth) : | # |
Preview Diff
1 | === modified file 'openlp/core/app.py' | |||
2 | --- openlp/core/app.py 2019-02-14 15:09:09 +0000 | |||
3 | +++ openlp/core/app.py 2019-03-15 20:50:11 +0000 | |||
4 | @@ -101,7 +101,7 @@ | |||
5 | 101 | ftw.initialize(screens) | 101 | ftw.initialize(screens) |
6 | 102 | if ftw.exec() == QtWidgets.QDialog.Accepted: | 102 | if ftw.exec() == QtWidgets.QDialog.Accepted: |
7 | 103 | Settings().setValue('core/has run wizard', True) | 103 | Settings().setValue('core/has run wizard', True) |
9 | 104 | elif ftw.was_cancelled: | 104 | else: |
10 | 105 | QtCore.QCoreApplication.exit() | 105 | QtCore.QCoreApplication.exit() |
11 | 106 | sys.exit() | 106 | sys.exit() |
12 | 107 | # Correct stylesheet bugs | 107 | # Correct stylesheet bugs |
13 | 108 | 108 | ||
14 | === modified file 'openlp/core/common/httputils.py' | |||
15 | --- openlp/core/common/httputils.py 2019-02-21 21:29:00 +0000 | |||
16 | +++ openlp/core/common/httputils.py 2019-03-15 20:50:11 +0000 | |||
17 | @@ -158,16 +158,20 @@ | |||
18 | 158 | return response.text | 158 | return response.text |
19 | 159 | 159 | ||
20 | 160 | 160 | ||
22 | 161 | def get_url_file_size(url): | 161 | def get_url_file_size(url, proxy=None): |
23 | 162 | """ | 162 | """ |
24 | 163 | Get the size of a file. | 163 | Get the size of a file. |
25 | 164 | 164 | ||
26 | 165 | :param url: The URL of the file we want to download. | 165 | :param url: The URL of the file we want to download. |
27 | 166 | :param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types | ||
28 | 167 | as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'} | ||
29 | 166 | """ | 168 | """ |
30 | 167 | retries = 0 | 169 | retries = 0 |
31 | 170 | if not isinstance(proxy, dict): | ||
32 | 171 | proxy = get_proxy_settings(mode=proxy) | ||
33 | 168 | while True: | 172 | while True: |
34 | 169 | try: | 173 | try: |
36 | 170 | response = requests.head(url, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True) | 174 | response = requests.head(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True) |
37 | 171 | return int(response.headers['Content-Length']) | 175 | return int(response.headers['Content-Length']) |
38 | 172 | except OSError: | 176 | except OSError: |
39 | 173 | if retries > CONNECTION_RETRIES: | 177 | if retries > CONNECTION_RETRIES: |
40 | @@ -178,7 +182,7 @@ | |||
41 | 178 | continue | 182 | continue |
42 | 179 | 183 | ||
43 | 180 | 184 | ||
45 | 181 | def download_file(update_object, url, file_path, sha256=None): | 185 | def download_file(update_object, url, file_path, sha256=None, proxy=None): |
46 | 182 | """" | 186 | """" |
47 | 183 | Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any | 187 | Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any |
48 | 184 | point. Returns False on download error. | 188 | point. Returns False on download error. |
49 | @@ -187,15 +191,19 @@ | |||
50 | 187 | :param url: URL to download | 191 | :param url: URL to download |
51 | 188 | :param file_path: Destination file | 192 | :param file_path: Destination file |
52 | 189 | :param sha256: The check sum value to be checked against the download value | 193 | :param sha256: The check sum value to be checked against the download value |
53 | 194 | :param dict | ProxyMode | None proxy: ProxyMode enum or a dictionary containing the proxy servers, with their types | ||
54 | 195 | as the key e.g. {'http': 'http://proxyserver:port', 'https': 'https://proxyserver:port'} | ||
55 | 190 | """ | 196 | """ |
56 | 191 | block_count = 0 | 197 | block_count = 0 |
57 | 192 | block_size = 4096 | 198 | block_size = 4096 |
58 | 193 | retries = 0 | 199 | retries = 0 |
59 | 200 | if not isinstance(proxy, dict): | ||
60 | 201 | proxy = get_proxy_settings(mode=proxy) | ||
61 | 194 | log.debug('url_get_file: %s', url) | 202 | log.debug('url_get_file: %s', url) |
62 | 195 | while retries < CONNECTION_RETRIES: | 203 | while retries < CONNECTION_RETRIES: |
63 | 196 | try: | 204 | try: |
64 | 197 | with file_path.open('wb') as saved_file: | 205 | with file_path.open('wb') as saved_file: |
66 | 198 | response = requests.get(url, timeout=float(CONNECTION_TIMEOUT), stream=True) | 206 | response = requests.get(url, proxies=proxy, timeout=float(CONNECTION_TIMEOUT), stream=True) |
67 | 199 | if sha256: | 207 | if sha256: |
68 | 200 | hasher = hashlib.sha256() | 208 | hasher = hashlib.sha256() |
69 | 201 | # Download until finished or canceled. | 209 | # Download until finished or canceled. |
70 | @@ -244,21 +252,21 @@ | |||
71 | 244 | """ | 252 | """ |
72 | 245 | self._base_url = base_url | 253 | self._base_url = base_url |
73 | 246 | self._file_name = file_name | 254 | self._file_name = file_name |
75 | 247 | self._download_cancelled = False | 255 | self.was_cancelled = False |
76 | 248 | super().__init__() | 256 | super().__init__() |
77 | 249 | 257 | ||
78 | 250 | def start(self): | 258 | def start(self): |
79 | 251 | """ | 259 | """ |
80 | 252 | Download the url to the temporary directory | 260 | Download the url to the temporary directory |
81 | 253 | """ | 261 | """ |
83 | 254 | if self._download_cancelled: | 262 | if self.was_cancelled: |
84 | 255 | self.quit.emit() | 263 | self.quit.emit() |
85 | 256 | return | 264 | return |
86 | 257 | try: | 265 | try: |
87 | 258 | dest_path = Path(gettempdir()) / 'openlp' / self._file_name | 266 | dest_path = Path(gettempdir()) / 'openlp' / self._file_name |
88 | 259 | url = '{url}{name}'.format(url=self._base_url, name=self._file_name) | 267 | url = '{url}{name}'.format(url=self._base_url, name=self._file_name) |
89 | 260 | is_success = download_file(self, url, dest_path) | 268 | is_success = download_file(self, url, dest_path) |
91 | 261 | if is_success and not self._download_cancelled: | 269 | if is_success and not self.was_cancelled: |
92 | 262 | self.download_succeeded.emit(dest_path) | 270 | self.download_succeeded.emit(dest_path) |
93 | 263 | else: | 271 | else: |
94 | 264 | self.download_failed.emit() | 272 | self.download_failed.emit() |
95 | @@ -273,4 +281,4 @@ | |||
96 | 273 | """ | 281 | """ |
97 | 274 | A slot to allow the download to be cancelled from outside of the thread | 282 | A slot to allow the download to be cancelled from outside of the thread |
98 | 275 | """ | 283 | """ |
100 | 276 | self._download_cancelled = True | 284 | self.was_cancelled = True |
101 | 277 | 285 | ||
102 | === modified file 'openlp/core/display/render.py' | |||
103 | --- openlp/core/display/render.py 2019-02-19 21:38:44 +0000 | |||
104 | +++ openlp/core/display/render.py 2019-03-15 20:50:11 +0000 | |||
105 | @@ -43,8 +43,8 @@ | |||
106 | 43 | log = logging.getLogger(__name__) | 43 | log = logging.getLogger(__name__) |
107 | 44 | 44 | ||
108 | 45 | SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\' | 45 | SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\' |
111 | 46 | CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' # noqa | 46 | CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' |
112 | 47 | '([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?') | 47 | r'([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?') |
113 | 48 | CHORD_TEMPLATE = '<span class="chordline">{chord}</span>' | 48 | CHORD_TEMPLATE = '<span class="chordline">{chord}</span>' |
114 | 49 | FIRST_CHORD_TEMPLATE = '<span class="chordline firstchordline">{chord}</span>' | 49 | FIRST_CHORD_TEMPLATE = '<span class="chordline firstchordline">{chord}</span>' |
115 | 50 | CHORD_LINE_TEMPLATE = '<span class="chord"><span><strong>{chord}</strong></span></span>{tail}{whitespace}{remainder}' | 50 | CHORD_LINE_TEMPLATE = '<span class="chord"><span><strong>{chord}</strong></span></span>{tail}{whitespace}{remainder}' |
116 | 51 | 51 | ||
117 | === modified file 'openlp/core/threading.py' | |||
118 | --- openlp/core/threading.py 2019-02-14 15:09:09 +0000 | |||
119 | +++ openlp/core/threading.py 2019-03-15 20:50:11 +0000 | |||
120 | @@ -76,11 +76,11 @@ | |||
121 | 76 | Get the worker by the thread name | 76 | Get the worker by the thread name |
122 | 77 | 77 | ||
123 | 78 | :param str thread_name: The name of the thread | 78 | :param str thread_name: The name of the thread |
125 | 79 | :returns: The worker for this thread name | 79 | :returns: The worker for this thread name, or None |
126 | 80 | """ | 80 | """ |
127 | 81 | thread_info = Registry().get('application').worker_threads.get(thread_name) | 81 | thread_info = Registry().get('application').worker_threads.get(thread_name) |
128 | 82 | if not thread_info: | 82 | if not thread_info: |
130 | 83 | raise KeyError('No thread named "{}" exists'.format(thread_name)) | 83 | return |
131 | 84 | return thread_info.get('worker') | 84 | return thread_info.get('worker') |
132 | 85 | 85 | ||
133 | 86 | 86 | ||
134 | 87 | 87 | ||
135 | === modified file 'openlp/core/ui/firsttimeform.py' | |||
136 | --- openlp/core/ui/firsttimeform.py 2019-02-21 21:29:00 +0000 | |||
137 | +++ openlp/core/ui/firsttimeform.py 2019-03-15 20:50:11 +0000 | |||
138 | @@ -32,7 +32,7 @@ | |||
139 | 32 | 32 | ||
140 | 33 | from PyQt5 import QtCore, QtWidgets | 33 | from PyQt5 import QtCore, QtWidgets |
141 | 34 | 34 | ||
143 | 35 | from openlp.core.common import clean_button_text, trace_error_handler | 35 | from openlp.core.common import trace_error_handler |
144 | 36 | from openlp.core.common.applocation import AppLocation | 36 | from openlp.core.common.applocation import AppLocation |
145 | 37 | from openlp.core.common.httputils import DownloadWorker, download_file, get_url_file_size, get_web_page | 37 | from openlp.core.common.httputils import DownloadWorker, download_file, get_url_file_size, get_web_page |
146 | 38 | from openlp.core.common.i18n import translate | 38 | from openlp.core.common.i18n import translate |
147 | @@ -46,6 +46,7 @@ | |||
148 | 46 | from openlp.core.threading import get_thread_worker, is_thread_finished, run_thread | 46 | from openlp.core.threading import get_thread_worker, is_thread_finished, run_thread |
149 | 47 | from openlp.core.ui.firsttimewizard import FirstTimePage, UiFirstTimeWizard | 47 | from openlp.core.ui.firsttimewizard import FirstTimePage, UiFirstTimeWizard |
150 | 48 | from openlp.core.ui.icons import UiIcons | 48 | from openlp.core.ui.icons import UiIcons |
151 | 49 | from openlp.core.widgets.widgets import ProxyDialog | ||
152 | 49 | 50 | ||
153 | 50 | 51 | ||
154 | 51 | log = logging.getLogger(__name__) | 52 | log = logging.getLogger(__name__) |
155 | @@ -91,7 +92,7 @@ | |||
156 | 91 | 92 | ||
157 | 92 | class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): | 93 | class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): |
158 | 93 | """ | 94 | """ |
160 | 94 | This is the Theme Import Wizard, which allows easy creation and editing of OpenLP themes. | 95 | This is the FirstTimeWizard, designed to help new users to get up and running quickly. |
161 | 95 | """ | 96 | """ |
162 | 96 | log.info('ThemeWizardForm loaded') | 97 | log.info('ThemeWizardForm loaded') |
163 | 97 | 98 | ||
164 | @@ -103,6 +104,7 @@ | |||
165 | 103 | self.web_access = True | 104 | self.web_access = True |
166 | 104 | self.web = '' | 105 | self.web = '' |
167 | 105 | self.setup_ui(self) | 106 | self.setup_ui(self) |
168 | 107 | self.customButtonClicked.connect(self._on_custom_button_clicked) | ||
169 | 106 | self.themes_list_widget.itemSelectionChanged.connect(self.on_themes_list_widget_selection_changed) | 108 | self.themes_list_widget.itemSelectionChanged.connect(self.on_themes_list_widget_selection_changed) |
170 | 107 | self.themes_deselect_all_button.clicked.connect(self.themes_list_widget.clearSelection) | 109 | self.themes_deselect_all_button.clicked.connect(self.themes_list_widget.clearSelection) |
171 | 108 | self.themes_select_all_button.clicked.connect(self.themes_list_widget.selectAll) | 110 | self.themes_select_all_button.clicked.connect(self.themes_list_widget.selectAll) |
172 | @@ -111,13 +113,13 @@ | |||
173 | 111 | """ | 113 | """ |
174 | 112 | Returns the id of the next FirstTimePage to go to based on enabled plugins | 114 | Returns the id of the next FirstTimePage to go to based on enabled plugins |
175 | 113 | """ | 115 | """ |
177 | 114 | if FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked(): | 116 | if FirstTimePage.Download < self.currentId() < FirstTimePage.Songs and self.songs_check_box.isChecked(): |
178 | 115 | # If the songs plugin is enabled then go to the songs page | 117 | # If the songs plugin is enabled then go to the songs page |
179 | 116 | return FirstTimePage.Songs | 118 | return FirstTimePage.Songs |
181 | 117 | elif FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked(): | 119 | elif FirstTimePage.Download < self.currentId() < FirstTimePage.Bibles and self.bible_check_box.isChecked(): |
182 | 118 | # Otherwise, if the Bibles plugin is enabled then go to the Bibles page | 120 | # Otherwise, if the Bibles plugin is enabled then go to the Bibles page |
183 | 119 | return FirstTimePage.Bibles | 121 | return FirstTimePage.Bibles |
185 | 120 | elif FirstTimePage.ScreenConfig < self.currentId() < FirstTimePage.Themes: | 122 | elif FirstTimePage.Download < self.currentId() < FirstTimePage.Themes: |
186 | 121 | # Otherwise, if the current page is somewhere between the Welcome and the Themes pages, go to the themes | 123 | # Otherwise, if the current page is somewhere between the Welcome and the Themes pages, go to the themes |
187 | 122 | return FirstTimePage.Themes | 124 | return FirstTimePage.Themes |
188 | 123 | else: | 125 | else: |
189 | @@ -133,9 +135,7 @@ | |||
190 | 133 | if not self.web_access: | 135 | if not self.web_access: |
191 | 134 | return FirstTimePage.NoInternet | 136 | return FirstTimePage.NoInternet |
192 | 135 | else: | 137 | else: |
196 | 136 | return FirstTimePage.Plugins | 138 | return FirstTimePage.Songs |
194 | 137 | elif self.currentId() == FirstTimePage.Plugins: | ||
195 | 138 | return self.get_next_page_id() | ||
197 | 139 | elif self.currentId() == FirstTimePage.Progress: | 139 | elif self.currentId() == FirstTimePage.Progress: |
198 | 140 | return -1 | 140 | return -1 |
199 | 141 | elif self.currentId() == FirstTimePage.NoInternet: | 141 | elif self.currentId() == FirstTimePage.NoInternet: |
200 | @@ -147,7 +147,7 @@ | |||
201 | 147 | Run the wizard. | 147 | Run the wizard. |
202 | 148 | """ | 148 | """ |
203 | 149 | self.set_defaults() | 149 | self.set_defaults() |
205 | 150 | return QtWidgets.QWizard.exec(self) | 150 | return super().exec() |
206 | 151 | 151 | ||
207 | 152 | def initialize(self, screens): | 152 | def initialize(self, screens): |
208 | 153 | """ | 153 | """ |
209 | @@ -227,17 +227,13 @@ | |||
210 | 227 | """ | 227 | """ |
211 | 228 | self.restart() | 228 | self.restart() |
212 | 229 | self.web = 'https://get.openlp.org/ftw/' | 229 | self.web = 'https://get.openlp.org/ftw/' |
213 | 230 | self.cancel_button.clicked.connect(self.on_cancel_button_clicked) | ||
214 | 231 | self.no_internet_finish_button.clicked.connect(self.on_no_internet_finish_button_clicked) | ||
215 | 232 | self.no_internet_cancel_button.clicked.connect(self.on_no_internet_cancel_button_clicked) | ||
216 | 233 | self.currentIdChanged.connect(self.on_current_id_changed) | 230 | self.currentIdChanged.connect(self.on_current_id_changed) |
217 | 234 | Registry().register_function('config_screen_changed', self.screen_selection_widget.load) | 231 | Registry().register_function('config_screen_changed', self.screen_selection_widget.load) |
218 | 235 | self.no_internet_finish_button.setVisible(False) | ||
219 | 236 | self.no_internet_cancel_button.setVisible(False) | ||
220 | 237 | # Check if this is a re-run of the wizard. | 232 | # Check if this is a re-run of the wizard. |
221 | 238 | self.has_run_wizard = Settings().value('core/has run wizard') | 233 | self.has_run_wizard = Settings().value('core/has run wizard') |
222 | 239 | create_paths(Path(gettempdir(), 'openlp')) | 234 | create_paths(Path(gettempdir(), 'openlp')) |
223 | 240 | self.theme_combo_box.clear() | 235 | self.theme_combo_box.clear() |
224 | 236 | self.button(QtWidgets.QWizard.CustomButton1).setVisible(False) | ||
225 | 241 | if self.has_run_wizard: | 237 | if self.has_run_wizard: |
226 | 242 | self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active()) | 238 | self.songs_check_box.setChecked(self.plugin_manager.get_plugin_by_name('songs').is_active()) |
227 | 243 | self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active()) | 239 | self.bible_check_box.setChecked(self.plugin_manager.get_plugin_by_name('bibles').is_active()) |
228 | @@ -260,57 +256,92 @@ | |||
229 | 260 | """ | 256 | """ |
230 | 261 | Detects Page changes and updates as appropriate. | 257 | Detects Page changes and updates as appropriate. |
231 | 262 | """ | 258 | """ |
233 | 263 | # Keep track of the page we are at. Triggering "Cancel" causes page_id to be a -1. | 259 | back_button = self.button(QtWidgets.QWizard.BackButton) |
234 | 260 | cancel_button = self.button(QtWidgets.QWizard.CancelButton) | ||
235 | 261 | internet_settings_button = self.button(QtWidgets.QWizard.CustomButton1) | ||
236 | 262 | next_button = self.button(QtWidgets.QWizard.NextButton) | ||
237 | 263 | back_button.setVisible(True) | ||
238 | 264 | next_button.setVisible(True) | ||
239 | 265 | internet_settings_button.setVisible(False) | ||
240 | 264 | self.application.process_events() | 266 | self.application.process_events() |
251 | 265 | if page_id != -1: | 267 | if page_id == FirstTimePage.SampleOption: |
252 | 266 | self.last_id = page_id | 268 | internet_settings_button.setVisible(True) |
253 | 267 | if page_id == FirstTimePage.Download: | 269 | elif page_id == FirstTimePage.Download: |
254 | 268 | self.back_button.setVisible(False) | 270 | back_button.setVisible(False) |
255 | 269 | self.next_button.setVisible(False) | 271 | next_button.setVisible(False) |
246 | 270 | # Set the no internet page text. | ||
247 | 271 | if self.has_run_wizard: | ||
248 | 272 | self.no_internet_label.setText(self.no_internet_text) | ||
249 | 273 | else: | ||
250 | 274 | self.no_internet_label.setText(self.no_internet_text + self.cancel_wizard_text) | ||
256 | 275 | self.application.set_busy_cursor() | 272 | self.application.set_busy_cursor() |
257 | 276 | self._download_index() | 273 | self._download_index() |
258 | 277 | self.application.set_normal_cursor() | 274 | self.application.set_normal_cursor() |
259 | 278 | self.back_button.setVisible(False) | ||
260 | 279 | self.next_button.setVisible(True) | ||
261 | 280 | self.next() | 275 | self.next() |
262 | 281 | elif page_id == FirstTimePage.NoInternet: | 276 | elif page_id == FirstTimePage.NoInternet: |
273 | 282 | self.back_button.setVisible(False) | 277 | next_button.setVisible(False) |
274 | 283 | self.next_button.setVisible(False) | 278 | cancel_button.setVisible(False) |
275 | 284 | self.cancel_button.setVisible(False) | 279 | internet_settings_button.setVisible(True) |
266 | 285 | self.no_internet_finish_button.setVisible(True) | ||
267 | 286 | if self.has_run_wizard: | ||
268 | 287 | self.no_internet_cancel_button.setVisible(False) | ||
269 | 288 | else: | ||
270 | 289 | self.no_internet_cancel_button.setVisible(True) | ||
271 | 290 | elif page_id == FirstTimePage.Plugins: | ||
272 | 291 | self.back_button.setVisible(False) | ||
276 | 292 | elif page_id == FirstTimePage.Progress: | 280 | elif page_id == FirstTimePage.Progress: |
277 | 281 | back_button.setVisible(False) | ||
278 | 282 | next_button.setVisible(False) | ||
279 | 293 | self.application.set_busy_cursor() | 283 | self.application.set_busy_cursor() |
280 | 294 | self._pre_wizard() | 284 | self._pre_wizard() |
281 | 295 | self._perform_wizard() | 285 | self._perform_wizard() |
282 | 296 | self._post_wizard() | 286 | self._post_wizard() |
283 | 297 | self.application.set_normal_cursor() | 287 | self.application.set_normal_cursor() |
284 | 298 | 288 | ||
288 | 299 | def on_cancel_button_clicked(self): | 289 | def accept(self): |
289 | 300 | """ | 290 | """ |
290 | 301 | Process the triggering of the cancel button. | 291 | Called when the user clicks 'Finish'. Reimplement it to to save the plugin status |
291 | 292 | |||
292 | 293 | :rtype: None | ||
293 | 294 | """ | ||
294 | 295 | self._set_plugin_status(self.songs_check_box, 'songs/status') | ||
295 | 296 | self._set_plugin_status(self.bible_check_box, 'bibles/status') | ||
296 | 297 | self._set_plugin_status(self.presentation_check_box, 'presentations/status') | ||
297 | 298 | self._set_plugin_status(self.image_check_box, 'images/status') | ||
298 | 299 | self._set_plugin_status(self.media_check_box, 'media/status') | ||
299 | 300 | self._set_plugin_status(self.custom_check_box, 'custom/status') | ||
300 | 301 | self._set_plugin_status(self.song_usage_check_box, 'songusage/status') | ||
301 | 302 | self._set_plugin_status(self.alert_check_box, 'alerts/status') | ||
302 | 303 | self.screen_selection_widget.save() | ||
303 | 304 | if self.theme_combo_box.currentIndex() != -1: | ||
304 | 305 | Settings().setValue('themes/global theme', self.theme_combo_box.currentText()) | ||
305 | 306 | super().accept() | ||
306 | 307 | |||
307 | 308 | def reject(self): | ||
308 | 309 | """ | ||
309 | 310 | Called when the user clicks the cancel button. Reimplement it to clean up the threads. | ||
310 | 311 | |||
311 | 312 | :rtype: None | ||
312 | 302 | """ | 313 | """ |
313 | 303 | self.was_cancelled = True | 314 | self.was_cancelled = True |
319 | 304 | if self.thumbnail_download_threads: # TODO: Use main thread list | 315 | for thread_name in self.thumbnail_download_threads: |
320 | 305 | for thread_name in self.thumbnail_download_threads: | 316 | worker = get_thread_worker(thread_name) |
321 | 306 | worker = get_thread_worker(thread_name) | 317 | if worker: |
322 | 307 | if worker: | 318 | worker.cancel_download() |
318 | 308 | worker.cancel_download() | ||
323 | 309 | # Was the thread created. | 319 | # Was the thread created. |
324 | 310 | if self.thumbnail_download_threads: | 320 | if self.thumbnail_download_threads: |
325 | 311 | while any([not is_thread_finished(thread_name) for thread_name in self.thumbnail_download_threads]): | 321 | while any([not is_thread_finished(thread_name) for thread_name in self.thumbnail_download_threads]): |
326 | 312 | time.sleep(0.1) | 322 | time.sleep(0.1) |
327 | 313 | self.application.set_normal_cursor() | 323 | self.application.set_normal_cursor() |
328 | 324 | super().reject() | ||
329 | 325 | |||
330 | 326 | def _on_custom_button_clicked(self, which): | ||
331 | 327 | """ | ||
332 | 328 | Slot to handle the a click on one of the wizards custom buttons. | ||
333 | 329 | |||
334 | 330 | :param int QtWidgets.QWizard which: The button pressed | ||
335 | 331 | :rtype: None | ||
336 | 332 | """ | ||
337 | 333 | # Internet settings button | ||
338 | 334 | if which == QtWidgets.QWizard.CustomButton1: | ||
339 | 335 | proxy_dialog = ProxyDialog(self) | ||
340 | 336 | proxy_dialog.retranslate_ui() | ||
341 | 337 | proxy_dialog.exec() | ||
342 | 338 | |||
343 | 339 | def on_projectors_check_box_clicked(self): | ||
344 | 340 | # When clicking projectors_check box, change the visibility setting for Projectors panel. | ||
345 | 341 | if Settings().value('projector/show after wizard'): | ||
346 | 342 | Settings().setValue('projector/show after wizard', False) | ||
347 | 343 | else: | ||
348 | 344 | Settings().setValue('projector/show after wizard', True) | ||
349 | 314 | 345 | ||
350 | 315 | def on_themes_list_widget_selection_changed(self): | 346 | def on_themes_list_widget_selection_changed(self): |
351 | 316 | """ | 347 | """ |
352 | @@ -330,23 +361,6 @@ | |||
353 | 330 | elif not item.isSelected() and cbox_index != -1: | 361 | elif not item.isSelected() and cbox_index != -1: |
354 | 331 | self.theme_combo_box.removeItem(cbox_index) | 362 | self.theme_combo_box.removeItem(cbox_index) |
355 | 332 | 363 | ||
356 | 333 | def on_no_internet_finish_button_clicked(self): | ||
357 | 334 | """ | ||
358 | 335 | Process the triggering of the "Finish" button on the No Internet page. | ||
359 | 336 | """ | ||
360 | 337 | self.application.set_busy_cursor() | ||
361 | 338 | self._perform_wizard() | ||
362 | 339 | self.application.set_normal_cursor() | ||
363 | 340 | Settings().setValue('core/has run wizard', True) | ||
364 | 341 | self.close() | ||
365 | 342 | |||
366 | 343 | def on_no_internet_cancel_button_clicked(self): | ||
367 | 344 | """ | ||
368 | 345 | Process the triggering of the "Cancel" button on the No Internet page. | ||
369 | 346 | """ | ||
370 | 347 | self.was_cancelled = True | ||
371 | 348 | self.close() | ||
372 | 349 | |||
373 | 350 | def update_progress(self, count, block_size): | 364 | def update_progress(self, count, block_size): |
374 | 351 | """ | 365 | """ |
375 | 352 | Calculate and display the download progress. This method is called by download_file(). | 366 | Calculate and display the download progress. This method is called by download_file(). |
376 | @@ -373,7 +387,7 @@ | |||
377 | 373 | Prepare the UI for the process. | 387 | Prepare the UI for the process. |
378 | 374 | """ | 388 | """ |
379 | 375 | self.max_progress = 0 | 389 | self.max_progress = 0 |
381 | 376 | self.finish_button.setVisible(False) | 390 | self.button(QtWidgets.QWizard.FinishButton).setEnabled(False) |
382 | 377 | self.application.process_events() | 391 | self.application.process_events() |
383 | 378 | try: | 392 | try: |
384 | 379 | # Loop through the songs list and increase for each selected item | 393 | # Loop through the songs list and increase for each selected item |
385 | @@ -432,54 +446,31 @@ | |||
386 | 432 | self.progress_bar.setValue(self.progress_bar.maximum()) | 446 | self.progress_bar.setValue(self.progress_bar.maximum()) |
387 | 433 | if self.has_run_wizard: | 447 | if self.has_run_wizard: |
388 | 434 | text = translate('OpenLP.FirstTimeWizard', | 448 | text = translate('OpenLP.FirstTimeWizard', |
392 | 435 | 'Download complete. Click the {button} button to return to OpenLP.' | 449 | 'Download complete. Click the \'{finish_button}\' button to return to OpenLP.') |
390 | 436 | ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) | ||
391 | 437 | self.progress_label.setText(text) | ||
393 | 438 | else: | 450 | else: |
394 | 439 | text = translate('OpenLP.FirstTimeWizard', | 451 | text = translate('OpenLP.FirstTimeWizard', |
398 | 440 | 'Download complete. Click the {button} button to start OpenLP.' | 452 | 'Download complete. Click the \'{finish_button}\' button to start OpenLP.') |
396 | 441 | ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) | ||
397 | 442 | self.progress_label.setText(text) | ||
399 | 443 | else: | 453 | else: |
400 | 444 | if self.has_run_wizard: | 454 | if self.has_run_wizard: |
405 | 445 | text = translate('OpenLP.FirstTimeWizard', | 455 | text = translate('OpenLP.FirstTimeWizard', 'Click the \'{finish_button}\' button to return to OpenLP.') |
402 | 446 | 'Click the {button} button to return to OpenLP.' | ||
403 | 447 | ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) | ||
404 | 448 | self.progress_label.setText(text) | ||
406 | 449 | else: | 456 | else: |
415 | 450 | text = translate('OpenLP.FirstTimeWizard', | 457 | text = translate('OpenLP.FirstTimeWizard', 'Click the \'{finish_button}\' button to start OpenLP.') |
416 | 451 | 'Click the {button} button to start OpenLP.' | 458 | self.progress_label.setText(text.format(finish_button=self.finish_button_text)) |
417 | 452 | ).format(button=clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton))) | 459 | self.button(QtWidgets.QWizard.FinishButton).setEnabled(True) |
418 | 453 | self.progress_label.setText(text) | 460 | self.button(QtWidgets.QWizard.CancelButton).setVisible(False) |
411 | 454 | self.finish_button.setVisible(True) | ||
412 | 455 | self.finish_button.setEnabled(True) | ||
413 | 456 | self.cancel_button.setVisible(False) | ||
414 | 457 | self.next_button.setVisible(False) | ||
419 | 458 | self.application.process_events() | 461 | self.application.process_events() |
420 | 459 | 462 | ||
421 | 460 | def _perform_wizard(self): | 463 | def _perform_wizard(self): |
422 | 461 | """ | 464 | """ |
423 | 462 | Run the tasks in the wizard. | 465 | Run the tasks in the wizard. |
424 | 463 | """ | 466 | """ |
435 | 464 | # Set plugin states | 467 | |
426 | 465 | self._increment_progress_bar(translate('OpenLP.FirstTimeWizard', 'Enabling selected plugins...')) | ||
427 | 466 | self._set_plugin_status(self.songs_check_box, 'songs/status') | ||
428 | 467 | self._set_plugin_status(self.bible_check_box, 'bibles/status') | ||
429 | 468 | self._set_plugin_status(self.presentation_check_box, 'presentations/status') | ||
430 | 469 | self._set_plugin_status(self.image_check_box, 'images/status') | ||
431 | 470 | self._set_plugin_status(self.media_check_box, 'media/status') | ||
432 | 471 | self._set_plugin_status(self.custom_check_box, 'custom/status') | ||
433 | 472 | self._set_plugin_status(self.song_usage_check_box, 'songusage/status') | ||
434 | 473 | self._set_plugin_status(self.alert_check_box, 'alerts/status') | ||
436 | 474 | if self.web_access: | 468 | if self.web_access: |
437 | 475 | if not self._download_selected(): | 469 | if not self._download_selected(): |
438 | 476 | critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'), | 470 | critical_error_message_box(translate('OpenLP.FirstTimeWizard', 'Download Error'), |
439 | 477 | translate('OpenLP.FirstTimeWizard', 'There was a connection problem while ' | 471 | translate('OpenLP.FirstTimeWizard', 'There was a connection problem while ' |
440 | 478 | 'downloading, so further downloads will be skipped. Try to re-run ' | 472 | 'downloading, so further downloads will be skipped. Try to re-run ' |
441 | 479 | 'the First Time Wizard later.')) | 473 | 'the First Time Wizard later.')) |
442 | 480 | self.screen_selection_widget.save() | ||
443 | 481 | if self.theme_combo_box.currentIndex() != -1: | ||
444 | 482 | Settings().setValue('themes/global theme', self.theme_combo_box.currentText()) | ||
445 | 483 | 474 | ||
446 | 484 | def _download_selected(self): | 475 | def _download_selected(self): |
447 | 485 | """ | 476 | """ |
448 | 486 | 477 | ||
449 | === modified file 'openlp/core/ui/firsttimewizard.py' | |||
450 | --- openlp/core/ui/firsttimewizard.py 2019-02-15 22:34:53 +0000 | |||
451 | +++ openlp/core/ui/firsttimewizard.py 2019-03-15 20:50:11 +0000 | |||
452 | @@ -26,7 +26,6 @@ | |||
453 | 26 | 26 | ||
454 | 27 | from openlp.core.common import clean_button_text, is_macosx | 27 | from openlp.core.common import clean_button_text, is_macosx |
455 | 28 | from openlp.core.common.i18n import translate | 28 | from openlp.core.common.i18n import translate |
456 | 29 | from openlp.core.common.settings import Settings | ||
457 | 30 | from openlp.core.lib.ui import add_welcome_page | 29 | from openlp.core.lib.ui import add_welcome_page |
458 | 31 | from openlp.core.ui.icons import UiIcons | 30 | from openlp.core.ui.icons import UiIcons |
459 | 32 | 31 | ||
460 | @@ -39,14 +38,15 @@ | |||
461 | 39 | An enumeration class with each of the pages of the wizard. | 38 | An enumeration class with each of the pages of the wizard. |
462 | 40 | """ | 39 | """ |
463 | 41 | Welcome = 0 | 40 | Welcome = 0 |
472 | 42 | ScreenConfig = 1 | 41 | Plugins = 1 |
473 | 43 | Download = 2 | 42 | ScreenConfig = 2 |
474 | 44 | NoInternet = 3 | 43 | SampleOption = 3 |
475 | 45 | Plugins = 4 | 44 | Download = 4 |
476 | 46 | Songs = 5 | 45 | NoInternet = 5 |
477 | 47 | Bibles = 6 | 46 | Songs = 6 |
478 | 48 | Themes = 7 | 47 | Bibles = 7 |
479 | 49 | Progress = 8 | 48 | Themes = 8 |
480 | 49 | Progress = 9 | ||
481 | 50 | 50 | ||
482 | 51 | 51 | ||
483 | 52 | class ThemeListWidget(QtWidgets.QListWidget): | 52 | class ThemeListWidget(QtWidgets.QListWidget): |
484 | @@ -97,20 +97,13 @@ | |||
485 | 97 | first_time_wizard.resize(550, 386) | 97 | first_time_wizard.resize(550, 386) |
486 | 98 | first_time_wizard.setModal(True) | 98 | first_time_wizard.setModal(True) |
487 | 99 | first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage | | 99 | first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage | |
490 | 100 | QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1 | | 100 | QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1) |
489 | 101 | QtWidgets.QWizard.HaveCustomButton2) | ||
491 | 102 | if is_macosx(): # pragma: nocover | 101 | if is_macosx(): # pragma: nocover |
492 | 103 | first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap, | 102 | first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap, |
493 | 104 | QtGui.QPixmap(':/wizards/openlp-osx-wizard.png')) | 103 | QtGui.QPixmap(':/wizards/openlp-osx-wizard.png')) |
494 | 105 | first_time_wizard.resize(634, 386) | 104 | first_time_wizard.resize(634, 386) |
495 | 106 | else: | 105 | else: |
496 | 107 | first_time_wizard.setWizardStyle(QtWidgets.QWizard.ModernStyle) | 106 | first_time_wizard.setWizardStyle(QtWidgets.QWizard.ModernStyle) |
497 | 108 | self.finish_button = self.button(QtWidgets.QWizard.FinishButton) | ||
498 | 109 | self.no_internet_finish_button = self.button(QtWidgets.QWizard.CustomButton1) | ||
499 | 110 | self.cancel_button = self.button(QtWidgets.QWizard.CancelButton) | ||
500 | 111 | self.no_internet_cancel_button = self.button(QtWidgets.QWizard.CustomButton2) | ||
501 | 112 | self.next_button = self.button(QtWidgets.QWizard.NextButton) | ||
502 | 113 | self.back_button = self.button(QtWidgets.QWizard.BackButton) | ||
503 | 114 | add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp') | 107 | add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp') |
504 | 115 | # The screen config page | 108 | # The screen config page |
505 | 116 | self.screen_page = QtWidgets.QWizardPage() | 109 | self.screen_page = QtWidgets.QWizardPage() |
506 | @@ -121,6 +114,18 @@ | |||
507 | 121 | self.screen_selection_widget.load() | 114 | self.screen_selection_widget.load() |
508 | 122 | self.screen_page_layout.addRow(self.screen_selection_widget) | 115 | self.screen_page_layout.addRow(self.screen_selection_widget) |
509 | 123 | first_time_wizard.setPage(FirstTimePage.ScreenConfig, self.screen_page) | 116 | first_time_wizard.setPage(FirstTimePage.ScreenConfig, self.screen_page) |
510 | 117 | # Download Samples page | ||
511 | 118 | self.resource_page = QtWidgets.QWizardPage() | ||
512 | 119 | self.resource_page.setObjectName('resource_page') | ||
513 | 120 | self.resource_page.setFinalPage(True) | ||
514 | 121 | self.resource_layout = QtWidgets.QVBoxLayout(self.resource_page) | ||
515 | 122 | self.resource_layout.setContentsMargins(50, 20, 50, 20) | ||
516 | 123 | self.resource_layout.setObjectName('resource_layout') | ||
517 | 124 | self.resource_label = QtWidgets.QLabel(self.resource_page) | ||
518 | 125 | self.resource_label.setObjectName('resource_label') | ||
519 | 126 | self.resource_label.setWordWrap(True) | ||
520 | 127 | self.resource_layout.addWidget(self.resource_label) | ||
521 | 128 | first_time_wizard.setPage(FirstTimePage.SampleOption, self.resource_page) | ||
522 | 124 | # The download page | 129 | # The download page |
523 | 125 | self.download_page = QtWidgets.QWizardPage() | 130 | self.download_page = QtWidgets.QWizardPage() |
524 | 126 | self.download_page.setObjectName('download_page') | 131 | self.download_page.setObjectName('download_page') |
525 | @@ -134,6 +139,7 @@ | |||
526 | 134 | # The "you don't have an internet connection" page. | 139 | # The "you don't have an internet connection" page. |
527 | 135 | self.no_internet_page = QtWidgets.QWizardPage() | 140 | self.no_internet_page = QtWidgets.QWizardPage() |
528 | 136 | self.no_internet_page.setObjectName('no_internet_page') | 141 | self.no_internet_page.setObjectName('no_internet_page') |
529 | 142 | self.no_internet_page.setFinalPage(True) | ||
530 | 137 | self.no_internet_layout = QtWidgets.QVBoxLayout(self.no_internet_page) | 143 | self.no_internet_layout = QtWidgets.QVBoxLayout(self.no_internet_page) |
531 | 138 | self.no_internet_layout.setContentsMargins(50, 30, 50, 40) | 144 | self.no_internet_layout.setContentsMargins(50, 30, 50, 40) |
532 | 139 | self.no_internet_layout.setObjectName('no_internet_layout') | 145 | self.no_internet_layout.setObjectName('no_internet_layout') |
533 | @@ -242,27 +248,32 @@ | |||
534 | 242 | self.progress_bar.setObjectName('progress_bar') | 248 | self.progress_bar.setObjectName('progress_bar') |
535 | 243 | self.progress_layout.addWidget(self.progress_bar) | 249 | self.progress_layout.addWidget(self.progress_bar) |
536 | 244 | first_time_wizard.setPage(FirstTimePage.Progress, self.progress_page) | 250 | first_time_wizard.setPage(FirstTimePage.Progress, self.progress_page) |
538 | 245 | self.retranslate_ui(first_time_wizard) | 251 | self.retranslate_ui() |
539 | 246 | 252 | ||
541 | 247 | def retranslate_ui(self, first_time_wizard): | 253 | def retranslate_ui(self): |
542 | 248 | """ | 254 | """ |
543 | 249 | Translate the UI on the fly | 255 | Translate the UI on the fly |
544 | 250 | 256 | ||
545 | 251 | :param first_time_wizard: The wizard form | 257 | :param first_time_wizard: The wizard form |
546 | 252 | """ | 258 | """ |
548 | 253 | first_time_wizard.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard')) | 259 | self.finish_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.FinishButton)) |
549 | 260 | back_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.BackButton)) | ||
550 | 261 | next_button_text = clean_button_text(self.buttonText(QtWidgets.QWizard.NextButton)) | ||
551 | 262 | |||
552 | 263 | self.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'First Time Wizard')) | ||
553 | 254 | text = translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard') | 264 | text = translate('OpenLP.FirstTimeWizard', 'Welcome to the First Time Wizard') |
558 | 255 | first_time_wizard.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}' | 265 | self.title_label.setText('<span style="font-size:14pt; font-weight:600;">{text}</span>'.format(text=text)) |
559 | 256 | '</span>'.format(text=text)) | 266 | self.information_label.setText( |
556 | 257 | button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.NextButton)) | ||
557 | 258 | first_time_wizard.information_label.setText( | ||
560 | 259 | translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. ' | 267 | translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. ' |
562 | 260 | 'Click the {button} button below to start.').format(button=button)) | 268 | 'Click the \'{next_button}\' button below to start.' |
563 | 269 | ).format(next_button=next_button_text)) | ||
564 | 270 | self.setButtonText( | ||
565 | 271 | QtWidgets.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Internet Settings')) | ||
566 | 261 | self.download_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading Resource Index')) | 272 | self.download_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading Resource Index')) |
571 | 262 | self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while the resource index is ' | 273 | self.download_page.setSubTitle(translate('OpenLP.FirstTimeWizard', |
572 | 263 | 'downloaded.')) | 274 | 'Please wait while the resource index is downloaded.')) |
573 | 264 | self.download_label.setText(translate('OpenLP.FirstTimeWizard', 'Please wait while OpenLP downloads the ' | 275 | self.download_label.setText(translate('OpenLP.FirstTimeWizard', |
574 | 265 | 'resource index file...')) | 276 | 'Please wait while OpenLP downloads the resource index file...')) |
575 | 266 | self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Select parts of the program you wish to use')) | 277 | self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Select parts of the program you wish to use')) |
576 | 267 | self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard', | 278 | self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard', |
577 | 268 | 'You can also change these settings after the Wizard.')) | 279 | 'You can also change these settings after the Wizard.')) |
578 | @@ -270,11 +281,10 @@ | |||
579 | 270 | self.screen_page.setSubTitle(translate('OpenLP.FirstTimeWizard', | 281 | self.screen_page.setSubTitle(translate('OpenLP.FirstTimeWizard', |
580 | 271 | 'Choose the main display screen for OpenLP.')) | 282 | 'Choose the main display screen for OpenLP.')) |
581 | 272 | self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs')) | 283 | self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs')) |
587 | 273 | self.custom_check_box.setText(translate('OpenLP.FirstTimeWizard', | 284 | self.custom_check_box.setText( |
588 | 274 | 'Custom Slides – Easier to manage than songs and they have their own' | 285 | translate('OpenLP.FirstTimeWizard', |
589 | 275 | ' list of slides')) | 286 | 'Custom Slides – Easier to manage than songs and they have their own list of slides')) |
590 | 276 | self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', | 287 | self.bible_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Bibles – Import and show Bibles')) |
586 | 277 | 'Bibles – Import and show Bibles')) | ||
591 | 278 | self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', | 288 | self.image_check_box.setText(translate('OpenLP.FirstTimeWizard', |
592 | 279 | 'Images – Show images or replace background with them')) | 289 | 'Images – Show images or replace background with them')) |
593 | 280 | self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', | 290 | self.presentation_check_box.setText(translate('OpenLP.FirstTimeWizard', |
594 | @@ -283,22 +293,25 @@ | |||
595 | 283 | self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Song Usage Monitor')) | 293 | self.song_usage_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Song Usage Monitor')) |
596 | 284 | self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard', | 294 | self.alert_check_box.setText(translate('OpenLP.FirstTimeWizard', |
597 | 285 | 'Alerts – Display informative messages while showing other slides')) | 295 | 'Alerts – Display informative messages while showing other slides')) |
598 | 296 | self.resource_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Resource Data')) | ||
599 | 297 | self.resource_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Can OpenLP download some resource data?')) | ||
600 | 298 | self.resource_label.setText( | ||
601 | 299 | translate('OpenLP.FirstTimeWizard', | ||
602 | 300 | 'OpenLP has collected some resources that we have permission to distribute.\n\n' | ||
603 | 301 | 'If you would like to download some of these resources click the \'{next_button}\' button, ' | ||
604 | 302 | 'otherwise click the \'{finish_button}\' button.' | ||
605 | 303 | ).format(next_button=next_button_text, finish_button=self.finish_button_text)) | ||
606 | 286 | self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection')) | 304 | self.no_internet_page.setTitle(translate('OpenLP.FirstTimeWizard', 'No Internet Connection')) |
622 | 287 | self.no_internet_page.setSubTitle( | 305 | self.no_internet_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Cannot connect to the internet.')) |
623 | 288 | translate('OpenLP.FirstTimeWizard', 'Unable to detect an Internet connection.')) | 306 | self.no_internet_label.setText( |
624 | 289 | button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton)) | 307 | translate('OpenLP.FirstTimeWizard', |
625 | 290 | self.no_internet_text = translate('OpenLP.FirstTimeWizard', | 308 | 'OpenLP could not connect to the internet to get information about the sample data available.\n\n' |
626 | 291 | 'No Internet connection was found. The First Time Wizard needs an Internet ' | 309 | 'Please check your internet connection. If your church uses a proxy server click the ' |
627 | 292 | 'connection in order to be able to download sample songs, Bibles and themes.' | 310 | '\'Internet Settings\' button below and enter the server details there.\n\nClick the ' |
628 | 293 | ' Click the {button} button now to start OpenLP with initial settings and ' | 311 | '\'{back_button}\' button to try again.\n\nIf you click the \'{finish_button}\' ' |
629 | 294 | 'no sample data.\n\nTo re-run the First Time Wizard and import this sample ' | 312 | 'button you can download the data at a later time by selecting \'Re-run First Time Wizard\' ' |
630 | 295 | 'data at a later time, check your Internet connection and re-run this ' | 313 | 'from the \'Tools\' menu in OpenLP.' |
631 | 296 | 'wizard by selecting "Tools/Re-run First Time Wizard" from OpenLP.' | 314 | ).format(back_button=back_button_text, finish_button=self.finish_button_text)) |
617 | 297 | ).format(button=button) | ||
618 | 298 | button = clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton)) | ||
619 | 299 | self.cancel_wizard_text = translate('OpenLP.FirstTimeWizard', | ||
620 | 300 | '\n\nTo cancel the First Time Wizard completely (and not start OpenLP), ' | ||
621 | 301 | 'click the {button} button now.').format(button=button) | ||
632 | 302 | self.songs_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs')) | 315 | self.songs_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs')) |
633 | 303 | self.songs_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.')) | 316 | self.songs_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.')) |
634 | 304 | self.bibles_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles')) | 317 | self.bibles_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles')) |
635 | @@ -310,17 +323,5 @@ | |||
636 | 310 | self.themes_select_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Select all')) | 323 | self.themes_select_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Select all')) |
637 | 311 | self.themes_deselect_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Deselect all')) | 324 | self.themes_deselect_all_button.setToolTip(translate('OpenLP.FirstTimeWizard', 'Deselect all')) |
638 | 312 | self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading and Configuring')) | 325 | self.progress_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Downloading and Configuring')) |
653 | 313 | self.progress_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded ' | 326 | self.progress_page.setSubTitle( |
654 | 314 | 'and OpenLP is configured.')) | 327 | translate('OpenLP.FirstTimeWizard', 'Please wait while resources are downloaded and OpenLP is configured.')) |
641 | 315 | self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...')) | ||
642 | 316 | first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton1, | ||
643 | 317 | clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.FinishButton))) | ||
644 | 318 | first_time_wizard.setButtonText(QtWidgets.QWizard.CustomButton2, | ||
645 | 319 | clean_button_text(first_time_wizard.buttonText(QtWidgets.QWizard.CancelButton))) | ||
646 | 320 | |||
647 | 321 | def on_projectors_check_box_clicked(self): | ||
648 | 322 | # When clicking projectors_check box, change the visibility setting for Projectors panel. | ||
649 | 323 | if Settings().value('projector/show after wizard'): | ||
650 | 324 | Settings().setValue('projector/show after wizard', False) | ||
651 | 325 | else: | ||
652 | 326 | Settings().setValue('projector/show after wizard', True) | ||
655 | 327 | 328 | ||
656 | === modified file 'openlp/core/ui/mainwindow.py' | |||
657 | --- openlp/core/ui/mainwindow.py 2019-02-14 15:09:09 +0000 | |||
658 | +++ openlp/core/ui/mainwindow.py 2019-03-15 20:50:11 +0000 | |||
659 | @@ -681,8 +681,7 @@ | |||
660 | 681 | return | 681 | return |
661 | 682 | first_run_wizard = FirstTimeForm(self) | 682 | first_run_wizard = FirstTimeForm(self) |
662 | 683 | first_run_wizard.initialize(ScreenList()) | 683 | first_run_wizard.initialize(ScreenList()) |
665 | 684 | first_run_wizard.exec() | 684 | if first_run_wizard.exec() == QtWidgets.QDialog.Rejected: |
664 | 685 | if first_run_wizard.was_cancelled: | ||
666 | 686 | return | 685 | return |
667 | 687 | self.application.set_busy_cursor() | 686 | self.application.set_busy_cursor() |
668 | 688 | self.first_time() | 687 | self.first_time() |
669 | 689 | 688 | ||
670 | === modified file 'openlp/core/widgets/widgets.py' | |||
671 | --- openlp/core/widgets/widgets.py 2019-02-14 15:09:09 +0000 | |||
672 | +++ openlp/core/widgets/widgets.py 2019-03-15 20:50:11 +0000 | |||
673 | @@ -150,6 +150,34 @@ | |||
674 | 150 | settings.setValue('advanced/proxy password', self.password_edit.text()) | 150 | settings.setValue('advanced/proxy password', self.password_edit.text()) |
675 | 151 | 151 | ||
676 | 152 | 152 | ||
677 | 153 | class ProxyDialog(QtWidgets.QDialog): | ||
678 | 154 | """ | ||
679 | 155 | A basic dialog to show proxy settingd | ||
680 | 156 | """ | ||
681 | 157 | def __init__(self, *args, **kwargs): | ||
682 | 158 | super().__init__(*args, **kwargs) | ||
683 | 159 | self.layout = QtWidgets.QVBoxLayout(self) | ||
684 | 160 | self.proxy_widget = ProxyWidget(self) | ||
685 | 161 | self.layout.addWidget(self.proxy_widget) | ||
686 | 162 | self.button_box = \ | ||
687 | 163 | QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self) | ||
688 | 164 | self.layout.addWidget(self.button_box) | ||
689 | 165 | self.button_box.accepted.connect(self.accept) | ||
690 | 166 | self.button_box.rejected.connect(self.reject) | ||
691 | 167 | |||
692 | 168 | def accept(self): | ||
693 | 169 | """ | ||
694 | 170 | Reimplement the the accept slot so that the ProxyWidget settings can be saved. | ||
695 | 171 | :rtype: None | ||
696 | 172 | """ | ||
697 | 173 | self.proxy_widget.save() | ||
698 | 174 | super().accept() | ||
699 | 175 | |||
700 | 176 | def retranslate_ui(self): | ||
701 | 177 | self.proxy_widget.retranslate_ui() | ||
702 | 178 | self.setWindowTitle(translate('OpenLP.ProxyDialog', 'Proxy Server Settings')) | ||
703 | 179 | |||
704 | 180 | |||
705 | 153 | class ScreenButton(QtWidgets.QPushButton): | 181 | class ScreenButton(QtWidgets.QPushButton): |
706 | 154 | """ | 182 | """ |
707 | 155 | A special button class that holds the screen information about it | 183 | A special button class that holds the screen information about it |
708 | 156 | 184 | ||
709 | === modified file 'openlp/plugins/songs/lib/importers/openlp.py' | |||
710 | --- openlp/plugins/songs/lib/importers/openlp.py 2019-02-14 15:09:09 +0000 | |||
711 | +++ openlp/plugins/songs/lib/importers/openlp.py 2019-03-15 20:50:11 +0000 | |||
712 | @@ -106,7 +106,7 @@ | |||
713 | 106 | pass | 106 | pass |
714 | 107 | 107 | ||
715 | 108 | # Check the file type | 108 | # Check the file type |
717 | 109 | if not isinstance(self.import_source, str) or not self.import_source.endswith('.sqlite'): | 109 | if self.import_source.suffix != '.sqlite': |
718 | 110 | self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport', | 110 | self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport', |
719 | 111 | 'Not a valid OpenLP 2 song database.')) | 111 | 'Not a valid OpenLP 2 song database.')) |
720 | 112 | return | 112 | return |
721 | 113 | 113 | ||
722 | === modified file 'openlp/plugins/songs/reporting.py' | |||
723 | --- openlp/plugins/songs/reporting.py 2019-02-14 15:09:09 +0000 | |||
724 | +++ openlp/plugins/songs/reporting.py 2019-03-15 20:50:11 +0000 | |||
725 | @@ -49,13 +49,6 @@ | |||
726 | 49 | Path(translate('SongPlugin.ReportSongList', 'song_extract.csv')), | 49 | Path(translate('SongPlugin.ReportSongList', 'song_extract.csv')), |
727 | 50 | translate('SongPlugin.ReportSongList', 'CSV format (*.csv)')) | 50 | translate('SongPlugin.ReportSongList', 'CSV format (*.csv)')) |
728 | 51 | 51 | ||
729 | 52 | if report_file_path is None: | ||
730 | 53 | main_window.error_message( | ||
731 | 54 | translate('SongPlugin.ReportSongList', 'Output Path Not Selected'), | ||
732 | 55 | translate('SongPlugin.ReportSongList', 'You have not set a valid output location for your report. \n' | ||
733 | 56 | 'Please select an existing path on your computer.') | ||
734 | 57 | ) | ||
735 | 58 | return | ||
736 | 59 | report_file_path.with_suffix('.csv') | 52 | report_file_path.with_suffix('.csv') |
737 | 60 | Registry().get('application').set_busy_cursor() | 53 | Registry().get('application').set_busy_cursor() |
738 | 61 | try: | 54 | try: |
739 | 62 | 55 | ||
740 | === modified file 'run_openlp.py' (properties changed: +x to -x) | |||
741 | === modified file 'setup.py' (properties changed: +x to -x) | |||
742 | === modified file 'tests/functional/openlp_core/common/test_httputils.py' | |||
743 | --- tests/functional/openlp_core/common/test_httputils.py 2019-02-14 15:09:09 +0000 | |||
744 | +++ tests/functional/openlp_core/common/test_httputils.py 2019-03-15 20:50:11 +0000 | |||
745 | @@ -224,7 +224,7 @@ | |||
746 | 224 | file_size = get_url_file_size(fake_url) | 224 | file_size = get_url_file_size(fake_url) |
747 | 225 | 225 | ||
748 | 226 | # THEN: The correct methods are called with the correct arguments and a web page is returned | 226 | # THEN: The correct methods are called with the correct arguments and a web page is returned |
750 | 227 | mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, timeout=30.0) | 227 | mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, proxies=None, timeout=30.0) |
751 | 228 | assert file_size == 100 | 228 | assert file_size == 100 |
752 | 229 | 229 | ||
753 | 230 | @patch('openlp.core.common.httputils.requests') | 230 | @patch('openlp.core.common.httputils.requests') |
754 | @@ -249,34 +249,30 @@ | |||
755 | 249 | self.addCleanup(self.destroy_settings) | 249 | self.addCleanup(self.destroy_settings) |
756 | 250 | 250 | ||
757 | 251 | @patch('openlp.core.common.httputils.Settings') | 251 | @patch('openlp.core.common.httputils.Settings') |
759 | 252 | def test_mode_arg_specified(self, MockSettings): | 252 | def test_mode_arg_specified(self, mocked_settings): |
760 | 253 | """ | 253 | """ |
761 | 254 | Test that the argument is used rather than reading the 'advanced/proxy mode' setting | 254 | Test that the argument is used rather than reading the 'advanced/proxy mode' setting |
762 | 255 | """ | 255 | """ |
763 | 256 | # GIVEN: Mocked settings | 256 | # GIVEN: Mocked settings |
764 | 257 | mocked_settings = MagicMock() | ||
765 | 258 | MockSettings.return_value = mocked_settings | ||
766 | 259 | 257 | ||
767 | 260 | # WHEN: Calling `get_proxy_settings` with the mode arg specified | 258 | # WHEN: Calling `get_proxy_settings` with the mode arg specified |
768 | 261 | get_proxy_settings(mode=ProxyMode.NO_PROXY) | 259 | get_proxy_settings(mode=ProxyMode.NO_PROXY) |
769 | 262 | 260 | ||
770 | 263 | # THEN: The mode arg should have been used rather than looking it up in the settings | 261 | # THEN: The mode arg should have been used rather than looking it up in the settings |
772 | 264 | mocked_settings.value.assert_not_called() | 262 | mocked_settings().value.assert_not_called() |
773 | 265 | 263 | ||
774 | 266 | @patch('openlp.core.common.httputils.Settings') | 264 | @patch('openlp.core.common.httputils.Settings') |
776 | 267 | def test_mode_incorrect_arg_specified(self, MockSettings): | 265 | def test_mode_incorrect_arg_specified(self, mocked_settings): |
777 | 268 | """ | 266 | """ |
778 | 269 | Test that the system settings are used when the mode arg specieied is invalid | 267 | Test that the system settings are used when the mode arg specieied is invalid |
779 | 270 | """ | 268 | """ |
780 | 271 | # GIVEN: Mocked settings | 269 | # GIVEN: Mocked settings |
781 | 272 | mocked_settings = MagicMock() | ||
782 | 273 | MockSettings.return_value = mocked_settings | ||
783 | 274 | 270 | ||
784 | 275 | # WHEN: Calling `get_proxy_settings` with an invalid mode arg specified | 271 | # WHEN: Calling `get_proxy_settings` with an invalid mode arg specified |
785 | 276 | result = get_proxy_settings(mode='qwerty') | 272 | result = get_proxy_settings(mode='qwerty') |
786 | 277 | 273 | ||
787 | 278 | # THEN: An None should be returned | 274 | # THEN: An None should be returned |
789 | 279 | mocked_settings.value.assert_not_called() | 275 | mocked_settings().value.assert_not_called() |
790 | 280 | assert result is None | 276 | assert result is None |
791 | 281 | 277 | ||
792 | 282 | def test_no_proxy_mode(self): | 278 | def test_no_proxy_mode(self): |
793 | 283 | 279 | ||
794 | === modified file 'tests/functional/openlp_core/test_threading.py' | |||
795 | --- tests/functional/openlp_core/test_threading.py 2019-02-14 15:09:09 +0000 | |||
796 | +++ tests/functional/openlp_core/test_threading.py 2019-03-15 20:50:11 +0000 | |||
797 | @@ -133,15 +133,11 @@ | |||
798 | 133 | # GIVEN: A mocked thread worker | 133 | # GIVEN: A mocked thread worker |
799 | 134 | MockRegistry.return_value.get.return_value.worker_threads = {} | 134 | MockRegistry.return_value.get.return_value.worker_threads = {} |
800 | 135 | 135 | ||
810 | 136 | try: | 136 | # WHEN: get_thread_worker() is called |
811 | 137 | # WHEN: get_thread_worker() is called | 137 | result = get_thread_worker('test_thread') |
812 | 138 | get_thread_worker('test_thread') | 138 | |
813 | 139 | assert False, 'A KeyError should have been raised' | 139 | # THEN: None should have been returned |
814 | 140 | except KeyError: | 140 | assert result is None |
806 | 141 | # THEN: The mocked worker is returned | ||
807 | 142 | pass | ||
808 | 143 | except Exception: | ||
809 | 144 | assert False, 'A KeyError should have been raised' | ||
815 | 145 | 141 | ||
816 | 146 | 142 | ||
817 | 147 | @patch('openlp.core.threading.Registry') | 143 | @patch('openlp.core.threading.Registry') |
818 | 148 | 144 | ||
819 | === modified file 'tests/functional/openlp_core/ui/test_firsttimeform.py' | |||
820 | --- tests/functional/openlp_core/ui/test_firsttimeform.py 2019-02-16 08:57:11 +0000 | |||
821 | +++ tests/functional/openlp_core/ui/test_firsttimeform.py 2019-03-15 20:50:11 +0000 | |||
822 | @@ -27,6 +27,8 @@ | |||
823 | 27 | from unittest import TestCase | 27 | from unittest import TestCase |
824 | 28 | from unittest.mock import MagicMock, call, patch, DEFAULT | 28 | from unittest.mock import MagicMock, call, patch, DEFAULT |
825 | 29 | 29 | ||
826 | 30 | from PyQt5 import QtWidgets | ||
827 | 31 | |||
828 | 30 | from openlp.core.common.path import Path | 32 | from openlp.core.common.path import Path |
829 | 31 | from openlp.core.common.registry import Registry | 33 | from openlp.core.common.registry import Registry |
830 | 32 | from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem | 34 | from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem |
831 | @@ -45,6 +47,7 @@ | |||
832 | 45 | """ | 47 | """ |
833 | 46 | Test the :class:`ThemeListWidgetItem` class | 48 | Test the :class:`ThemeListWidgetItem` class |
834 | 47 | """ | 49 | """ |
835 | 50 | |||
836 | 48 | def setUp(self): | 51 | def setUp(self): |
837 | 49 | self.sample_theme_data = {'file_name': 'BlueBurst.otz', 'sha256': 'sha_256_hash', | 52 | self.sample_theme_data = {'file_name': 'BlueBurst.otz', 'sha256': 'sha_256_hash', |
838 | 50 | 'thumbnail': 'BlueBurst.png', 'title': 'Blue Burst'} | 53 | 'thumbnail': 'BlueBurst.png', 'title': 'Blue Burst'} |
839 | @@ -59,7 +62,7 @@ | |||
840 | 59 | """ | 62 | """ |
841 | 60 | Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated | 63 | Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated |
842 | 61 | """ | 64 | """ |
844 | 62 | # GIVEN: A sample theme dictanary object | 65 | # GIVEN: A sample theme dictionary object |
845 | 63 | # WHEN: Creating an instance of `ThemeListWidgetItem` | 66 | # WHEN: Creating an instance of `ThemeListWidgetItem` |
846 | 64 | instance = ThemeListWidgetItem('url', self.sample_theme_data, MagicMock()) | 67 | instance = ThemeListWidgetItem('url', self.sample_theme_data, MagicMock()) |
847 | 65 | 68 | ||
848 | @@ -74,7 +77,7 @@ | |||
849 | 74 | """ | 77 | """ |
850 | 75 | Test that the `DownloadWorker` worker is set up correctly and that the thread is started. | 78 | Test that the `DownloadWorker` worker is set up correctly and that the thread is started. |
851 | 76 | """ | 79 | """ |
853 | 77 | # GIVEN: A sample theme dictanary object | 80 | # GIVEN: A sample theme dictionary object |
854 | 78 | mocked_ftw = MagicMock(spec=FirstTimeForm) | 81 | mocked_ftw = MagicMock(spec=FirstTimeForm) |
855 | 79 | mocked_ftw.thumbnail_download_threads = [] | 82 | mocked_ftw.thumbnail_download_threads = [] |
856 | 80 | 83 | ||
857 | @@ -120,10 +123,23 @@ | |||
858 | 120 | # THEN: The screens should be set up, and the default values initialised | 123 | # THEN: The screens should be set up, and the default values initialised |
859 | 121 | assert expected_screens == frw.screens, 'The screens should be correct' | 124 | assert expected_screens == frw.screens, 'The screens should be correct' |
860 | 122 | assert frw.web_access is True, 'The default value of self.web_access should be True' | 125 | assert frw.web_access is True, 'The default value of self.web_access should be True' |
861 | 123 | assert frw.was_cancelled is False, 'The default value of self.was_cancelled should be False' | ||
862 | 124 | assert [] == frw.thumbnail_download_threads, 'The list of threads should be empty' | 126 | assert [] == frw.thumbnail_download_threads, 'The list of threads should be empty' |
863 | 125 | assert frw.has_run_wizard is False, 'has_run_wizard should be False' | 127 | assert frw.has_run_wizard is False, 'has_run_wizard should be False' |
864 | 126 | 128 | ||
865 | 129 | @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.exec') | ||
866 | 130 | def test_exec(self, mocked_qwizard_exec): | ||
867 | 131 | |||
868 | 132 | # GIVEN: An instance of FirstTimeForm | ||
869 | 133 | frw = FirstTimeForm(None) | ||
870 | 134 | with patch.object(frw, 'set_defaults') as mocked_set_defaults: | ||
871 | 135 | |||
872 | 136 | # WHEN: exec is called | ||
873 | 137 | frw.exec() | ||
874 | 138 | |||
875 | 139 | # THEN: The wizard should be reset and the exec methon on the super class should have been called | ||
876 | 140 | mocked_set_defaults.assert_called_once() | ||
877 | 141 | mocked_qwizard_exec.assert_called_once() | ||
878 | 142 | |||
879 | 127 | def test_set_defaults(self): | 143 | def test_set_defaults(self): |
880 | 128 | """ | 144 | """ |
881 | 129 | Test that the default values are set when set_defaults() is run | 145 | Test that the default values are set when set_defaults() is run |
882 | @@ -134,8 +150,6 @@ | |||
883 | 134 | mocked_settings = MagicMock() | 150 | mocked_settings = MagicMock() |
884 | 135 | mocked_settings.value.side_effect = lambda key: {'core/has run wizard': False}[key] | 151 | mocked_settings.value.side_effect = lambda key: {'core/has run wizard': False}[key] |
885 | 136 | with patch.object(frw, 'restart') as mocked_restart, \ | 152 | with patch.object(frw, 'restart') as mocked_restart, \ |
886 | 137 | patch.object(frw, 'cancel_button') as mocked_cancel_button, \ | ||
887 | 138 | patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \ | ||
888 | 139 | patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \ | 153 | patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \ |
889 | 140 | patch.object(frw, 'theme_combo_box') as mocked_theme_combo_box, \ | 154 | patch.object(frw, 'theme_combo_box') as mocked_theme_combo_box, \ |
890 | 141 | patch.object(frw, 'songs_check_box') as mocked_songs_check_box, \ | 155 | patch.object(frw, 'songs_check_box') as mocked_songs_check_box, \ |
891 | @@ -153,12 +167,8 @@ | |||
892 | 153 | # THEN: The default values should have been set | 167 | # THEN: The default values should have been set |
893 | 154 | mocked_restart.assert_called_once() | 168 | mocked_restart.assert_called_once() |
894 | 155 | assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set' | 169 | assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set' |
895 | 156 | mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked) | ||
896 | 157 | mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with( | ||
897 | 158 | frw.on_no_internet_finish_button_clicked) | ||
898 | 159 | mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed) | 170 | mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed) |
899 | 160 | mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load) | 171 | mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load) |
900 | 161 | mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False) | ||
901 | 162 | mocked_settings.value.assert_has_calls([call('core/has run wizard')]) | 172 | mocked_settings.value.assert_has_calls([call('core/has run wizard')]) |
902 | 163 | mocked_gettempdir.assert_called_once() | 173 | mocked_gettempdir.assert_called_once() |
903 | 164 | mocked_create_paths.assert_called_once_with(Path('temp', 'openlp')) | 174 | mocked_create_paths.assert_called_once_with(Path('temp', 'openlp')) |
904 | @@ -177,8 +187,6 @@ | |||
905 | 177 | mocked_settings.value.side_effect = \ | 187 | mocked_settings.value.side_effect = \ |
906 | 178 | lambda key: {'core/has run wizard': True, 'themes/global theme': 'Default Theme'}[key] | 188 | lambda key: {'core/has run wizard': True, 'themes/global theme': 'Default Theme'}[key] |
907 | 179 | with patch.object(frw, 'restart') as mocked_restart, \ | 189 | with patch.object(frw, 'restart') as mocked_restart, \ |
908 | 180 | patch.object(frw, 'cancel_button') as mocked_cancel_button, \ | ||
909 | 181 | patch.object(frw, 'no_internet_finish_button') as mocked_no_internet_finish_btn, \ | ||
910 | 182 | patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \ | 190 | patch.object(frw, 'currentIdChanged') as mocked_currentIdChanged, \ |
911 | 183 | patch.object(frw, 'theme_combo_box', **{'findText.return_value': 3}) as mocked_theme_combo_box, \ | 191 | patch.object(frw, 'theme_combo_box', **{'findText.return_value': 3}) as mocked_theme_combo_box, \ |
912 | 184 | patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT, | 192 | patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT, |
913 | @@ -200,12 +208,8 @@ | |||
914 | 200 | # THEN: The default values should have been set | 208 | # THEN: The default values should have been set |
915 | 201 | mocked_restart.assert_called_once() | 209 | mocked_restart.assert_called_once() |
916 | 202 | assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set' | 210 | assert 'https://get.openlp.org/ftw/' == frw.web, 'The default URL should be set' |
917 | 203 | mocked_cancel_button.clicked.connect.assert_called_once_with(frw.on_cancel_button_clicked) | ||
918 | 204 | mocked_no_internet_finish_btn.clicked.connect.assert_called_once_with( | ||
919 | 205 | frw.on_no_internet_finish_button_clicked) | ||
920 | 206 | mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed) | 211 | mocked_currentIdChanged.connect.assert_called_once_with(frw.on_current_id_changed) |
921 | 207 | mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load) | 212 | mocked_register_function.assert_called_once_with('config_screen_changed', frw.screen_selection_widget.load) |
922 | 208 | mocked_no_internet_finish_btn.setVisible.assert_called_once_with(False) | ||
923 | 209 | mocked_settings.value.assert_has_calls([call('core/has run wizard'), call('themes/global theme')]) | 213 | mocked_settings.value.assert_has_calls([call('core/has run wizard'), call('themes/global theme')]) |
924 | 210 | mocked_gettempdir.assert_called_once() | 214 | mocked_gettempdir.assert_called_once() |
925 | 211 | mocked_create_paths.assert_called_once_with(Path('temp', 'openlp')) | 215 | mocked_create_paths.assert_called_once_with(Path('temp', 'openlp')) |
926 | @@ -219,12 +223,78 @@ | |||
927 | 219 | mocked_theme_combo_box.findText.assert_called_once_with('Default Theme') | 223 | mocked_theme_combo_box.findText.assert_called_once_with('Default Theme') |
928 | 220 | mocked_theme_combo_box.setCurrentIndex(3) | 224 | mocked_theme_combo_box.setCurrentIndex(3) |
929 | 221 | 225 | ||
930 | 226 | @patch('openlp.core.ui.firsttimeform.Settings') | ||
931 | 227 | @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.accept') | ||
932 | 228 | def test_accept_method(self, mocked_qwizard_accept, *args): | ||
933 | 229 | """ | ||
934 | 230 | Test the FirstTimeForm.accept method | ||
935 | 231 | """ | ||
936 | 232 | # GIVEN: An instance of FirstTimeForm | ||
937 | 233 | frw = FirstTimeForm(None) | ||
938 | 234 | with patch.object(frw, '_set_plugin_status') as mocked_set_plugin_status, \ | ||
939 | 235 | patch.multiple(frw, songs_check_box=DEFAULT, bible_check_box=DEFAULT, presentation_check_box=DEFAULT, | ||
940 | 236 | image_check_box=DEFAULT, media_check_box=DEFAULT, custom_check_box=DEFAULT, | ||
941 | 237 | song_usage_check_box=DEFAULT, alert_check_box=DEFAULT) as mocked_check_boxes, \ | ||
942 | 238 | patch.object(frw, 'screen_selection_widget') as mocked_screen_selection_widget: | ||
943 | 239 | |||
944 | 240 | # WHEN: Calling accept | ||
945 | 241 | frw.accept() | ||
946 | 242 | |||
947 | 243 | # THEN: The selected plugins should be enabled, the screen selection saved and the super method called | ||
948 | 244 | mocked_set_plugin_status.assert_has_calls([ | ||
949 | 245 | call(mocked_check_boxes['songs_check_box'], 'songs/status'), | ||
950 | 246 | call(mocked_check_boxes['bible_check_box'], 'bibles/status'), | ||
951 | 247 | call(mocked_check_boxes['presentation_check_box'], 'presentations/status'), | ||
952 | 248 | call(mocked_check_boxes['image_check_box'], 'images/status'), | ||
953 | 249 | call(mocked_check_boxes['media_check_box'], 'media/status'), | ||
954 | 250 | call(mocked_check_boxes['custom_check_box'], 'custom/status'), | ||
955 | 251 | call(mocked_check_boxes['song_usage_check_box'], 'songusage/status'), | ||
956 | 252 | call(mocked_check_boxes['alert_check_box'], 'alerts/status')]) | ||
957 | 253 | mocked_screen_selection_widget.save.assert_called_once() | ||
958 | 254 | mocked_qwizard_accept.assert_called_once() | ||
959 | 255 | |||
960 | 256 | @patch('openlp.core.ui.firsttimeform.Settings') | ||
961 | 257 | def test_accept_method_theme_not_selected(self, mocked_settings): | ||
962 | 258 | """ | ||
963 | 259 | Test the FirstTimeForm.accept method when there is no default theme selected | ||
964 | 260 | """ | ||
965 | 261 | # GIVEN: An instance of FirstTimeForm | ||
966 | 262 | frw = FirstTimeForm(None) | ||
967 | 263 | with patch.object(frw, '_set_plugin_status'), patch.object(frw, 'screen_selection_widget'), \ | ||
968 | 264 | patch.object(frw, 'theme_combo_box', **{'currentIndex.return_value': -1}): | ||
969 | 265 | |||
970 | 266 | # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns -1 | ||
971 | 267 | frw.accept() | ||
972 | 268 | |||
973 | 269 | # THEN: OpenLP should not try to save a theme name | ||
974 | 270 | mocked_settings().setValue.assert_not_called() | ||
975 | 271 | |||
976 | 272 | @patch('openlp.core.ui.firsttimeform.Settings') | ||
977 | 273 | def test_accept_method_theme_selected(self, mocked_settings): | ||
978 | 274 | """ | ||
979 | 275 | Test the FirstTimeForm.accept method when a default theme is selected | ||
980 | 276 | """ | ||
981 | 277 | # GIVEN: An instance of FirstTimeForm | ||
982 | 278 | frw = FirstTimeForm(None) | ||
983 | 279 | with patch.object(frw, '_set_plugin_status'), \ | ||
984 | 280 | patch.object(frw, 'screen_selection_widget'), \ | ||
985 | 281 | patch.object( | ||
986 | 282 | frw, 'theme_combo_box', **{'currentIndex.return_value': 0, 'currentText.return_value': 'Test Item'}): | ||
987 | 283 | |||
988 | 284 | # WHEN: Calling accept and the currentIndex method of the theme_combo_box returns 0 | ||
989 | 285 | frw.accept() | ||
990 | 286 | |||
991 | 287 | # THEN: The 'currentItem' in the combobox should have been set as the default theme. | ||
992 | 288 | mocked_settings().setValue.assert_called_once_with('themes/global theme', 'Test Item') | ||
993 | 289 | |||
994 | 290 | @patch('openlp.core.ui.firsttimeform.QtWidgets.QWizard.reject') | ||
995 | 222 | @patch('openlp.core.ui.firsttimeform.time') | 291 | @patch('openlp.core.ui.firsttimeform.time') |
996 | 223 | @patch('openlp.core.ui.firsttimeform.get_thread_worker') | 292 | @patch('openlp.core.ui.firsttimeform.get_thread_worker') |
997 | 224 | @patch('openlp.core.ui.firsttimeform.is_thread_finished') | 293 | @patch('openlp.core.ui.firsttimeform.is_thread_finished') |
999 | 225 | def test_on_cancel_button_clicked(self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time): | 294 | def test_reject_method( |
1000 | 295 | self, mocked_is_thread_finished, mocked_get_thread_worker, mocked_time, mocked_qwizard_reject): | ||
1001 | 226 | """ | 296 | """ |
1003 | 227 | Test that the cancel button click slot shuts down the threads correctly | 297 | Test that the reject method shuts down the threads correctly |
1004 | 228 | """ | 298 | """ |
1005 | 229 | # GIVEN: A FRW, some mocked threads and workers (that isn't quite done) and other mocked stuff | 299 | # GIVEN: A FRW, some mocked threads and workers (that isn't quite done) and other mocked stuff |
1006 | 230 | mocked_worker = MagicMock() | 300 | mocked_worker = MagicMock() |
1007 | @@ -235,17 +305,47 @@ | |||
1008 | 235 | frw.thumbnail_download_threads = ['test_thread'] | 305 | frw.thumbnail_download_threads = ['test_thread'] |
1009 | 236 | with patch.object(frw.application, 'set_normal_cursor') as mocked_set_normal_cursor: | 306 | with patch.object(frw.application, 'set_normal_cursor') as mocked_set_normal_cursor: |
1010 | 237 | 307 | ||
1013 | 238 | # WHEN: on_cancel_button_clicked() is called | 308 | # WHEN: the reject method is called |
1014 | 239 | frw.on_cancel_button_clicked() | 309 | frw.reject() |
1015 | 240 | 310 | ||
1016 | 241 | # THEN: The right things should be called in the right order | 311 | # THEN: The right things should be called in the right order |
1017 | 242 | assert frw.was_cancelled is True, 'The was_cancelled property should have been set to True' | ||
1018 | 243 | mocked_get_thread_worker.assert_called_once_with('test_thread') | 312 | mocked_get_thread_worker.assert_called_once_with('test_thread') |
1019 | 244 | mocked_worker.cancel_download.assert_called_once() | 313 | mocked_worker.cancel_download.assert_called_once() |
1020 | 245 | mocked_is_thread_finished.assert_called_with('test_thread') | 314 | mocked_is_thread_finished.assert_called_with('test_thread') |
1021 | 246 | assert mocked_is_thread_finished.call_count == 2, 'isRunning() should have been called twice' | 315 | assert mocked_is_thread_finished.call_count == 2, 'isRunning() should have been called twice' |
1022 | 247 | mocked_time.sleep.assert_called_once_with(0.1) | 316 | mocked_time.sleep.assert_called_once_with(0.1) |
1024 | 248 | mocked_set_normal_cursor.assert_called_once_with() | 317 | mocked_set_normal_cursor.assert_called_once() |
1025 | 318 | mocked_qwizard_reject.assert_called_once() | ||
1026 | 319 | |||
1027 | 320 | @patch('openlp.core.ui.firsttimeform.ProxyDialog') | ||
1028 | 321 | def test_on_custom_button_clicked(self, mocked_proxy_dialog): | ||
1029 | 322 | """ | ||
1030 | 323 | Test _on_custom_button when it is called whe the 'internet settings' (CustomButton1) button is not clicked. | ||
1031 | 324 | """ | ||
1032 | 325 | # GIVEN: An instance of the FirstTimeForm | ||
1033 | 326 | frw = FirstTimeForm(None) | ||
1034 | 327 | |||
1035 | 328 | # WHEN: Calling _on_custom_button_clicked with a different button to the 'internet settings button. | ||
1036 | 329 | frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton2) | ||
1037 | 330 | |||
1038 | 331 | # THEN: The ProxyDialog should not be shown. | ||
1039 | 332 | mocked_proxy_dialog.assert_not_called() | ||
1040 | 333 | |||
1041 | 334 | @patch('openlp.core.ui.firsttimeform.ProxyDialog') | ||
1042 | 335 | def test_on_custom_button_clicked_internet_settings(self, mocked_proxy_dialog): | ||
1043 | 336 | """ | ||
1044 | 337 | Test _on_custom_button when it is called when the 'internet settings' (CustomButton1) button is clicked. | ||
1045 | 338 | """ | ||
1046 | 339 | # GIVEN: An instance of the FirstTimeForm | ||
1047 | 340 | frw = FirstTimeForm(None) | ||
1048 | 341 | |||
1049 | 342 | # WHEN: Calling _on_custom_button_clicked with the constant for the 'internet settings' button (CustomButton1) | ||
1050 | 343 | frw._on_custom_button_clicked(QtWidgets.QWizard.CustomButton1) | ||
1051 | 344 | |||
1052 | 345 | # THEN: The ProxyDialog should be shown. | ||
1053 | 346 | mocked_proxy_dialog.assert_called_with(frw) | ||
1054 | 347 | mocked_proxy_dialog().retranslate_ui.assert_called_once() | ||
1055 | 348 | mocked_proxy_dialog().exec.assert_called_once() | ||
1056 | 249 | 349 | ||
1057 | 250 | @patch('openlp.core.ui.firsttimeform.critical_error_message_box') | 350 | @patch('openlp.core.ui.firsttimeform.critical_error_message_box') |
1058 | 251 | def test__parse_config_invalid_config(self, mocked_critical_error_message_box): | 351 | def test__parse_config_invalid_config(self, mocked_critical_error_message_box): |
1059 | @@ -279,10 +379,10 @@ | |||
1060 | 279 | 379 | ||
1061 | 280 | # THEN: the critical_error_message_box should have been called | 380 | # THEN: the critical_error_message_box should have been called |
1062 | 281 | mocked_message_box.critical.assert_called_once_with( | 381 | mocked_message_box.critical.assert_called_once_with( |
1065 | 282 | first_time_form, 'Network Error', 'There was a network error attempting to connect to retrieve ' | 382 | first_time_form, 'Network Error', |
1066 | 283 | 'initial configuration information', 'OK') | 383 | 'There was a network error attempting to connect to retrieve initial configuration information', 'OK') |
1067 | 284 | 384 | ||
1069 | 285 | @patch('openlp.core.ui.firsttimewizard.Settings') | 385 | @patch('openlp.core.ui.firsttimeform.Settings') |
1070 | 286 | def test_on_projectors_check_box_checked(self, MockSettings): | 386 | def test_on_projectors_check_box_checked(self, MockSettings): |
1071 | 287 | """ | 387 | """ |
1072 | 288 | Test that the projector panel is shown when the checkbox in the first time wizard is checked | 388 | Test that the projector panel is shown when the checkbox in the first time wizard is checked |
1073 | @@ -300,7 +400,7 @@ | |||
1074 | 300 | mocked_settings.value.assert_called_once_with('projector/show after wizard') | 400 | mocked_settings.value.assert_called_once_with('projector/show after wizard') |
1075 | 301 | mocked_settings.setValue.assert_called_once_with('projector/show after wizard', False) | 401 | mocked_settings.setValue.assert_called_once_with('projector/show after wizard', False) |
1076 | 302 | 402 | ||
1078 | 303 | @patch('openlp.core.ui.firsttimewizard.Settings') | 403 | @patch('openlp.core.ui.firsttimeform.Settings') |
1079 | 304 | def test_on_projectors_check_box_unchecked(self, MockSettings): | 404 | def test_on_projectors_check_box_unchecked(self, MockSettings): |
1080 | 305 | """ | 405 | """ |
1081 | 306 | Test that the projector panel is shown when the checkbox in the first time wizard is checked | 406 | Test that the projector panel is shown when the checkbox in the first time wizard is checked |
1082 | 307 | 407 | ||
1083 | === modified file 'tests/functional/openlp_plugins/songs/test_openlpimporter.py' | |||
1084 | --- tests/functional/openlp_plugins/songs/test_openlpimporter.py 2019-02-14 15:09:09 +0000 | |||
1085 | +++ tests/functional/openlp_plugins/songs/test_openlpimporter.py 2019-03-15 20:50:11 +0000 | |||
1086 | @@ -22,6 +22,7 @@ | |||
1087 | 22 | """ | 22 | """ |
1088 | 23 | This module contains tests for the OpenLP song importer. | 23 | This module contains tests for the OpenLP song importer. |
1089 | 24 | """ | 24 | """ |
1090 | 25 | from pathlib import Path | ||
1091 | 25 | from unittest import TestCase | 26 | from unittest import TestCase |
1092 | 26 | from unittest.mock import MagicMock, patch | 27 | from unittest.mock import MagicMock, patch |
1093 | 27 | 28 | ||
1094 | @@ -66,10 +67,9 @@ | |||
1095 | 66 | importer.stop_import_flag = True | 67 | importer.stop_import_flag = True |
1096 | 67 | 68 | ||
1097 | 68 | # WHEN: Import source is not a list | 69 | # WHEN: Import source is not a list |
1100 | 69 | for source in ['not a list', 0]: | 70 | importer.import_source = Path() |
1099 | 70 | importer.import_source = source | ||
1101 | 71 | 71 | ||
1106 | 72 | # THEN: do_import should return none and the progress bar maximum should not be set. | 72 | # THEN: do_import should return none and the progress bar maximum should not be set. |
1107 | 73 | assert importer.do_import() is None, 'do_import should return None when import_source is not a list' | 73 | assert importer.do_import() is None, 'do_import should return None when import_source is not a list' |
1108 | 74 | assert mocked_import_wizard.progress_bar.setMaximum.called is False, \ | 74 | assert mocked_import_wizard.progress_bar.setMaximum.called is False, \ |
1109 | 75 | 'setMaximum on import_wizard.progress_bar should not have been called' | 75 | 'setMaximum on import_wizard.progress_bar should not have been called' |
Linux tests failed, please see https:/ /ci.openlp. io/job/ MP-02-Linux_ Tests/89/ for more details