Merge branch 'issue-1383' into 'master'

Try to fix an issue that only seems to happen on macOS

Closes #1383

See merge request openlp/openlp!640
This commit is contained in:
Raoul Snyman 2023-08-11 22:23:25 +00:00
commit e4a6d647dc
2 changed files with 94 additions and 34 deletions

View File

@ -21,6 +21,7 @@
"""
This is the main window, where all the action happens.
"""
import json
import shutil
from contextlib import contextmanager
from datetime import datetime, date
@ -36,6 +37,7 @@ from openlp.core.common import add_actions
from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.common.applocation import AppLocation
from openlp.core.common.i18n import LanguageManager, UiStrings, translate
from openlp.core.common.json import OpenLPJSONDecoder
from openlp.core.common.mixins import LogMixin, RegistryProperties
from openlp.core.common.path import create_paths, resolve
from openlp.core.common.platform import is_macosx, is_win
@ -531,7 +533,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.screen_updating_lock = Lock()
@contextmanager
def _show_wait_dialog(self, title, message):
def _show_wait_dialog(self, title: str, message: str):
"""
Show a wait dialog, wait for some tasks to complete, and then close it.
"""
@ -612,7 +614,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
if widget:
widget.on_focus()
def on_media_tool_box_changed(self, index):
def on_media_tool_box_changed(self, index: int):
"""
Focus a widget when the media toolbox changes.
"""
@ -620,7 +622,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
if widget:
widget.on_focus()
def on_new_version(self, version):
def on_new_version(self, version: str):
"""
Notifies the user that a newer version of OpenLP is available. Triggered by delay thread and cannot display
popup.
@ -632,7 +634,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
'https://openlp.org/.').format(new=version, current=get_version()[u'full'])
QtWidgets.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Version Updated'), version_text)
def on_new_remote_version(self, version):
def on_new_remote_version(self, version: str):
"""
Notifies the user that a newer version of the web remote is available. Triggered by delay thread and cannot
display popup.
@ -755,7 +757,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
QtWidgets.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Main Display Blanked'),
translate('OpenLP.MainWindow', 'The Main Display has been blanked out'))
def error_message(self, title, message):
def error_message(self, title: str, message: str):
"""
Display an error message
@ -766,7 +768,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.application.splash.close()
QtWidgets.QMessageBox.critical(self, title, message)
def warning_message(self, title, message):
def warning_message(self, title: str, message: str):
"""
Display a warning message
@ -777,7 +779,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.application.splash.close()
QtWidgets.QMessageBox.warning(self, title, message)
def information_message(self, title, message):
def information_message(self, title: str, message: str):
"""
Display an informational message
@ -1011,8 +1013,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.settings.setValue('user interface/is preset layout', True)
self.settings.setValue('projector/show after wizard', True)
def set_view_mode(self, media=True, service=True, theme=True, preview=True,
live=True, projector=True, mode='') -> None:
def set_view_mode(self, media: bool = True, service: bool = True, theme: bool = True, preview: bool = True,
live: bool = True, projector: bool = True, mode: str = '') -> None:
"""
Set OpenLP to a different view mode.
"""
@ -1059,7 +1061,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
finally:
self.screen_updating_lock.release()
def closeEvent(self, event):
def closeEvent(self, event: QtGui.QCloseEvent):
"""
Hook to close the main window and display windows on exit
"""
@ -1104,7 +1106,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
# If we just did a settings import, close without saving changes.
self.clean_up(save_settings=not self.settings_imported)
def eventFilter(self, obj, event):
def eventFilter(self, obj, event: QtCore.QEvent):
if event.type() == QtCore.QEvent.FileOpen:
file_name = event.file()
self.log_debug('Got open file event for {name}!'.format(name=file_name))
@ -1125,7 +1127,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
return super(MainWindow, self).eventFilter(obj, event)
def clean_up(self, save_settings=True):
def clean_up(self, save_settings: bool = True):
"""
Runs all the cleanup code before OpenLP shuts down.
@ -1153,7 +1155,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
# Needed for Windows to stop crashes on exit
Registry().remove('application')
def set_service_modified(self, modified, file_name):
def set_service_modified(self, modified: bool, file_name: Path | str):
"""
This method is called from the ServiceManager to set the title of the main window.
@ -1166,7 +1168,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
title = '{title} - {name}'.format(title=UiStrings().OpenLP, name=file_name)
self.setWindowTitle(title)
def show_status_message(self, message):
def show_status_message(self, message: str):
"""
Show a message in the status bar
"""
@ -1225,25 +1227,25 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.settings.setValue('user interface/is preset layout', False)
self.settings.setValue('user interface/show themes', self.theme_manager_dock.isVisible())
def set_preview_panel_visibility(self, visible):
def set_preview_panel_visibility(self, is_visible: bool):
"""
Sets the visibility of the preview panel including saving the setting and updating the menu.
:param visible: A bool giving the state to set the panel to
:param is_visible: A bool giving the state to set the panel to
True - Visible
False - Hidden
"""
self.preview_controller.panel.setVisible(visible)
self.settings.setValue('user interface/preview panel', visible)
self.view_preview_panel.setChecked(visible)
self.preview_controller.panel.setVisible(is_visible)
self.settings.setValue('user interface/preview panel', is_visible)
self.view_preview_panel.setChecked(is_visible)
self.settings.setValue('user interface/is preset layout', False)
def set_lock_panel(self, lock):
def set_lock_panel(self, is_locked: bool):
"""
Sets the ability to stop the toolbars being changed.
"""
if lock:
if is_locked:
self.theme_manager_dock.setFeatures(QtWidgets.QDockWidget.DockWidgetFeature.NoDockWidgetFeatures)
self.service_manager_dock.setFeatures(QtWidgets.QDockWidget.DockWidgetFeature.NoDockWidgetFeatures)
self.media_manager_dock.setFeatures(QtWidgets.QDockWidget.DockWidgetFeature.NoDockWidgetFeatures)
@ -1267,20 +1269,19 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.view_projector_manager_item.setEnabled(True)
self.view_preview_panel.setEnabled(True)
self.view_live_panel.setEnabled(True)
self.settings.setValue('user interface/lock panel', lock)
self.settings.setValue('user interface/lock panel', is_locked)
def set_live_panel_visibility(self, visible):
def set_live_panel_visibility(self, is_visible: bool):
"""
Sets the visibility of the live panel including saving the setting and updating the menu.
:param visible: A bool giving the state to set the panel to
:param is_visible: A bool giving the state to set the panel to
True - Visible
False - Hidden
"""
self.live_controller.panel.setVisible(visible)
self.settings.setValue('user interface/live panel', visible)
self.view_live_panel.setChecked(visible)
self.live_controller.panel.setVisible(is_visible)
self.settings.setValue('user interface/live panel', is_visible)
self.view_live_panel.setChecked(is_visible)
self.settings.setValue('user interface/is preset layout', False)
def load_settings(self):
@ -1303,7 +1304,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
# which was True (by default) < OpenLP 2.1.
self.control_splitter.setChildrenCollapsible(False)
def _window_position_is_valid(self, position, geometry):
def _window_position_is_valid(self, position: QtCore.QPoint, geometry: QtCore.QRect):
"""
Checks if the saved window position is still valid by checking if the bar at the top of the window
(which allows the user to move the window) appears on one of the screens.
@ -1347,6 +1348,23 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
Updates the recent file menu with the latest list of service files accessed.
"""
recent_file_count = self.settings.value('advanced/recent file count')
# This is to get around a weird issue that we're seeing on macOS. We've not been able to reproduce it
# ourselves, but hopefully this will catch the issue.
# See https://gitlab.com/openlp/openlp/-/issues/1383
if isinstance(self.recent_files, str):
try:
self.recent_files = json.loads(self.recent_files, cls=OpenLPJSONDecoder)
except json.JSONDecodeError as e:
self.log_exception(e)
self.recent_files = []
elif isinstance(self.recent_files, list) and self.recent_files and isinstance(self.recent_files[0], str) and \
self.recent_files[0].startswith('['):
try:
self.recent_files = json.loads(self.recent_files[0], cls=OpenLPJSONDecoder)
except json.JSONDecodeError as e:
self.log_exception(e)
self.recent_files = []
# Now continue as usual...
self.recent_files_menu.clear()
count = 0
for recent_path in self.recent_files:
@ -1369,7 +1387,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
enabled=bool(self.recent_files), triggers=self.clear_recent_file_menu)
add_actions(self.recent_files_menu, (None, clear_recent_files_action))
def add_recent_file(self, filename):
def add_recent_file(self, filename: Path | str):
"""
Adds a service to the list of recently used files.
@ -1393,7 +1411,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
"""
self.recent_files = []
def display_progress_bar(self, size):
def display_progress_bar(self, size: int):
"""
Make Progress bar visible and set size
"""
@ -1402,7 +1420,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.load_progress_bar.setValue(0)
self.application.process_events()
def increment_progress_bar(self, increment=1):
def increment_progress_bar(self, increment: int = 1):
"""
Increase the Progress Bar by the value in increment.
@ -1417,7 +1435,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
"""
self.timer_id = self.startTimer(2500)
def timerEvent(self, event):
def timerEvent(self, event: QtCore.QTimerEvent):
"""
Remove the Progress bar from view.
"""

View File

@ -40,6 +40,10 @@ from openlp.core.ui.mainwindow import MainWindow
from tests.utils.constants import TEST_RESOURCES_PATH, RESOURCE_PATH
JSON_RECENT_FILES = ('[{"parts": ["/", "path", "to", "service-1.osz"], "json_meta": {"class": "Path", "version": 1}},'
'{"parts": ["/", "path", "to", "service-2.osz"], "json_meta": {"class": "Path", "version": 1}}]')
def _create_mock_action(parent, name, **kwargs):
"""
Create a fake action with some "real" attributes
@ -823,7 +827,45 @@ def test_update_recent_files_menu(mocked_create_action, mocked_add_actions, Mock
# GIVEN: A mocked settings object, and some other fixtures
MockPath.return_value.is_file.side_effect = [False, True]
settings.setValue('advanced/recent file count', 5)
main_window_reduced.recent_files = [None, '/fake/path', '/path/to/real/file']
main_window_reduced.recent_files = [None, '/fake/path', Path('/path/to/real/file')]
main_window_reduced.recent_files_menu = MagicMock()
# WHEN: update_recent_files_menu() is called
main_window_reduced.update_recent_files_menu()
# THEN: There should be no errors
assert mocked_create_action.call_count == 2
@patch('openlp.core.ui.mainwindow.Path')
@patch('openlp.core.ui.mainwindow.add_actions')
@patch('openlp.core.ui.mainwindow.create_action')
def test_update_recent_files_menu_as_string(mocked_create_action, mocked_add_actions, MockPath, settings, registry,
main_window_reduced):
"""Test that the update_recent_files_menu() method works correctly when the entry is a string instead of a list"""
# GIVEN: A mocked settings object, and some other fixtures
MockPath.return_value.is_file.side_effect = [False, True]
settings.setValue('advanced/recent file count', 5)
main_window_reduced.recent_files = JSON_RECENT_FILES
main_window_reduced.recent_files_menu = MagicMock()
# WHEN: update_recent_files_menu() is called
main_window_reduced.update_recent_files_menu()
# THEN: There should be no errors
assert mocked_create_action.call_count == 2
@patch('openlp.core.ui.mainwindow.Path')
@patch('openlp.core.ui.mainwindow.add_actions')
@patch('openlp.core.ui.mainwindow.create_action')
def test_update_recent_files_menu_as_list_with_string(mocked_create_action, mocked_add_actions, MockPath, settings,
registry, main_window_reduced):
"""Test that the update_recent_files_menu() method works correctly when the entry is a string instead of a list"""
# GIVEN: A mocked settings object, and some other fixtures
MockPath.return_value.is_file.side_effect = [False, True]
settings.setValue('advanced/recent file count', 5)
main_window_reduced.recent_files = [JSON_RECENT_FILES]
main_window_reduced.recent_files_menu = MagicMock()
# WHEN: update_recent_files_menu() is called