diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2023-08-02 08:52:31 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2023-08-04 09:52:45 +0200 |
commit | 39e65f1719b98d58408a5b7c0dddfb6664a41afb (patch) | |
tree | ce5cac95ea78b5e425fff122485556f39705ed08 /examples/multimedia/screencapture | |
parent | 40ec55c2606892848f8b4855d8e761cdf2e49656 (diff) |
Multimedia screen capture example: Add window capture
Port from qtmultimedia/3edff8e367b9060dd138a2b67cb87d2246a4a3e6.
Task-number: PYSIDE-2206
Change-Id: Ia702faf47946a0f656b1546b205dfb442cf2f56a
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Reviewed-by: Shyamnath Premnadh <Shyamnath.Premnadh@qt.io>
Reviewed-by: Adrian Herrmann <adrian.herrmann@qt.io>
Diffstat (limited to 'examples/multimedia/screencapture')
-rw-r--r-- | examples/multimedia/screencapture/doc/screencapture.rst | 57 | ||||
-rw-r--r-- | examples/multimedia/screencapture/doc/screencapture.webp | bin | 20604 -> 53592 bytes | |||
-rw-r--r-- | examples/multimedia/screencapture/main.py | 3 | ||||
-rw-r--r-- | examples/multimedia/screencapture/screencapture.pyproject | 2 | ||||
-rw-r--r-- | examples/multimedia/screencapture/screencapturepreview.py | 132 | ||||
-rw-r--r-- | examples/multimedia/screencapture/windowlistmodel.py | 30 |
6 files changed, 171 insertions, 53 deletions
diff --git a/examples/multimedia/screencapture/doc/screencapture.rst b/examples/multimedia/screencapture/doc/screencapture.rst index 69b1ec166..116d7773b 100644 --- a/examples/multimedia/screencapture/doc/screencapture.rst +++ b/examples/multimedia/screencapture/doc/screencapture.rst @@ -1,35 +1,42 @@ Screen Capture Example ====================== -The Screen Capture Example demonstrates how to capture a screen or window using -QScreenCapture. It shows a list of screens and and displays a live preview of -the selected item using a ``QMediaCaptureSession`` and a ``QVideoWidget``. -There is a button to start and stop the capturing. +Screen Capture demonstrates how to capture a screen or window using +``QScreenCapture`` and ``QWindowCapture``. The example shows a list of screens +and windows and displays a live preview of the selected item using a +``QMediaCaptureSession`` and a ``QVideoWidget``. Capturing can be started and +stopped with a ``QPushButton``. Application Structure +++++++++++++++++++++ -The example consists of two custom classes. The UI and all screen capture +The example consists of three custom classes. The UI and all screen capture functionality is implemented in the class ``ScreenCapturePreview``. The classes -``ScreenListModel`` serves as model behind the ``QListView``. The main function -creates a ``ScreenCapturePreview`` object, which in turn creates an instance of -``QScreenCapture``, ``QMediaCaptureSession`` and ``QVideoWidget`` in addition -to all the UI widgets. - -The list model is populated with the return values of ``QGuiApplication.screens()``. - -When a list item is selected it is connected to the ``QScreenCapture`` object -with ``QScreenCapture.setScreen()``. The ``QScreenCapture`` object is connected -to the ``QMediaCaptureSession`` object with -``QMediaCaptureSession.setScreenCapture()``, which in turn is connected to the -``QVideoWidget`` object with ``QMediaCaptureSession.setVideoOutput()`` Thus the -screen capture output is previewed in the video widget on the right hand side -of the UI. - -The start/stop button calls ``QScreenCapture.start()`` and ``QScreenCapture.stop()``. - -A ``QMessageBox`` pops up if the ``QScreenCapture.errorOccurred()`` signal is emitted. - -.. image:: screencapture.webp +``ScreenListModel`` and ``WindowListModel`` only serve as models behind the two +``QListView`` widgets. The main function creates a ``ScreenCapturePreview`` +object, which in turn creates instances of ``QScreenCapture`` and +``QWindowCapture``, and a ``QMediaCaptureSession`` and ``QVideoWidget``, in +addition to all the UI widgets. + +The screen and window models are populated with the return values of +``QGuiApplication.screens()`` and ``QWindowCapture.capturableWindows()``, +respectively. + +When a list item is selected, it is connected to the ``QScreenCapture`` object +with ``QScreenCapture.setScreen()``, or to the ``QWindowCapture`` object with +``QWindowCapture.setWindow().`` The capture object is connected to the +``QMediaCaptureSession`` object with +``QMediaCaptureSession.setScreenCapture()`` and +``QMediaCaptureSession.setWindowCapture()``, respectively. The capture session +in turn is connected to the ``QVideoWidget`` object with +``QMediaCaptureSession.setVideoOutput()``. Thus, the capture output is +previewed in the video widget on the right hand side of the UI. + +The start/stop button calls ``QScreenCapture.start()`` and ``QScreenCapture.stop()``, +or ``QWindowCapture.start()`` and ``QWindowCapture.stop()``. + +A QMessageBox pops up if an ``errorOccurred`` signal is emitted. + +.. image. screencapture.webp :width: 600 :alt: screen capture example diff --git a/examples/multimedia/screencapture/doc/screencapture.webp b/examples/multimedia/screencapture/doc/screencapture.webp Binary files differindex 2723b1d53..58ad36c7f 100644 --- a/examples/multimedia/screencapture/doc/screencapture.webp +++ b/examples/multimedia/screencapture/doc/screencapture.webp diff --git a/examples/multimedia/screencapture/main.py b/examples/multimedia/screencapture/main.py index dce30186c..f445bac03 100644 --- a/examples/multimedia/screencapture/main.py +++ b/examples/multimedia/screencapture/main.py @@ -5,6 +5,7 @@ import sys +from PySide6.QtCore import QCoreApplication from PySide6.QtWidgets import QApplication from screencapturepreview import ScreenCapturePreview @@ -12,6 +13,8 @@ from screencapturepreview import ScreenCapturePreview if __name__ == "__main__": app = QApplication(sys.argv) + QCoreApplication.setApplicationName("screencapture") + QCoreApplication.setOrganizationName("QtProject") screen_capture_preview = ScreenCapturePreview() screen_capture_preview.show() sys.exit(app.exec()) diff --git a/examples/multimedia/screencapture/screencapture.pyproject b/examples/multimedia/screencapture/screencapture.pyproject index 9b18fa093..dfec6c901 100644 --- a/examples/multimedia/screencapture/screencapture.pyproject +++ b/examples/multimedia/screencapture/screencapture.pyproject @@ -1,3 +1,3 @@ { - "files": ["main.py", "screencapturepreview.py", "screenlistmodel.py"] + "files": ["main.py", "screencapturepreview.py", "screenlistmodel.py", "windowlistmodel.py"] } diff --git a/examples/multimedia/screencapture/screencapturepreview.py b/examples/multimedia/screencapture/screencapturepreview.py index 72fe77c64..3f75a0601 100644 --- a/examples/multimedia/screencapture/screencapturepreview.py +++ b/examples/multimedia/screencapture/screencapturepreview.py @@ -1,14 +1,23 @@ # Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +from enum import Enum, auto + from PySide6.QtMultimediaWidgets import QVideoWidget -from PySide6.QtMultimedia import QScreenCapture, QMediaCaptureSession +from PySide6.QtMultimedia import (QCapturableWindow, QMediaCaptureSession, + QScreenCapture, QWindowCapture) from PySide6.QtWidgets import (QGridLayout, QLabel, QListView, QMessageBox, QPushButton, QWidget) -from PySide6.QtGui import QGuiApplication -from PySide6.QtCore import Slot +from PySide6.QtGui import QAction, QGuiApplication +from PySide6.QtCore import QItemSelection, Qt, Slot from screenlistmodel import ScreenListModel +from windowlistmodel import WindowListModel + + +class SourceType(Enum): + Screen = auto() + Window = auto() class ScreenCapturePreview(QWidget): @@ -16,58 +25,127 @@ class ScreenCapturePreview(QWidget): def __init__(self, parent=None): super().__init__(parent) + self._source = SourceType.Screen + self._screen_capture = QScreenCapture(self) self._media_capture_session = QMediaCaptureSession(self) self._video_widget = QVideoWidget(self) self._screen_list_view = QListView(self) - self._screen_label = QLabel("Double-click screen to capture:", self) - self._video_widget_label = QLabel("QScreenCapture output:", self) - self._start_stop_button = QPushButton("Stop screencapture", self) + self._screen_label = QLabel("Select screen to capture:", self) + self._video_widget_label = QLabel("Capture output:", self) + self._start_stop_button = QPushButton(self) self._screen_list_model = ScreenListModel(self) # Setup QScreenCapture with initial source: - self.set_screen(QGuiApplication.primaryScreen()) + self.setScreen(QGuiApplication.primaryScreen()) self._screen_capture.start() self._media_capture_session.setScreenCapture(self._screen_capture) self._media_capture_session.setVideoOutput(self._video_widget) self._screen_list_view.setModel(self._screen_list_model) + self._window_list_view = QListView(self) + self._window_capture = QWindowCapture(self) + self._media_capture_session.setWindowCapture(self._window_capture) + self._window_label = QLabel("Select window to capture:", self) + + self._window_list_model = WindowListModel(self) + self._window_list_view.setModel(self._window_list_model) + update_action = QAction("Update windows List", self) + update_action.triggered.connect(self._window_list_model.populate) + self._window_list_view.addAction(update_action) + self._window_list_view.setContextMenuPolicy(Qt.ActionsContextMenu) + grid_layout = QGridLayout(self) grid_layout.addWidget(self._screen_label, 0, 0) grid_layout.addWidget(self._screen_list_view, 1, 0) - grid_layout.addWidget(self._start_stop_button, 2, 0) + grid_layout.addWidget(self._start_stop_button, 4, 0) grid_layout.addWidget(self._video_widget_label, 0, 1) - grid_layout.addWidget(self._video_widget, 1, 1, 2, 1) + grid_layout.addWidget(self._video_widget, 1, 1, 4, 1) + grid_layout.addWidget(self._window_label, 2, 0) + grid_layout.addWidget(self._window_list_view, 3, 0) grid_layout.setColumnStretch(1, 1) grid_layout.setRowStretch(1, 1) grid_layout.setColumnMinimumWidth(0, 400) grid_layout.setColumnMinimumWidth(1, 400) + grid_layout.setRowMinimumHeight(3, 1) - self._screen_list_view.activated.connect(self.on_screen_selection_changed) - self._start_stop_button.clicked.connect(self.on_start_stop_button_clicked) - self._screen_capture.errorOccurred.connect(self.on_screen_capture_error_occured) + selection_model = self._screen_list_view.selectionModel() + selection_model.selectionChanged.connect(self.on_current_screen_selection_changed) + selection_model = self._window_list_view.selectionModel() + selection_model.selectionChanged.connect(self.on_current_window_selection_changed) - def set_screen(self, screen): - self._screen_capture.setScreen(screen) - self.setWindowTitle(f"Capturing {screen.name()}") + self._start_stop_button.clicked.connect(self.on_start_stop_button_clicked) + self._screen_capture.errorOccurred.connect(self.on_screen_capture_error_occured, + Qt.QueuedConnection) + self._window_capture.errorOccurred.connect(self.on_window_capture_error_occured, + Qt.QueuedConnection) + self.update_active(SourceType.Screen, True) + + @Slot(QItemSelection) + def on_current_screen_selection_changed(self, selection): + indexes = selection.indexes() + if indexes: + self._screen_capture.setScreen(self._screen_list_model.screen(indexes[0])) + self.update_active(SourceType.Screen, self.is_active()) + self._window_list_view.clearSelection() + else: + self._screen_capture.setScreen(None) + + @Slot(QItemSelection) + def on_current_window_selection_changed(self, selection): + indexes = selection.indexes() + if indexes: + window = self._window_list_model.window(indexes[0]) + if not window.isValid(): + m = "The window is no longer valid. Update the list of windows?" + answer = QMessageBox.question(self, "Invalid window", m) + if answer == QMessageBox.Yes: + self.update_active(SourceType.Window, False) + self._window_list_view.clearSelection() + self._window_list_model.populate() + return + self._window_capture.setWindow(window) + self.update_active(SourceType.Window, self.is_active()) + self._screen_list_view.clearSelection() + else: + self._window_capture.setWindow(QCapturableWindow()) - @Slot() - def on_screen_selection_changed(self, index): - self.set_screen(self._screen_list_model.screen(index)) + @Slot(QWindowCapture.Error, str) + def on_window_capture_error_occured(self, error, error_string): + QMessageBox.warning(self, "QWindowCapture: Error occurred", + error_string) - @Slot() - def on_screen_capture_error_occured(self, error, errorString): + @Slot(QScreenCapture.Error, str) + def on_screen_capture_error_occured(self, error, error_string): QMessageBox.warning(self, "QScreenCapture: Error occurred", - errorString) + error_string) @Slot() def on_start_stop_button_clicked(self): - if self._screen_capture.isActive(): - self._screen_capture.stop() - self._start_stop_button.setText("Start screencapture") - else: - self._screen_capture.start() - self._start_stop_button.setText("Stop screencapture") + self.update_active(self._source_type, not self.is_active()) + + def update_start_stop_button_text(self): + active = self.is_active() + if self._source_type == SourceType.Window: + m = "Stop window capture" if active else "Start window capture" + self._start_stop_button.setText(m) + elif self._source_type == SourceType.Screen: + m = "Stop screen capture" if active else "Start screen capture" + self._start_stop_button.setText(m) + + def update_active(self, source_type, active): + self._source_type = source_type + self._screen_capture.setActive(active and source_type == SourceType.Screen) + self._window_capture.setActive(active and source_type == SourceType.Window) + + self.update_start_stop_button_text() + + def is_active(self): + if self._source_type == SourceType.Window: + return self._window_capture.isActive() + if self._source_type == SourceType.Screen: + return self._screen_capture.isActive() + return False diff --git a/examples/multimedia/screencapture/windowlistmodel.py b/examples/multimedia/screencapture/windowlistmodel.py new file mode 100644 index 000000000..079040ec2 --- /dev/null +++ b/examples/multimedia/screencapture/windowlistmodel.py @@ -0,0 +1,30 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtCore import QAbstractListModel, Qt, Slot +from PySide6.QtMultimedia import QWindowCapture + + +class WindowListModel(QAbstractListModel): + + def __init__(self, parent=None): + super().__init__(parent) + self._window_list = QWindowCapture.capturableWindows() + + def rowCount(self, QModelIndex): + return len(self._window_list) + + def data(self, index, role): + if role == Qt.DisplayRole: + window = self._window_list[index.row()] + return window.description() + return None + + def window(self, index): + return self._window_list[index.row()] + + @Slot() + def populate(self): + self.beginResetModel() + self._window_list = QWindowCapture.capturableWindows() + self.endResetModel() |