forked from openlp/openlp
Remove and add screens without reloading screens. Do not show screen-change-popup when the settings form is visible.
This commit is contained in:
parent
eac44f4f24
commit
73221cef9b
@ -83,7 +83,7 @@ class OpenLP(QtCore.QObject, LogMixin):
|
|||||||
self.server.close_server()
|
self.server.close_server()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def run(self, args):
|
def run(self, args, app):
|
||||||
"""
|
"""
|
||||||
Run the OpenLP application.
|
Run the OpenLP application.
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ class OpenLP(QtCore.QObject, LogMixin):
|
|||||||
args.remove('OpenLP')
|
args.remove('OpenLP')
|
||||||
self.args.extend(args)
|
self.args.extend(args)
|
||||||
# Decide how many screens we have and their size
|
# Decide how many screens we have and their size
|
||||||
screens = ScreenList.create(QtWidgets.QApplication.desktop())
|
screens = ScreenList.create(app)
|
||||||
# First time checks in settings
|
# First time checks in settings
|
||||||
has_run_wizard = self.settings.value('core/has run wizard')
|
has_run_wizard = self.settings.value('core/has run wizard')
|
||||||
if not has_run_wizard:
|
if not has_run_wizard:
|
||||||
@ -435,4 +435,4 @@ def main():
|
|||||||
log.debug('Could not find translators.')
|
log.debug('Could not find translators.')
|
||||||
if args and not args.no_error_form:
|
if args and not args.no_error_form:
|
||||||
sys.excepthook = app.hook_exception
|
sys.excepthook = app.hook_exception
|
||||||
sys.exit(app.run(qt_args))
|
sys.exit(app.run(qt_args, application))
|
||||||
|
@ -23,6 +23,7 @@ The :mod:`screen` module provides management functionality for a machines'
|
|||||||
displays.
|
displays.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
import copy
|
||||||
from functools import cmp_to_key
|
from functools import cmp_to_key
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
from PyQt5 import QtCore, QtWidgets
|
||||||
@ -77,7 +78,14 @@ class Screen(object):
|
|||||||
"""
|
"""
|
||||||
Returns the geometry to use when displaying. This property decides between the native and custom geometries
|
Returns the geometry to use when displaying. This property decides between the native and custom geometries
|
||||||
"""
|
"""
|
||||||
return self.custom_geometry or self.geometry
|
# If custom geometry is used, convert to absolute position
|
||||||
|
if self.custom_geometry:
|
||||||
|
adjusted_custom_geometry = copy.deepcopy(self.custom_geometry)
|
||||||
|
adjusted_custom_geometry.moveTo(self.geometry.x() + adjusted_custom_geometry.x(),
|
||||||
|
self.geometry.y() + adjusted_custom_geometry.y())
|
||||||
|
return adjusted_custom_geometry
|
||||||
|
else:
|
||||||
|
return self.geometry
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, screen_dict):
|
def from_dict(cls, screen_dict):
|
||||||
@ -147,6 +155,13 @@ class Screen(object):
|
|||||||
screen_dict['custom_geometry']['width'],
|
screen_dict['custom_geometry']['width'],
|
||||||
screen_dict['custom_geometry']['height'])
|
screen_dict['custom_geometry']['height'])
|
||||||
|
|
||||||
|
def on_geometry_changed(self, geometry):
|
||||||
|
"""
|
||||||
|
Callback function for when the screens geometry changes
|
||||||
|
"""
|
||||||
|
self.geometry = geometry
|
||||||
|
Registry().execute('config_screen_changed')
|
||||||
|
|
||||||
|
|
||||||
class ScreenList(metaclass=Singleton):
|
class ScreenList(metaclass=Singleton):
|
||||||
"""
|
"""
|
||||||
@ -202,37 +217,66 @@ class ScreenList(metaclass=Singleton):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, desktop):
|
def create(cls, application):
|
||||||
"""
|
"""
|
||||||
Initialise the screen list.
|
Initialise the screen list.
|
||||||
|
|
||||||
:param desktop: A QDesktopWidget object.
|
:param desktop: A QApplication object.
|
||||||
"""
|
"""
|
||||||
screen_list = cls()
|
screen_list = cls()
|
||||||
screen_list.desktop = desktop
|
screen_list.application = application
|
||||||
screen_list.desktop.resized.connect(screen_list.on_screen_resolution_changed)
|
screen_list.application.primaryScreenChanged.connect(screen_list.on_primary_screen_changed)
|
||||||
screen_list.desktop.screenCountChanged.connect(screen_list.on_screen_count_changed)
|
screen_list.application.screenAdded.connect(screen_list.on_screen_added)
|
||||||
screen_list.desktop.primaryScreenChanged.connect(screen_list.on_primary_screen_changed)
|
screen_list.application.screenRemoved.connect(screen_list.on_screen_removed)
|
||||||
screen_list.update_screens()
|
screen_list.update_screens()
|
||||||
cls.settings = Registry().get('settings')
|
cls.settings = Registry().get('settings')
|
||||||
screen_list.load_screen_settings()
|
screen_list.load_screen_settings()
|
||||||
return screen_list
|
return screen_list
|
||||||
|
|
||||||
|
def find_new_display_screen(self):
|
||||||
|
"""
|
||||||
|
If more than 1 screen, set first non-primary screen to display, otherwise just set the available screen as
|
||||||
|
display.
|
||||||
|
"""
|
||||||
|
if len(self) > 1:
|
||||||
|
for screen in self:
|
||||||
|
if not screen.is_primary:
|
||||||
|
screen.is_display = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self[0].is_display = True
|
||||||
|
|
||||||
def load_screen_settings(self):
|
def load_screen_settings(self):
|
||||||
"""
|
"""
|
||||||
Loads the screen size and the screen number from the settings.
|
Loads the screen size and the screen number from the settings.
|
||||||
"""
|
"""
|
||||||
screen_settings = self.settings.value('core/screens')
|
screen_settings = self.settings.value('core/screens')
|
||||||
if screen_settings:
|
if screen_settings:
|
||||||
|
need_new_display_screen = False
|
||||||
for number, screen_dict in screen_settings.items():
|
for number, screen_dict in screen_settings.items():
|
||||||
# Sometimes this loads as a string instead of an int
|
# Sometimes this loads as a string instead of an int
|
||||||
number = int(number)
|
number = int(number)
|
||||||
if self.has_screen(number):
|
# Compare geometry, primarity of screen from settings with avilable screens
|
||||||
|
if self.has_screen(screen_dict):
|
||||||
|
# If match was found, we're all happy, update with custom geometry, display info, if available
|
||||||
self[number].update(screen_dict)
|
self[number].update(screen_dict)
|
||||||
else:
|
else:
|
||||||
self.screens.append(Screen.from_dict(screen_dict))
|
# If no match, ignore this screen, also need to find new display screen if the discarded screen was
|
||||||
|
# marked as such.
|
||||||
|
if screen_dict['is_display']:
|
||||||
|
need_new_display_screen = True
|
||||||
|
if need_new_display_screen:
|
||||||
|
QtWidgets.QMessageBox.warning(None, translate('OpenLP.Screen',
|
||||||
|
'Screen settings and screen setup is not the same'),
|
||||||
|
translate('OpenLP.Screen',
|
||||||
|
'There is a mismatch between screens and screen settings. '
|
||||||
|
'OpenLP will try to automatically select a display screen, but '
|
||||||
|
'you should consider updating the screen settings.'),
|
||||||
|
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
|
||||||
|
self.find_new_display_screen()
|
||||||
else:
|
else:
|
||||||
self[len(self) - 1].is_display = True
|
# if no settings we need to set a display
|
||||||
|
self.find_new_display_screen()
|
||||||
|
|
||||||
def save_screen_settings(self):
|
def save_screen_settings(self):
|
||||||
"""
|
"""
|
||||||
@ -285,14 +329,15 @@ class ScreenList(metaclass=Singleton):
|
|||||||
if can_save:
|
if can_save:
|
||||||
self.save_screen_settings()
|
self.save_screen_settings()
|
||||||
|
|
||||||
def has_screen(self, number):
|
def has_screen(self, screen_dict):
|
||||||
"""
|
"""
|
||||||
Confirms a screen is known.
|
Confirms a screen is known.
|
||||||
|
|
||||||
:param number: The screen number (int).
|
:param screen_dict: The dict descrebing the screen.
|
||||||
"""
|
"""
|
||||||
for screen in self.screens:
|
for screen in self.screens:
|
||||||
if screen.number == number:
|
if screen.to_dict()['geometry'] == screen_dict['geometry'] \
|
||||||
|
and screen.is_primary == screen_dict['is_primary']:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -316,39 +361,44 @@ class ScreenList(metaclass=Singleton):
|
|||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
self.screens = []
|
self.screens = []
|
||||||
os_screens = QtWidgets.QApplication.screens()
|
os_screens = self.application.screens()
|
||||||
os_screens.sort(key=cmp_to_key(_screen_compare))
|
os_screens.sort(key=cmp_to_key(_screen_compare))
|
||||||
for number, screen in enumerate(os_screens):
|
for number, screen in enumerate(os_screens):
|
||||||
self.screens.append(
|
self.screens.append(
|
||||||
Screen(number, screen.geometry(), is_primary=self.desktop.primaryScreen() == number))
|
Screen(number, screen.geometry(), is_primary=self.application.primaryScreen() == screen))
|
||||||
|
screen.geometryChanged.connect(self.screens[-1].on_geometry_changed)
|
||||||
|
|
||||||
def on_screen_resolution_changed(self, number):
|
def on_screen_added(self, changed_screen):
|
||||||
"""
|
"""
|
||||||
Called when the resolution of a screen has changed.
|
Called when a screen has been added
|
||||||
|
|
||||||
``number``
|
:param changed_screen: The screen which has been plugged.
|
||||||
The number of the screen, which size has changed.
|
|
||||||
"""
|
"""
|
||||||
log.info('screen_resolution_changed {number:d}'.format(number=number))
|
number = len(self.screens)
|
||||||
|
self.screens.append(Screen(number, changed_screen.geometry(),
|
||||||
|
is_primary=self.application.primaryScreen() == changed_screen))
|
||||||
|
changed_screen.geometryChanged.connect(self.screens[-1].on_geometry_changed)
|
||||||
|
Registry().execute('config_screen_changed')
|
||||||
|
|
||||||
|
def on_screen_removed(self, removed_screen):
|
||||||
|
"""
|
||||||
|
Called when a screen has been removed.
|
||||||
|
|
||||||
|
:param changed_screen: The screen which has been unplugged.
|
||||||
|
"""
|
||||||
|
# Remove screens
|
||||||
|
removed_screen_number = -1
|
||||||
for screen in self.screens:
|
for screen in self.screens:
|
||||||
if number == screen.number:
|
# once the screen that must be removed has been found, update numbering
|
||||||
screen.geometry = self.desktop.screenGeometry(number)
|
if removed_screen_number >= 0:
|
||||||
screen.is_primary = self.desktop.primaryScreen() == number
|
screen.number -= 1
|
||||||
Registry().execute('config_screen_changed')
|
# find the screen that is removed
|
||||||
break
|
if removed_screen.geometry() == screen.geometry:
|
||||||
|
removed_screen_number = screen.number
|
||||||
def on_screen_count_changed(self, changed_screen=None):
|
removed_screen_is_display = self.screens[removed_screen_number].is_display
|
||||||
"""
|
self.screens.pop(removed_screen_number)
|
||||||
Called when a screen has been added or removed.
|
if removed_screen_is_display:
|
||||||
|
self.find_new_display_screen()
|
||||||
``changed_screen``
|
|
||||||
The screen's number which has been (un)plugged.
|
|
||||||
"""
|
|
||||||
screen_count = self.desktop.screenCount()
|
|
||||||
log.info('screen_count_changed {count:d}'.format(count=screen_count))
|
|
||||||
# Update the list of screens
|
|
||||||
self.update_screens()
|
|
||||||
# Reload setting tabs to apply possible changes.
|
|
||||||
Registry().execute('config_screen_changed')
|
Registry().execute('config_screen_changed')
|
||||||
|
|
||||||
def on_primary_screen_changed(self):
|
def on_primary_screen_changed(self):
|
||||||
@ -356,5 +406,5 @@ class ScreenList(metaclass=Singleton):
|
|||||||
The primary screen has changed, let's sort it out and then notify everyone
|
The primary screen has changed, let's sort it out and then notify everyone
|
||||||
"""
|
"""
|
||||||
for screen in self.screens:
|
for screen in self.screens:
|
||||||
screen.is_primary = self.desktop.primaryScreen() == screen.number
|
screen.is_primary = self.desktop.primaryScreen().geometry() == screen.geometry
|
||||||
Registry().execute('config_screen_changed')
|
Registry().execute('config_screen_changed')
|
||||||
|
@ -521,6 +521,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
|
|||||||
# Media Manager
|
# Media Manager
|
||||||
self.media_tool_box.currentChanged.connect(self.on_media_tool_box_changed)
|
self.media_tool_box.currentChanged.connect(self.on_media_tool_box_changed)
|
||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
|
# Timestamp for latest screen-change-popup. Used to prevent spamming the user with popups
|
||||||
|
self.screen_change_timestamp = None
|
||||||
# Simple message boxes
|
# Simple message boxes
|
||||||
Registry().register_function('theme_update_global', self.default_theme_changed)
|
Registry().register_function('theme_update_global', self.default_theme_changed)
|
||||||
Registry().register_function('config_screen_changed', self.screen_changed)
|
Registry().register_function('config_screen_changed', self.screen_changed)
|
||||||
@ -999,6 +1001,17 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
|
|||||||
self.setFocus()
|
self.setFocus()
|
||||||
self.activateWindow()
|
self.activateWindow()
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
|
# if a warning has been shown within the last 5 seconds, skip showing again to avoid spamming user,
|
||||||
|
# also do not show if the settings window is visible
|
||||||
|
if not self.settings_form.isVisible() and \
|
||||||
|
not self.screen_change_timestamp or (datetime.now() - self.screen_change_timestamp).seconds > 5:
|
||||||
|
QtWidgets.QMessageBox.warning(self, translate('OpenLP.MainWindow', 'Screen setup has changed'),
|
||||||
|
translate('OpenLP.MainWindow',
|
||||||
|
'The screen setup has changed. '
|
||||||
|
'OpenLP will try to automatically select a display screen, but '
|
||||||
|
'you should consider updating the screen settings.'),
|
||||||
|
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
|
||||||
|
self.screen_change_timestamp = datetime.now()
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
"""
|
"""
|
||||||
|
@ -661,9 +661,16 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
Settings dialog has changed the screen size of adjust output and screen previews.
|
Settings dialog has changed the screen size of adjust output and screen previews.
|
||||||
"""
|
"""
|
||||||
|
size = self.screens.current.display_geometry.size()
|
||||||
if self.is_live and self.displays:
|
if self.is_live and self.displays:
|
||||||
for display in self.displays:
|
for display in self.displays:
|
||||||
display.resize(self.screens.current.display_geometry.size())
|
display.resize(size)
|
||||||
|
old_preview_width = self.preview_display.size().width()
|
||||||
|
scale = old_preview_width / size.width()
|
||||||
|
new_preview_size = size * scale
|
||||||
|
self.ratio = self.screens.current.display_geometry.width() / self.screens.current.display_geometry.height()
|
||||||
|
self.preview_display.resize(new_preview_size)
|
||||||
|
self.slide_layout.set_aspect_ratio(self.ratio)
|
||||||
|
|
||||||
def __add_actions_to_widget(self, widget):
|
def __add_actions_to_widget(self, widget):
|
||||||
"""
|
"""
|
||||||
|
@ -60,8 +60,10 @@ class AspectRatioLayout(QtWidgets.QLayout):
|
|||||||
|
|
||||||
:param float aspect_ratio: The aspect ratio to set
|
:param float aspect_ratio: The aspect ratio to set
|
||||||
"""
|
"""
|
||||||
# TODO: Update the layout/widget if this changes
|
|
||||||
self._aspect_ratio = aspect_ratio
|
self._aspect_ratio = aspect_ratio
|
||||||
|
# Update the layout/widget
|
||||||
|
geo = self.geometry()
|
||||||
|
self.setGeometry(geo)
|
||||||
|
|
||||||
aspect_ratio = property(get_aspect_ratio, set_aspect_ratio)
|
aspect_ratio = property(get_aspect_ratio, set_aspect_ratio)
|
||||||
|
|
||||||
|
@ -74,17 +74,15 @@ def test_create_screen_list(mocked_screens, settings):
|
|||||||
"""
|
"""
|
||||||
Create the screen list
|
Create the screen list
|
||||||
"""
|
"""
|
||||||
# GIVEN: Mocked desktop
|
# GIVEN: Mocked application
|
||||||
mocked_desktop = MagicMock()
|
mocked_application = MagicMock()
|
||||||
mocked_desktop.screenCount.return_value = 2
|
mocked_screen1 = MagicMock(**{'geometry.return_value': QtCore.QRect(0, 0, 1024, 768)})
|
||||||
mocked_desktop.primaryScreen.return_value = 0
|
mocked_screen2 = MagicMock(**{'geometry.return_value': QtCore.QRect(1024, 0, 1024, 768)})
|
||||||
mocked_screens.return_value = [
|
mocked_application.screens.return_value = [mocked_screen1, mocked_screen2]
|
||||||
MagicMock(**{'geometry.return_value': QtCore.QRect(0, 0, 1024, 768)}),
|
mocked_application.primaryScreen.return_value = mocked_screen1
|
||||||
MagicMock(**{'geometry.return_value': QtCore.QRect(1024, 0, 1024, 768)})
|
|
||||||
]
|
|
||||||
|
|
||||||
# WHEN: create() is called
|
# WHEN: create() is called
|
||||||
screen_list = ScreenList.create(mocked_desktop)
|
screen_list = ScreenList.create(mocked_application)
|
||||||
|
|
||||||
# THEN: The correct screens have been set up
|
# THEN: The correct screens have been set up
|
||||||
assert screen_list.screens[0].number == 0
|
assert screen_list.screens[0].number == 0
|
||||||
|
@ -47,7 +47,7 @@ def _create_mock_action(parent, name, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture()
|
@pytest.yield_fixture()
|
||||||
def main_window(state, settings):
|
def main_window(state, settings, mocked_qapp):
|
||||||
app = Registry().get('application')
|
app = Registry().get('application')
|
||||||
app.set_busy_cursor = MagicMock()
|
app.set_busy_cursor = MagicMock()
|
||||||
app.set_normal_cursor = MagicMock()
|
app.set_normal_cursor = MagicMock()
|
||||||
@ -59,11 +59,13 @@ def main_window(state, settings):
|
|||||||
mocked_add_toolbar_action.side_effect = _create_mock_action
|
mocked_add_toolbar_action.side_effect = _create_mock_action
|
||||||
renderer_patcher = patch('openlp.core.display.render.Renderer')
|
renderer_patcher = patch('openlp.core.display.render.Renderer')
|
||||||
renderer_patcher.start()
|
renderer_patcher.start()
|
||||||
mocked_desktop = MagicMock()
|
mocked_screen = MagicMock()
|
||||||
mocked_desktop.screenCount.return_value = 1
|
mocked_screen.geometry.return_value = QtCore.QRect(0, 0, 1024, 768)
|
||||||
mocked_desktop.screenGeometry.return_value = QtCore.QRect(0, 0, 1024, 768)
|
mocked_qapp.screens = MagicMock()
|
||||||
mocked_desktop.primaryScreen.return_value = 1
|
mocked_qapp.screens.return_value = [mocked_screen]
|
||||||
ScreenList.create(mocked_desktop)
|
mocked_qapp.primaryScreen = MagicMock()
|
||||||
|
mocked_qapp.primaryScreen.return_value = mocked_screen
|
||||||
|
ScreenList.create(mocked_qapp)
|
||||||
mainwindow = MainWindow()
|
mainwindow = MainWindow()
|
||||||
yield mainwindow
|
yield mainwindow
|
||||||
del mainwindow
|
del mainwindow
|
||||||
|
@ -38,14 +38,15 @@ from tests.utils.constants import RESOURCE_PATH
|
|||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture()
|
@pytest.yield_fixture()
|
||||||
def pdf_env(settings, mock_plugin):
|
def pdf_env(settings, mock_plugin, mocked_qapp):
|
||||||
temp_folder_path = Path(mkdtemp())
|
temp_folder_path = Path(mkdtemp())
|
||||||
thumbnail_folder_path = Path(mkdtemp())
|
thumbnail_folder_path = Path(mkdtemp())
|
||||||
desktop = MagicMock()
|
mocked_screen = MagicMock()
|
||||||
desktop.primaryScreen.return_value = SCREEN['primary']
|
mocked_screen.geometry.return_value = QtCore.QRect(0, 0, 1024, 768)
|
||||||
desktop.screenCount.return_value = SCREEN['number']
|
mocked_qapp.screens.return_value = [mocked_screen]
|
||||||
desktop.screenGeometry.return_value = SCREEN['size']
|
mocked_qapp.primaryScreen = MagicMock()
|
||||||
ScreenList.create(desktop)
|
mocked_qapp.primaryScreen.return_value = mocked_screen
|
||||||
|
ScreenList.create(mocked_qapp)
|
||||||
yield settings, mock_plugin, temp_folder_path, thumbnail_folder_path
|
yield settings, mock_plugin, temp_folder_path, thumbnail_folder_path
|
||||||
rmtree(thumbnail_folder_path)
|
rmtree(thumbnail_folder_path)
|
||||||
rmtree(temp_folder_path)
|
rmtree(temp_folder_path)
|
||||||
|
Loading…
Reference in New Issue
Block a user