mirror of https://gitlab.com/openlp/openlp.git
fix the deadlock on macos
This commit is contained in:
parent
32dc18a31f
commit
4351651ad5
|
@ -49,3 +49,5 @@ tags
|
|||
test
|
||||
openlp-test-projectordb.sqlite
|
||||
*/test-results.xml
|
||||
.DS_Store
|
||||
OpenLP.spec
|
||||
|
|
|
@ -18,13 +18,14 @@
|
|||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
from flask import jsonify, Blueprint
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.api.lib import old_auth, old_success_response
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.plugin import PluginStatus, StringContent
|
||||
from openlp.core.state import State
|
||||
|
||||
from flask import jsonify, Blueprint
|
||||
|
||||
core_views = Blueprint('old_core', __name__)
|
||||
|
||||
|
||||
|
@ -54,5 +55,8 @@ def plugin_list():
|
|||
|
||||
@core_views.route('/main/image')
|
||||
def main_image():
|
||||
img = 'data:image/jpeg;base64,{}'.format(Registry().get('live_controller').grab_maindisplay())
|
||||
live_controller = Registry().get('live_controller')
|
||||
img_data = live_controller.staticMetaObject.invokeMethod(
|
||||
live_controller, 'grab_maindisplay', QtCore.Qt.BlockingQueuedConnection, QtCore.Q_RETURN_ARG(str))
|
||||
img = 'data:image/jpeg;base64,{}'.format(img_data)
|
||||
return jsonify({'slide_image': img})
|
||||
|
|
|
@ -19,13 +19,15 @@
|
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from flask import jsonify, request, abort, Blueprint
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.api.lib import login_required
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.plugin import PluginStatus, StringContent
|
||||
from openlp.core.state import State
|
||||
|
||||
from flask import jsonify, request, abort, Blueprint
|
||||
|
||||
core = Blueprint('core', __name__)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -82,6 +84,8 @@ def login():
|
|||
|
||||
@core.route('/live-image')
|
||||
def main_image():
|
||||
controller = Registry().get('live_controller')
|
||||
img = 'data:image/jpeg;base64,{}'.format(controller.grab_maindisplay())
|
||||
live_controller = Registry().get('live_controller')
|
||||
img_data = live_controller.staticMetaObject.invokeMethod(
|
||||
live_controller, 'grab_maindisplay', QtCore.Qt.BlockingQueuedConnection, QtCore.Q_RETURN_ARG(str))
|
||||
img = 'data:image/jpeg;base64,{}'.format(img_data)
|
||||
return jsonify({'binary_image': img})
|
||||
|
|
|
@ -262,7 +262,7 @@ def build_icon(icon):
|
|||
return button_icon
|
||||
|
||||
|
||||
def image_to_byte(image, base_64=True):
|
||||
def image_to_byte(image: QtGui.QPixmap, base_64: bool = True) -> QtCore.QByteArray | str:
|
||||
"""
|
||||
Resize an image to fit on the current screen for the web and returns it as a byte stream.
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ from openlp.core.common import SlideLimits
|
|||
from openlp.core.common.actions import ActionList, CategoryOrder
|
||||
from openlp.core.common.i18n import UiStrings, translate
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.platform import is_macosx, is_wayland_compositor
|
||||
from openlp.core.common.platform import is_wayland_compositor
|
||||
from openlp.core.common.registry import Registry, RegistryBase
|
||||
from openlp.core.common.utils import wait_for
|
||||
from openlp.core.display.screens import ScreenList
|
||||
|
@ -1288,17 +1288,6 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||
return self._capture_maindisplay_desktop()
|
||||
|
||||
def _capture_maindisplay_desktop(self):
|
||||
# At least on macOS, there's a crash when opening /main Remote API endpoint,
|
||||
# due to macOS requiring the screenshot code to be called on the main thread.
|
||||
if is_macosx():
|
||||
self.log_debug('_capture_maindisplay_desktop macos thread-safe call')
|
||||
return self._capture_maindisplay_desktop_mainthread_safe()
|
||||
else:
|
||||
self.log_debug('_capture_maindisplay_desktop default call')
|
||||
return self._capture_maindisplay_desktop_signal()
|
||||
|
||||
@QtCore.pyqtSlot(result='QPixmap')
|
||||
def _capture_maindisplay_desktop_signal(self):
|
||||
current_screen = ScreenList().current
|
||||
display_rect = current_screen.display_geometry
|
||||
screen_rect = current_screen.geometry
|
||||
|
@ -1312,14 +1301,6 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||
win_image = current_screen.try_grab_screen_part(relative_x, relative_y, width, height)
|
||||
return win_image
|
||||
|
||||
def _capture_maindisplay_desktop_mainthread_safe(self):
|
||||
# Using internal Qt's messaging/event system to invoke the function.
|
||||
# Usually we would need to use PyQt's signals, but they aren't blocking. So we had to resort to this solution,
|
||||
# which use a less-documented Qt mechanism to invoke the signal in a blocking way.
|
||||
return QtCore.QMetaObject.invokeMethod(self, '_capture_maindisplay_desktop_signal',
|
||||
QtCore.Qt.ConnectionType.BlockingQueuedConnection,
|
||||
QtCore.Q_RETURN_ARG('QPixmap'))
|
||||
|
||||
def _capture_maindisplay_window(self):
|
||||
win_image = None
|
||||
for display in self.displays:
|
||||
|
@ -1347,7 +1328,8 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||
slide_ready_time = self.slide_changed_time + datetime.timedelta(seconds=slide_delay_time)
|
||||
return datetime.datetime.now() > slide_ready_time
|
||||
|
||||
def grab_maindisplay(self):
|
||||
@QtCore.pyqtSlot(result=str)
|
||||
def grab_maindisplay(self) -> str:
|
||||
"""
|
||||
Gets the last taken screenshot
|
||||
"""
|
||||
|
|
|
@ -111,11 +111,12 @@ def test_login_with_valid_credetials_returns_token(flask_client, settings):
|
|||
|
||||
def test_retrieving_image(flask_client):
|
||||
class FakeController:
|
||||
def grab_maindisplay(self):
|
||||
class FakeImage:
|
||||
def save(self, first, second):
|
||||
pass
|
||||
return FakeImage()
|
||||
@property
|
||||
def staticMetaObject(self):
|
||||
class FakeMetaObject:
|
||||
def invokeMethod(self, obj, meth, conn_type, return_type):
|
||||
return ''
|
||||
return FakeMetaObject()
|
||||
Registry.create().register('live_controller', FakeController())
|
||||
res = flask_client.get('/api/v2/core/live-image').get_json()
|
||||
assert res['binary_image'] != ''
|
||||
|
|
|
@ -1447,8 +1447,6 @@ def _init__capture_maindisplay_mocks(geometry, mocked_screenlist, mocked_applica
|
|||
display_mock = MagicMock(grab_screenshot_safe=MagicMock(return_value=windowed_screenshot_mock), is_display=True)
|
||||
slide_controller.displays = [display_mock]
|
||||
slide_controller.service_item = ServiceItem(None)
|
||||
# Bypassing signal call to avoid test freeze
|
||||
slide_controller._capture_maindisplay_desktop_mainthread_safe = slide_controller._capture_maindisplay_desktop_signal
|
||||
mocked_geometry = MagicMock(
|
||||
x=MagicMock(return_value=geometry[1][0]),
|
||||
y=MagicMock(return_value=geometry[1][1]),
|
||||
|
@ -1614,25 +1612,6 @@ def test__capture_maindisplay_window_fakes_black_screen(mocked_is_wayland_compos
|
|||
assert image.pixelColor(int(geometry[1][2] / 2), int(geometry[1][3] / 2)) == QtGui.QColorConstants.Black
|
||||
|
||||
|
||||
@patch('openlp.core.ui.slidecontroller.is_macosx')
|
||||
def test__capture_maindisplay_desktop_calls_safe_on_macos(mocked_is_macosx, registry, settings):
|
||||
"""
|
||||
Test the _capture_maindisplay_desktop method fallbacks to calling thread-safe code on macOS
|
||||
(avoids a hard crash due to Cocoa/macOS internal details)
|
||||
"""
|
||||
# GIVEN: A mocked system check (running macOS) and mocked maindisplay call
|
||||
mocked_is_macosx.return_value = True
|
||||
slide_controller = SlideController()
|
||||
# slidecontroller._capture_maindisplay_desktop_signal = MagicMock(return_value=QtGui.QPixmap())
|
||||
slide_controller._capture_maindisplay_desktop_mainthread_safe = MagicMock()
|
||||
|
||||
# WHEN: trying to grab desktop screenshot
|
||||
slide_controller._capture_maindisplay_desktop()
|
||||
|
||||
# THEN: Screenshot should have been taken through thread-safe call
|
||||
slide_controller._capture_maindisplay_desktop_mainthread_safe.assert_called_once()
|
||||
|
||||
|
||||
@patch(u'openlp.core.ui.slidecontroller.image_to_byte')
|
||||
def test_grab_maindisplay(mocked_image_to_byte, registry):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue