Merge lp:~phill-ridout/openlp/wigify_screen_selection into lp:~openlp-dev/openlp/webengine-migrate
- wigify_screen_selection
- Merge into webengine-migrate
Proposed by
Phill
Status: | Merged |
---|---|
Merged at revision: | 2862 |
Proposed branch: | lp:~phill-ridout/openlp/wigify_screen_selection |
Merge into: | lp:~openlp-dev/openlp/webengine-migrate |
Diff against target: |
793 lines (+462/-231) 4 files modified
openlp/core/common/settings.py (+1/-1) openlp/core/ui/screenstab.py (+11/-229) openlp/core/widgets/widgets.py (+251/-1) tests/functional/openlp_core/widgets/test_widgets.py (+199/-0) |
To merge this branch: | bzr merge lp:~phill-ridout/openlp/wigify_screen_selection |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tomas Groth | Approve | ||
Review via email: mp+358590@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'openlp/core/common/settings.py' |
2 | --- openlp/core/common/settings.py 2018-10-24 21:02:06 +0000 |
3 | +++ openlp/core/common/settings.py 2018-11-10 08:28:19 +0000 |
4 | @@ -84,7 +84,7 @@ |
5 | """ |
6 | geometry_key = 'geometry' |
7 | if can_override: |
8 | - geometry_key = 'display_geometry' |
9 | + geometry_key = 'custom_geometry' |
10 | return { |
11 | number: { |
12 | 'number': number, |
13 | |
14 | === modified file 'openlp/core/ui/screenstab.py' |
15 | --- openlp/core/ui/screenstab.py 2018-11-06 20:39:09 +0000 |
16 | +++ openlp/core/ui/screenstab.py 2018-11-10 08:28:19 +0000 |
17 | @@ -22,49 +22,15 @@ |
18 | """ |
19 | The screen settings tab in the configuration dialog |
20 | """ |
21 | -import logging |
22 | - |
23 | -from PyQt5 import QtCore, QtWidgets |
24 | +from PyQt5 import QtWidgets |
25 | |
26 | from openlp.core.common.i18n import translate |
27 | from openlp.core.common.settings import Settings |
28 | from openlp.core.display.screens import ScreenList |
29 | from openlp.core.lib.settingstab import SettingsTab |
30 | +from openlp.core.common.registry import Registry |
31 | from openlp.core.ui.icons import UiIcons |
32 | - |
33 | - |
34 | -SCREENS_LAYOUT_STYLE = """ |
35 | -#screen_frame { |
36 | - background-color: palette(base); |
37 | -} |
38 | -#screen_frame QPushButton { |
39 | - background-color: palette(window); |
40 | - border: 3px solid palette(text); |
41 | - border-radius: 3px; |
42 | - height: 100px; |
43 | - outline: 0; |
44 | - width: 150px; |
45 | -} |
46 | -#screen_frame QPushButton:checked { |
47 | - border-color: palette(highlight); |
48 | -} |
49 | -""" |
50 | -log = logging.getLogger(__name__) |
51 | - |
52 | - |
53 | -class ScreenButton(QtWidgets.QPushButton): |
54 | - """ |
55 | - A special button class that holds the screen information about it |
56 | - """ |
57 | - def __init__(self, parent, screen): |
58 | - """ |
59 | - Initialise this button |
60 | - """ |
61 | - super(ScreenButton, self).__init__(parent) |
62 | - self.setObjectName('screen{number}_button'.format(number=screen.number)) |
63 | - self.setText(str(screen)) |
64 | - self.setCheckable(True) |
65 | - self.screen = screen |
66 | +from openlp.core.widgets.widgets import ScreenSelectionWidget |
67 | |
68 | |
69 | class ScreensTab(SettingsTab): |
70 | @@ -75,88 +41,21 @@ |
71 | """ |
72 | Initialise the screen settings tab |
73 | """ |
74 | - self.screens = ScreenList() |
75 | self.icon_path = UiIcons().settings |
76 | screens_translated = translate('OpenLP.ScreensTab', 'Screens') |
77 | super(ScreensTab, self).__init__(parent, 'Screens', screens_translated) |
78 | self.settings_section = 'core' |
79 | - self.current_screen = None |
80 | - self.identify_labels = [] |
81 | |
82 | def setup_ui(self): |
83 | """ |
84 | Set up the user interface elements |
85 | """ |
86 | self.setObjectName('self') |
87 | - self.setStyleSheet(SCREENS_LAYOUT_STYLE) |
88 | self.tab_layout = QtWidgets.QVBoxLayout(self) |
89 | self.tab_layout.setObjectName('tab_layout') |
90 | - self.screen_frame = QtWidgets.QFrame(self) |
91 | - self.screen_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) |
92 | - self.screen_frame.setFrameShadow(QtWidgets.QFrame.Sunken) |
93 | - self.screen_frame.setObjectName('screen_frame') |
94 | - self.screen_frame_layout = QtWidgets.QHBoxLayout(self.screen_frame) |
95 | - self.screen_frame_layout.setContentsMargins(16, 16, 16, 16) |
96 | - self.screen_frame_layout.setSpacing(8) |
97 | - self.screen_frame_layout.setObjectName('screen_frame_layout') |
98 | - self.tab_layout.addWidget(self.screen_frame) |
99 | - self.screen_details_widget = QtWidgets.QWidget(self) |
100 | - self.screen_details_widget.setObjectName('screen_details_widget') |
101 | - self.screen_details_layout = QtWidgets.QGridLayout(self.screen_details_widget) |
102 | - self.screen_details_layout.setSpacing(8) |
103 | - self.screen_details_layout.setObjectName('screen_details_layout') |
104 | - self.screen_number_label = QtWidgets.QLabel(self.screen_details_widget) |
105 | - self.screen_number_label.setObjectName('screen_number_label') |
106 | - self.screen_details_layout.addWidget(self.screen_number_label, 0, 0, 1, 4) |
107 | - self.is_display_check_box = QtWidgets.QCheckBox(self.screen_details_widget) |
108 | - self.is_display_check_box.setObjectName('is_display_check_box') |
109 | - self.screen_details_layout.addWidget(self.is_display_check_box, 1, 0, 1, 4) |
110 | - self.full_screen_radio_button = QtWidgets.QRadioButton(self.screen_details_widget) |
111 | - self.full_screen_radio_button.setObjectName('full_screen_radio_button') |
112 | - self.screen_details_layout.addWidget(self.full_screen_radio_button, 2, 0, 1, 4) |
113 | - self.custom_geometry_button = QtWidgets.QRadioButton(self.screen_details_widget) |
114 | - self.custom_geometry_button.setObjectName('custom_geometry_button') |
115 | - self.screen_details_layout.addWidget(self.custom_geometry_button, 3, 0, 1, 4) |
116 | - self.left_label = QtWidgets.QLabel(self.screen_details_widget) |
117 | - self.left_label.setObjectName('left_label') |
118 | - self.screen_details_layout.addWidget(self.left_label, 4, 1, 1, 1) |
119 | - self.top_label = QtWidgets.QLabel(self.screen_details_widget) |
120 | - self.top_label.setObjectName('top_label') |
121 | - self.screen_details_layout.addWidget(self.top_label, 4, 2, 1, 1) |
122 | - self.width_label = QtWidgets.QLabel(self.screen_details_widget) |
123 | - self.width_label.setObjectName('width_label') |
124 | - self.screen_details_layout.addWidget(self.width_label, 4, 3, 1, 1) |
125 | - self.height_label = QtWidgets.QLabel(self.screen_details_widget) |
126 | - self.height_label.setObjectName('height_label') |
127 | - self.screen_details_layout.addWidget(self.height_label, 4, 4, 1, 1) |
128 | - self.left_spin_box = QtWidgets.QSpinBox(self.screen_details_widget) |
129 | - self.left_spin_box.setObjectName('left_spin_box') |
130 | - self.screen_details_layout.addWidget(self.left_spin_box, 5, 1, 1, 1) |
131 | - self.top_spin_box = QtWidgets.QSpinBox(self.screen_details_widget) |
132 | - self.top_spin_box.setObjectName('top_spin_box') |
133 | - self.screen_details_layout.addWidget(self.top_spin_box, 5, 2, 1, 1) |
134 | - self.width_spin_box = QtWidgets.QSpinBox(self.screen_details_widget) |
135 | - self.width_spin_box.setObjectName('width_spin_box') |
136 | - self.screen_details_layout.addWidget(self.width_spin_box, 5, 3, 1, 1) |
137 | - self.height_spin_box = QtWidgets.QSpinBox(self.screen_details_widget) |
138 | - self.height_spin_box.setObjectName('height_spin_box') |
139 | - self.screen_details_layout.addWidget(self.height_spin_box, 5, 4, 1, 1) |
140 | - self.screen_details_layout.setColumnStretch(5, 1) |
141 | - self.tab_layout.addWidget(self.screen_details_widget) |
142 | - self.tab_layout.addStretch() |
143 | - self.identify_button = QtWidgets.QPushButton(self) |
144 | - self.identify_button.setGeometry(QtCore.QRect(596, 98, 124, 32)) |
145 | - self.identify_button.setObjectName('identify_button') |
146 | - self.screen_button_group = QtWidgets.QButtonGroup(self.screen_frame) |
147 | - self.screen_button_group.setExclusive(True) |
148 | - self.screen_button_group.setObjectName('screen_button_group') |
149 | - self.identify_button.clicked.connect(self.on_identify_button_clicked) |
150 | - |
151 | - self._setup_spin_box(self.left_spin_box, 0, 9999, 0) |
152 | - self._setup_spin_box(self.top_spin_box, 0, 9999, 0) |
153 | - self._setup_spin_box(self.width_spin_box, 0, 9999, 0) |
154 | - self._setup_spin_box(self.height_spin_box, 0, 9999, 0) |
155 | - |
156 | + |
157 | + self.screen_selection_widget = ScreenSelectionWidget(self, ScreenList()) |
158 | + self.tab_layout.addWidget(self.screen_selection_widget) |
159 | self.generic_group_box = QtWidgets.QGroupBox(self) |
160 | self.generic_group_box.setObjectName('generic_group_box') |
161 | self.generic_group_layout = QtWidgets.QVBoxLayout(self.generic_group_box) |
162 | @@ -165,19 +64,12 @@ |
163 | self.generic_group_layout.addWidget(self.display_on_monitor_check) |
164 | self.tab_layout.addWidget(self.generic_group_box) |
165 | |
166 | + Registry().register_function('config_screen_changed', self.screen_selection_widget.load) |
167 | + |
168 | self.retranslate_ui() |
169 | |
170 | def retranslate_ui(self): |
171 | - self.setWindowTitle(translate('self', 'self')) |
172 | - self.full_screen_radio_button.setText(translate('OpenLP.ScreensTab', 'F&ull screen')) |
173 | - self.width_label.setText(translate('OpenLP.ScreensTab', 'Width:')) |
174 | - self.is_display_check_box.setText(translate('OpenLP.ScreensTab', 'Use this screen as a display')) |
175 | - self.left_label.setText(translate('OpenLP.ScreensTab', 'Left:')) |
176 | - self.custom_geometry_button.setText(translate('OpenLP.ScreensTab', 'Custom &geometry')) |
177 | - self.top_label.setText(translate('OpenLP.ScreensTab', 'Top:')) |
178 | - self.height_label.setText(translate('OpenLP.ScreensTab', 'Height')) |
179 | - self.screen_number_label.setText(translate('OpenLP.ScreensTab', '<strong>Screen 1</strong>')) |
180 | - self.identify_button.setText(translate('OpenLP.ScreensTab', 'Identify Screens')) |
181 | + self.setWindowTitle(translate('self', 'self')) # TODO: ??? |
182 | self.generic_group_box.setTitle(translate('OpenLP.ScreensTab', 'Generic screen settings')) |
183 | self.display_on_monitor_check.setText(translate('OpenLP.ScreensTab', 'Display if a single screen')) |
184 | |
185 | @@ -187,130 +79,20 @@ |
186 | |
187 | NB: Don't call SettingsTab's resizeEvent() because we're not using its widgets. |
188 | """ |
189 | - button_geometry = self.identify_button.geometry() |
190 | - frame_geometry = self.screen_frame.geometry() |
191 | - button_geometry.moveTop(frame_geometry.bottom() + 8) |
192 | - button_geometry.moveRight(frame_geometry.right()) |
193 | - self.identify_button.setGeometry(button_geometry) |
194 | QtWidgets.QWidget.resizeEvent(self, event) |
195 | |
196 | - def show(self): |
197 | - """ |
198 | - Override show just to do some initialisation |
199 | - """ |
200 | - super(ScreensTab, self).show() |
201 | - if self.screen_frame_layout.count() > 2: |
202 | - self.screen_frame_layout.itemAt(1).widget().click() |
203 | - |
204 | - def _setup_spin_box(self, spin_box, mininum, maximum, value): |
205 | - """ |
206 | - Set up the spin box |
207 | - """ |
208 | - spin_box.setMinimum(mininum) |
209 | - spin_box.setMaximum(maximum) |
210 | - spin_box.setValue(value) |
211 | - |
212 | - def _save_screen(self, screen): |
213 | - """ |
214 | - Save the details in the UI to the screen |
215 | - """ |
216 | - if not screen: |
217 | - return |
218 | - screen.is_display = self.is_display_check_box.isChecked() |
219 | - if self.custom_geometry_button.isChecked(): |
220 | - custom_geometry = QtCore.QRect() |
221 | - custom_geometry.setTop(self.top_spin_box.value()) |
222 | - custom_geometry.setLeft(self.left_spin_box.value()) |
223 | - custom_geometry.setWidth(self.width_spin_box.value()) |
224 | - custom_geometry.setHeight(self.height_spin_box.value()) |
225 | - screen.custom_geometry = custom_geometry |
226 | - else: |
227 | - screen.custom_geometry = None |
228 | - |
229 | def load(self): |
230 | """ |
231 | Load the settings to populate the tab |
232 | """ |
233 | settings = Settings() |
234 | settings.beginGroup(self.settings_section) |
235 | - # Remove all the existing items in the layout |
236 | - while self.screen_frame_layout.count() > 0: |
237 | - item = self.screen_frame_layout.takeAt(0) |
238 | - if item.widget() is not None: |
239 | - widget = item.widget() |
240 | - if widget in self.screen_button_group.buttons(): |
241 | - self.screen_button_group.removeButton(widget) |
242 | - widget.setParent(None) |
243 | - widget.deleteLater() |
244 | - del item |
245 | - # Add the existing screens into the frame |
246 | - self.screen_frame_layout.addStretch() |
247 | - for screen in self.screens: |
248 | - screen_button = ScreenButton(self.screen_frame, screen) |
249 | - screen_button.clicked.connect(self.on_screen_button_clicked) |
250 | - self.screen_frame_layout.addWidget(screen_button) |
251 | - self.screen_button_group.addButton(screen_button) |
252 | - self.screen_frame_layout.addStretch() |
253 | + self.screen_selection_widget.load() |
254 | # Load generic settings |
255 | self.display_on_monitor_check.setChecked(Settings().value('core/display on monitor')) |
256 | |
257 | def save(self): |
258 | - """ |
259 | - Save the screen settings |
260 | - """ |
261 | - self._save_screen(self.current_screen) |
262 | - settings = Settings() |
263 | - screen_settings = {} |
264 | - for screen in self.screens: |
265 | - screen_settings[screen.number] = screen.to_dict() |
266 | - settings.setValue('core/screens', screen_settings) |
267 | + self.screen_selection_widget.save() |
268 | settings.setValue('core/display on monitor', self.display_on_monitor_check.isChecked()) |
269 | # On save update the screens as well |
270 | self.settings_form.register_post_process('config_screen_changed') |
271 | - |
272 | - @QtCore.pyqtSlot() |
273 | - def _on_identify_timer_shot(self): |
274 | - for label in self.identify_labels: |
275 | - label.hide() |
276 | - label.setParent(None) |
277 | - label.deleteLater() |
278 | - self.identify_labels = [] |
279 | - |
280 | - def on_identify_button_clicked(self): |
281 | - """ |
282 | - Display a widget on every screen for 5 seconds |
283 | - """ |
284 | - for screen in self.screens: |
285 | - label = QtWidgets.QLabel(None) |
286 | - label.setAlignment(QtCore.Qt.AlignCenter) |
287 | - label.font().setBold(True) |
288 | - label.font().setPointSize(24) |
289 | - label.setText('<font size="24">Screen {number}</font>'.format(number=screen.number + 1)) |
290 | - label.setStyleSheet('background-color: #0c0; color: #000; border: 5px solid #000;') |
291 | - label.setGeometry(QtCore.QRect(screen.geometry.x(), screen.geometry.y(), 200, 100)) |
292 | - label.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint | |
293 | - QtCore.Qt.WindowDoesNotAcceptFocus) |
294 | - label.show() |
295 | - self.identify_labels.append(label) |
296 | - |
297 | - QtCore.QTimer.singleShot(3000, self._on_identify_timer_shot) |
298 | - |
299 | - def on_screen_button_clicked(self): |
300 | - """ |
301 | - Respond to a screen button being clicked |
302 | - """ |
303 | - screen = self.sender().screen |
304 | - if self.current_screen is not screen: |
305 | - self._save_screen(self.current_screen) |
306 | - self.screen_number_label.setText(str(screen)) |
307 | - self.is_display_check_box.setChecked(screen.is_display) |
308 | - self.full_screen_radio_button.setChecked(screen.custom_geometry is None) |
309 | - self.custom_geometry_button.setChecked(screen.custom_geometry is not None) |
310 | - self._setup_spin_box(self.left_spin_box, screen.display_geometry.y(), screen.display_geometry.right(), |
311 | - screen.display_geometry.x()) |
312 | - self._setup_spin_box(self.top_spin_box, screen.display_geometry.y(), screen.display_geometry.bottom(), |
313 | - screen.display_geometry.y()) |
314 | - self._setup_spin_box(self.width_spin_box, 0, screen.display_geometry.width(), screen.display_geometry.width()) |
315 | - self._setup_spin_box(self.height_spin_box, 0, screen.display_geometry.height(), |
316 | - screen.display_geometry.height()) |
317 | - self.current_screen = screen |
318 | |
319 | === modified file 'openlp/core/widgets/widgets.py' |
320 | --- openlp/core/widgets/widgets.py 2018-06-10 06:38:16 +0000 |
321 | +++ openlp/core/widgets/widgets.py 2018-11-10 08:28:19 +0000 |
322 | @@ -22,12 +22,30 @@ |
323 | """ |
324 | The :mod:`~openlp.core.widgets.widgets` module contains custom widgets used in OpenLP |
325 | """ |
326 | -from PyQt5 import QtWidgets |
327 | +from PyQt5 import QtCore, QtWidgets |
328 | |
329 | from openlp.core.common.i18n import translate |
330 | from openlp.core.common.settings import ProxyMode, Settings |
331 | |
332 | |
333 | +SCREENS_LAYOUT_STYLE = """ |
334 | +#screen_frame { |
335 | + background-color: palette(base); |
336 | +} |
337 | +#screen_frame QPushButton { |
338 | + background-color: palette(window); |
339 | + border: 3px solid palette(text); |
340 | + border-radius: 3px; |
341 | + height: 100px; |
342 | + outline: 0; |
343 | + width: 150px; |
344 | +} |
345 | +#screen_frame QPushButton:checked { |
346 | + border-color: palette(highlight); |
347 | +} |
348 | +""" |
349 | + |
350 | + |
351 | class ProxyWidget(QtWidgets.QGroupBox): |
352 | """ |
353 | A proxy settings widget that implements loading and saving its settings. |
354 | @@ -130,3 +148,235 @@ |
355 | settings.setValue('advanced/proxy https', self.https_edit.text()) |
356 | settings.setValue('advanced/proxy username', self.username_edit.text()) |
357 | settings.setValue('advanced/proxy password', self.password_edit.text()) |
358 | + |
359 | + |
360 | +class ScreenButton(QtWidgets.QPushButton): |
361 | + """ |
362 | + A special button class that holds the screen information about it |
363 | + """ |
364 | + def __init__(self, parent, screen): |
365 | + """ |
366 | + Initialise this button |
367 | + """ |
368 | + super().__init__(parent) |
369 | + self.setObjectName('screen_{number}_button'.format(number=screen.number)) |
370 | + self.setCheckable(True) |
371 | + self.setText(str(screen)) |
372 | + self.screen = screen |
373 | + |
374 | + |
375 | +class ScreenSelectionWidget(QtWidgets.QWidget): |
376 | + def __init__(self, parent=None, screens=[]): |
377 | + super().__init__(parent) |
378 | + self.current_screen = None |
379 | + self.identify_labels = [] |
380 | + self.screens = screens |
381 | + self.timer = QtCore.QTimer() |
382 | + self.timer.setSingleShot(True) |
383 | + self.timer.setInterval(3000) |
384 | + self.timer.timeout.connect(self._on_identify_timer_shot) |
385 | + self.setup_ui() |
386 | + |
387 | + def setup_ui(self): |
388 | + self.setStyleSheet(SCREENS_LAYOUT_STYLE) |
389 | + self.layout = QtWidgets.QVBoxLayout(self) |
390 | + self.screen_frame = QtWidgets.QFrame(self) |
391 | + self.screen_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) |
392 | + self.screen_frame.setFrameShadow(QtWidgets.QFrame.Sunken) |
393 | + self.screen_frame.setObjectName('screen_frame') |
394 | + self.screen_frame_layout = QtWidgets.QHBoxLayout(self.screen_frame) |
395 | + self.screen_frame_layout.setContentsMargins(16, 16, 16, 16) |
396 | + self.screen_frame_layout.setSpacing(8) |
397 | + self.screen_frame_layout.setObjectName('screen_frame_layout') |
398 | + self.layout.addWidget(self.screen_frame) |
399 | + self.identify_layout = QtWidgets.QHBoxLayout(self) |
400 | + self.screen_details_layout = QtWidgets.QVBoxLayout(self) |
401 | + self.screen_details_layout.setObjectName('screen_details_layout') |
402 | + self.screen_number_label = QtWidgets.QLabel(self) |
403 | + self.screen_number_label.setObjectName('screen_number_label') |
404 | + self.screen_details_layout.addWidget(self.screen_number_label) |
405 | + self.display_group_box = QtWidgets.QGroupBox(self) |
406 | + self.display_group_box.setObjectName('display_group_box') |
407 | + self.display_group_box.setCheckable(True) |
408 | + self.display_group_box_layout = QtWidgets.QGridLayout(self.display_group_box) |
409 | + self.display_group_box_layout.setSpacing(8) |
410 | + self.display_group_box_layout.setObjectName('display_group_box_layout') |
411 | + self.full_screen_radio_button = QtWidgets.QRadioButton(self.display_group_box) |
412 | + self.full_screen_radio_button.setObjectName('full_screen_radio_button') |
413 | + self.display_group_box_layout.addWidget(self.full_screen_radio_button, 0, 0, 1, 4) |
414 | + self.custom_geometry_button = QtWidgets.QRadioButton(self.display_group_box) |
415 | + self.custom_geometry_button.setObjectName('custom_geometry_button') |
416 | + self.display_group_box_layout.addWidget(self.custom_geometry_button, 1, 0, 1, 4) |
417 | + self.left_label = QtWidgets.QLabel(self.display_group_box) |
418 | + self.left_label.setObjectName('left_label') |
419 | + self.display_group_box_layout.addWidget(self.left_label, 2, 1, 1, 1) |
420 | + self.top_label = QtWidgets.QLabel(self.display_group_box) |
421 | + self.top_label.setObjectName('top_label') |
422 | + self.display_group_box_layout.addWidget(self.top_label, 2, 2, 1, 1) |
423 | + self.width_label = QtWidgets.QLabel(self.display_group_box) |
424 | + self.width_label.setObjectName('width_label') |
425 | + self.display_group_box_layout.addWidget(self.width_label, 2, 3, 1, 1) |
426 | + self.height_label = QtWidgets.QLabel(self.display_group_box) |
427 | + self.height_label.setObjectName('height_label') |
428 | + self.display_group_box_layout.addWidget(self.height_label, 2, 4, 1, 1) |
429 | + self.left_spin_box = QtWidgets.QSpinBox(self.display_group_box) |
430 | + self.left_spin_box.setObjectName('left_spin_box') |
431 | + self.left_spin_box.setEnabled(False) |
432 | + self.display_group_box_layout.addWidget(self.left_spin_box, 3, 1, 1, 1) |
433 | + self.top_spin_box = QtWidgets.QSpinBox(self.display_group_box) |
434 | + self.top_spin_box.setObjectName('top_spin_box') |
435 | + self.top_spin_box.setEnabled(False) |
436 | + self.display_group_box_layout.addWidget(self.top_spin_box, 3, 2, 1, 1) |
437 | + self.width_spin_box = QtWidgets.QSpinBox(self.display_group_box) |
438 | + self.width_spin_box.setObjectName('width_spin_box') |
439 | + self.width_spin_box.setEnabled(False) |
440 | + self.display_group_box_layout.addWidget(self.width_spin_box, 3, 3, 1, 1) |
441 | + self.height_spin_box = QtWidgets.QSpinBox(self.display_group_box) |
442 | + self.height_spin_box.setObjectName('height_spin_box') |
443 | + self.height_spin_box.setEnabled(False) |
444 | + self.display_group_box_layout.addWidget(self.height_spin_box, 3, 4, 1, 1) |
445 | + self.display_group_box_layout.setColumnStretch(3, 1) |
446 | + self.display_group_box.setLayout(self.display_group_box_layout) |
447 | + self.screen_details_layout.addWidget(self.display_group_box) |
448 | + self.identify_layout.addLayout(self.screen_details_layout) |
449 | + self.identify_button = QtWidgets.QPushButton(self) |
450 | + self.identify_button.setObjectName('identify_button') |
451 | + self.identify_layout.addWidget( |
452 | + self.identify_button, stretch=1, alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignTop) |
453 | + self.screen_button_group = QtWidgets.QButtonGroup(self.screen_frame) |
454 | + self.screen_button_group.setExclusive(True) |
455 | + self.screen_button_group.setObjectName('screen_button_group') |
456 | + self.layout.addLayout(self.identify_layout) |
457 | + self.layout.addStretch() |
458 | + |
459 | + # Signals and slots |
460 | + self.custom_geometry_button.toggled.connect(self.height_spin_box.setEnabled) |
461 | + self.custom_geometry_button.toggled.connect(self.left_spin_box.setEnabled) |
462 | + self.custom_geometry_button.toggled.connect(self.top_spin_box.setEnabled) |
463 | + self.custom_geometry_button.toggled.connect(self.width_spin_box.setEnabled) |
464 | + self.identify_button.clicked.connect(self.on_identify_button_clicked) |
465 | + |
466 | + self._setup_spin_box(self.left_spin_box, 0, 9999, 0) |
467 | + self._setup_spin_box(self.top_spin_box, 0, 9999, 0) |
468 | + self._setup_spin_box(self.width_spin_box, 0, 9999, 0) |
469 | + self._setup_spin_box(self.height_spin_box, 0, 9999, 0) |
470 | + self.retranslate_ui() |
471 | + |
472 | + def retranslate_ui(self): |
473 | + self.full_screen_radio_button.setText(translate('OpenLP.ScreensTab', 'F&ull screen')) |
474 | + self.width_label.setText(translate('OpenLP.ScreensTab', 'Width:')) |
475 | + self.display_group_box.setTitle(translate('OpenLP.ScreensTab', 'Use this screen as a display')) |
476 | + self.left_label.setText(translate('OpenLP.ScreensTab', 'Left:')) |
477 | + self.custom_geometry_button.setText(translate('OpenLP.ScreensTab', 'Custom &geometry')) |
478 | + self.top_label.setText(translate('OpenLP.ScreensTab', 'Top:')) |
479 | + self.height_label.setText(translate('OpenLP.ScreensTab', 'Height:')) |
480 | + self.identify_button.setText(translate('OpenLP.ScreensTab', 'Identify Screens')) |
481 | + |
482 | + def _save_screen(self, screen): |
483 | + """ |
484 | + Save the details in the UI to the screen |
485 | + |
486 | + :param openlp.core.display.screens.Screen screen: |
487 | + :return: None |
488 | + """ |
489 | + if not screen: |
490 | + return |
491 | + screen.is_display = self.display_group_box.isChecked() |
492 | + if self.custom_geometry_button.isChecked(): |
493 | + screen.custom_geometry = QtCore.QRect(self.left_spin_box.value(), self.top_spin_box.value(), |
494 | + self.width_spin_box.value(), self.height_spin_box.value()) |
495 | + else: |
496 | + screen.custom_geometry = None |
497 | + |
498 | + def _setup_spin_box(self, spin_box, mininum, maximum, value): |
499 | + """ |
500 | + Set up the spin box |
501 | + |
502 | + :param QtWidgets.QSpinBox spin_box: |
503 | + :param int minimun: |
504 | + :param int maximum: |
505 | + :param int value: |
506 | + :return: None |
507 | + """ |
508 | + spin_box.setMinimum(mininum) |
509 | + spin_box.setMaximum(maximum) |
510 | + spin_box.setValue(value) |
511 | + |
512 | + def load(self): |
513 | + # Remove all the existing items in the layout |
514 | + while self.screen_frame_layout.count() > 0: |
515 | + item = self.screen_frame_layout.takeAt(0) |
516 | + if item.widget() is not None: |
517 | + widget = item.widget() |
518 | + if widget in self.screen_button_group.buttons(): |
519 | + self.screen_button_group.removeButton(widget) |
520 | + widget.setParent(None) |
521 | + widget.deleteLater() |
522 | + del item |
523 | + # Add the existing screens into the frame |
524 | + self.screen_frame_layout.addStretch() |
525 | + for screen in self.screens: |
526 | + screen_button = ScreenButton(self.screen_frame, screen) |
527 | + screen_button.clicked.connect(self.on_screen_button_clicked) |
528 | + if not self.current_screen or screen.is_display: |
529 | + screen_button.click() |
530 | + self.screen_frame_layout.addWidget(screen_button) |
531 | + self.screen_button_group.addButton(screen_button) |
532 | + self.screen_frame_layout.addStretch() |
533 | + |
534 | + def save(self): |
535 | + """ |
536 | + Save the screen settings |
537 | + """ |
538 | + self._save_screen(self.current_screen) |
539 | + settings = Settings() |
540 | + screen_settings = {} |
541 | + for screen in self.screens: |
542 | + screen_settings[screen.number] = screen.to_dict() |
543 | + settings.setValue('core/screens', screen_settings) |
544 | + # On save update the screens as well |
545 | + |
546 | + @QtCore.pyqtSlot() |
547 | + def _on_identify_timer_shot(self): |
548 | + for label in self.identify_labels: |
549 | + label.hide() |
550 | + label.setParent(None) |
551 | + label.deleteLater() |
552 | + self.identify_labels = [] |
553 | + |
554 | + def on_identify_button_clicked(self): |
555 | + """ |
556 | + Display a widget on every screen for 5 seconds |
557 | + """ |
558 | + for screen in self.screens: |
559 | + label = QtWidgets.QLabel(None) |
560 | + label.setAlignment(QtCore.Qt.AlignCenter) |
561 | + label.setText(str(screen)) |
562 | + label.setStyleSheet('font-size: 24pt; font-weight: bold;' |
563 | + 'background-color: #0C0; color: #000; border: 5px solid #000;') |
564 | + label.setGeometry(QtCore.QRect(screen.geometry.x(), screen.geometry.y(), screen.geometry.width(), 100)) |
565 | + label.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint | |
566 | + QtCore.Qt.WindowDoesNotAcceptFocus) |
567 | + label.show() |
568 | + self.identify_labels.append(label) |
569 | + self.timer.start() |
570 | + |
571 | + def on_screen_button_clicked(self): |
572 | + """ |
573 | + Respond to a screen button being clicked |
574 | + """ |
575 | + screen = self.sender().screen |
576 | + if self.current_screen is not screen: |
577 | + self._save_screen(self.current_screen) |
578 | + self.screen_number_label.setText(str(screen)) |
579 | + self.display_group_box.setChecked(screen.is_display) |
580 | + self.full_screen_radio_button.setChecked(screen.custom_geometry is None) |
581 | + self.custom_geometry_button.setChecked(screen.custom_geometry is not None) |
582 | + self._setup_spin_box(self.left_spin_box, screen.display_geometry.y(), screen.display_geometry.right(), |
583 | + screen.display_geometry.x()) |
584 | + self._setup_spin_box(self.top_spin_box, screen.display_geometry.y(), screen.display_geometry.bottom(), |
585 | + screen.display_geometry.y()) |
586 | + self._setup_spin_box(self.width_spin_box, 0, screen.display_geometry.width(), screen.display_geometry.width()) |
587 | + self._setup_spin_box(self.height_spin_box, 0, screen.display_geometry.height(), |
588 | + screen.display_geometry.height()) |
589 | + self.current_screen = screen |
590 | |
591 | === added file 'tests/functional/openlp_core/widgets/test_widgets.py' |
592 | --- tests/functional/openlp_core/widgets/test_widgets.py 1970-01-01 00:00:00 +0000 |
593 | +++ tests/functional/openlp_core/widgets/test_widgets.py 2018-11-10 08:28:19 +0000 |
594 | @@ -0,0 +1,199 @@ |
595 | +# -*- coding: utf-8 -*- |
596 | +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
597 | + |
598 | +############################################################################### |
599 | +# OpenLP - Open Source Lyrics Projection # |
600 | +# --------------------------------------------------------------------------- # |
601 | +# Copyright (c) 2008-2018 OpenLP Developers # |
602 | +# --------------------------------------------------------------------------- # |
603 | +# This program is free software; you can redistribute it and/or modify it # |
604 | +# under the terms of the GNU General Public License as published by the Free # |
605 | +# Software Foundation; version 2 of the License. # |
606 | +# # |
607 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
608 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
609 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
610 | +# more details. # |
611 | +# # |
612 | +# You should have received a copy of the GNU General Public License along # |
613 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
614 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
615 | +############################################################################### |
616 | +""" |
617 | +Package to test the openlp.core.widgets.widgets package. |
618 | +""" |
619 | +from unittest import TestCase |
620 | +from unittest.mock import MagicMock, patch |
621 | + |
622 | +from PyQt5 import QtCore, QtWidgets |
623 | + |
624 | +from openlp.core.display.screens import Screen |
625 | +from openlp.core.widgets.widgets import ScreenButton, ScreenSelectionWidget |
626 | + |
627 | + |
628 | +class TestSceenButton(TestCase): |
629 | + def test_screen_button_initialisation(self): |
630 | + """ |
631 | + Test the initialisation of the ScreenButton object |
632 | + """ |
633 | + # GIVEN: A mocked screen object |
634 | + screen_mock = MagicMock(spec=Screen) |
635 | + screen_mock.number = 0 |
636 | + screen_mock.__str__.return_value = 'Mocked Screen Object' |
637 | + |
638 | + # WHEN: initialising the ScreenButton object |
639 | + instance = ScreenButton(None, screen_mock) |
640 | + |
641 | + # THEN: The ScreenButton should have been initalised correctly with the data from the mocked screen object |
642 | + assert isinstance(instance, QtWidgets.QPushButton) |
643 | + assert instance.objectName() == 'screen_0_button' |
644 | + assert instance.isCheckable() is True |
645 | + assert instance.text() == 'Mocked Screen Object' |
646 | + |
647 | + |
648 | +class TestScreenSelectionWidget(TestCase): |
649 | + def setUp(self): |
650 | + patched_qtimer = patch('openlp.core.widgets.widgets.QtCore.QTimer') |
651 | + self.addCleanup(patched_qtimer.stop) |
652 | + self.timer_mock = MagicMock(spec=QtCore.QTimer) |
653 | + qtimer_mock = patched_qtimer.start() |
654 | + qtimer_mock.return_value = self.timer_mock |
655 | + |
656 | + patched_screen_selection_widget_setup_ui = patch.object(ScreenSelectionWidget, 'setup_ui') |
657 | + self.addCleanup(patched_screen_selection_widget_setup_ui.stop) |
658 | + patched_screen_selection_widget_setup_ui.start() |
659 | + |
660 | + def test_init_default_args(self): |
661 | + """ |
662 | + Test the initialisation of ScreenSelectionWidget, when initialised with default arguments |
663 | + """ |
664 | + # GIVEN: The ScreenSelectionWidget class |
665 | + # WHEN: Initialising ScreenSelectionWidget with default arguments |
666 | + instance = ScreenSelectionWidget() |
667 | + |
668 | + # THEN: ScreenSelectionWidget should be an instance of QWidget and the screens attribute should be an empty list |
669 | + assert isinstance(instance, QtWidgets.QWidget) |
670 | + assert instance.screens == [] |
671 | + self.timer_mock.setSingleShot.assert_called_once_with(True) |
672 | + self.timer_mock.setInterval.assert_called_once_with(3000) |
673 | + |
674 | + def test_init_with_args(self): |
675 | + """ |
676 | + Test the initialisation of ScreenSelectionWidget, when initialised with the screens keyword arg set |
677 | + """ |
678 | + # GIVEN: The ScreenSelectionWidget class |
679 | + screens_object_mock = MagicMock() |
680 | + |
681 | + # WHEN: Initialising ScreenSelectionWidget with the screens keyword arg set |
682 | + instance = ScreenSelectionWidget(screens=screens_object_mock) |
683 | + |
684 | + # THEN: ScreenSelectionWidget should be an instance of QWidget and the screens attribute should the mock used |
685 | + assert isinstance(instance, QtWidgets.QWidget) |
686 | + assert instance.screens is screens_object_mock |
687 | + self.timer_mock.setSingleShot.assert_called_once_with(True) |
688 | + self.timer_mock.setInterval.assert_called_once_with(3000) |
689 | + |
690 | + def test_save_screen_none(self): |
691 | + """ |
692 | + Test ScreenSelectionWidget._save_screen when called with the screen arg set as None |
693 | + """ |
694 | + # GIVEN: An instance of the ScreenSelectionWidget |
695 | + instance = ScreenSelectionWidget() |
696 | + instance.display_group_box = MagicMock(spec=QtWidgets.QGroupBox) |
697 | + |
698 | + # WHEN: Calling _save_screen and no screen is selected |
699 | + instance._save_screen(None) |
700 | + |
701 | + # THEN: _save_screen should return without attempting to write to the screen object |
702 | + instance.display_group_box.isChecked.assert_not_called() |
703 | + |
704 | + def test_save_screen_not_display(self): |
705 | + """ |
706 | + Test ScreenSelectionWidget._save_screen when the display_group_box is not checked. |
707 | + """ |
708 | + # GIVEN: An instance of the ScreenSelectionWidget, and a mocked group_box |
709 | + instance = ScreenSelectionWidget() |
710 | + instance.display_group_box = MagicMock(spec=QtWidgets.QGroupBox) |
711 | + instance.custom_geometry_button = MagicMock(spec=QtWidgets.QRadioButton, **{'isChecked.return_value': False}) |
712 | + mocked_screen_object = MagicMock(spec=Screen) |
713 | + mocked_screen_object.is_dislpay = True |
714 | + |
715 | + # WHEN: display_group_box isn't checked and _save_screen is called with a mocked Screen object. |
716 | + instance.display_group_box.isChecked.return_value = False |
717 | + instance._save_screen(mocked_screen_object) |
718 | + |
719 | + # THEN: _save_screen should should be set to False |
720 | + assert mocked_screen_object.is_display is False |
721 | + |
722 | + def test_save_screen_display(self): |
723 | + """ |
724 | + Test ScreenSelectionWidget._save_screen when the display_group_box is checked. |
725 | + """ |
726 | + # GIVEN: An instance of the ScreenSelectionWidget, and a mocked group_box |
727 | + instance = ScreenSelectionWidget() |
728 | + instance.display_group_box = MagicMock(spec=QtWidgets.QGroupBox) |
729 | + instance.custom_geometry_button = MagicMock(spec=QtWidgets.QRadioButton, **{'isChecked.return_value': False}) |
730 | + mocked_screen_object = MagicMock(spec=Screen) |
731 | + |
732 | + # WHEN: display_group_box is checked and _save_screen is called with a mocked Screen object. |
733 | + instance.display_group_box.isChecked.return_value = True |
734 | + instance._save_screen(mocked_screen_object) |
735 | + |
736 | + # THEN: _save_screen should should be set to True |
737 | + assert mocked_screen_object.is_display is True |
738 | + |
739 | + @patch('openlp.core.widgets.widgets.QtCore.QRect') |
740 | + def test_save_screen_full_screen(self, mocked_q_rect): |
741 | + """ |
742 | + Test ScreenSelectionWidget._save_screen when the display is set to full screen |
743 | + """ |
744 | + # GIVEN: An instance of the ScreenSelectionWidget, and a mocked custom_geometry_button |
745 | + instance = ScreenSelectionWidget() |
746 | + instance.display_group_box = MagicMock(spec=QtWidgets.QGroupBox) |
747 | + instance.custom_geometry_button = MagicMock(spec=QtWidgets.QRadioButton) |
748 | + mocked_screen_object = MagicMock(spec=Screen) |
749 | + |
750 | + # WHEN: custom_geometry_button isn't checked and _save_screen is called with a mocked Screen object. |
751 | + instance.custom_geometry_button.isChecked.return_value = False |
752 | + instance._save_screen(mocked_screen_object) |
753 | + |
754 | + # THEN: _save_screen should not attempt to save a custom geometry |
755 | + mocked_q_rect.assert_not_called() |
756 | + |
757 | + @patch('openlp.core.widgets.widgets.QtCore.QRect') |
758 | + def test_save_screen_custom_geometry(self, mocked_q_rect): |
759 | + """ |
760 | + Test ScreenSelectionWidget._save_screen when a custom geometry is set |
761 | + """ |
762 | + # GIVEN: An instance of the ScreenSelectionWidget, and a mocked custom_geometry_button |
763 | + instance = ScreenSelectionWidget() |
764 | + instance.display_group_box = MagicMock(spec=QtWidgets.QGroupBox) |
765 | + instance.custom_geometry_button = MagicMock(spec=QtWidgets.QRadioButton) |
766 | + instance.left_spin_box = MagicMock(spec=QtWidgets.QSpinBox, **{'value.return_value': 100}) |
767 | + instance.top_spin_box = MagicMock(spec=QtWidgets.QSpinBox, **{'value.return_value': 200}) |
768 | + instance.width_spin_box = MagicMock(spec=QtWidgets.QSpinBox, **{'value.return_value': 300}) |
769 | + instance.height_spin_box = MagicMock(spec=QtWidgets.QSpinBox, **{'value.return_value': 400}) |
770 | + mocked_screen_object = MagicMock(spec=Screen) |
771 | + |
772 | + # WHEN: custom_geometry_button is checked and _save_screen is called with a mocked Screen object. |
773 | + instance.custom_geometry_button.isChecked.return_value = True |
774 | + instance._save_screen(mocked_screen_object) |
775 | + |
776 | + # THEN: _save_screen should save the custom geometry |
777 | + mocked_q_rect.assert_called_once_with(100, 200, 300, 400) |
778 | + |
779 | + def test_setup_spin_box(self): |
780 | + """ |
781 | + Test that ScreenSelectionWidget._setup_spin_box sets up the given spinbox correctly |
782 | + """ |
783 | + # GIVEN: An instance of the ScreenSelectionWidget class and a mocked spin box object |
784 | + instance = ScreenSelectionWidget() |
785 | + spin_box_mock = MagicMock(spec=QtWidgets.QSpinBox) |
786 | + |
787 | + # WHEN: Calling _setup_spin_box with the mocked spin box object and some sample values |
788 | + instance._setup_spin_box(spin_box_mock, 0, 100, 50) |
789 | + |
790 | + # THEN: The mocked spin box object should have been set up with the specified values |
791 | + spin_box_mock.setMinimum.assert_called_once_with(0) |
792 | + spin_box_mock.setMaximum.assert_called_once_with(100) |
793 | + spin_box_mock.setValue.assert_called_once_with(50) |
MERGE! :-D