屏幕捕获示例¶
屏幕捕获演示了如何使用QScreenCapture
和QWindowCapture
来捕获屏幕或窗口。该示例显示了一个屏幕和窗口的列表,并使用QMediaCaptureSession
和QVideoWidget
显示所选项目的实时预览。可以通过QPushButton
开始和停止捕获。
应用程序结构¶
该示例由三个自定义类组成。用户界面和所有屏幕捕获功能都在类ScreenCapturePreview
中实现。类ScreenListModel
和WindowListModel
仅作为两个QListView
小部件背后的模型。主函数创建了一个ScreenCapturePreview
对象,该对象又创建了QScreenCapture
和QWindowCapture
的实例,以及一个QMediaCaptureSession
和QVideoWidget
,此外还包括所有用户界面小部件。
屏幕和窗口模型分别填充了QGuiApplication.screens()
和QWindowCapture.capturableWindows()
的返回值。
当选择一个列表项时,它通过QScreenCapture.setScreen()
连接到QScreenCapture
对象,或者通过QWindowCapture.setWindow()
连接到QWindowCapture
对象。捕获对象分别通过QMediaCaptureSession.setScreenCapture()
和QMediaCaptureSession.setWindowCapture()
连接到QMediaCaptureSession
对象。捕获会话又通过QMediaCaptureSession.setVideoOutput()
连接到QVideoWidget
对象。因此,捕获输出在用户界面右侧的视频小部件中预览。
开始/停止按钮调用 QScreenCapture.start()
和 QScreenCapture.stop()
,
或者 QWindowCapture.start()
和 QWindowCapture.stop()
。
如果发出errorOccurred
信号,则会弹出一个QMessageBox。
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
"""PySide6 port of the QtMultiMedia Screen Capture Example from Qt v6.x"""
import sys
from PySide6.QtCore import QCoreApplication
from PySide6.QtWidgets import QApplication
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())
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
from enum import Enum, auto
from PySide6.QtMultimediaWidgets import QVideoWidget
from PySide6.QtMultimedia import (QCapturableWindow, QMediaCaptureSession,
QScreenCapture, QWindowCapture)
from PySide6.QtWidgets import (QGridLayout, QLabel, QListView,
QMessageBox, QPushButton, QWidget)
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):
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("Select screen to capture:", self)
self._video_widget_label = QLabel("Capture output:", self)
self._start_stop_button = QPushButton(self)
self._status_label = QLabel(self)
self._screen_list_model = ScreenListModel(self)
# Setup QScreenCapture with initial source:
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, 4, 0)
grid_layout.addWidget(self._video_widget_label, 0, 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.addWidget(self._status_label, 5, 0, 1, 2)
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)
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)
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):
self.clear_error_string()
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):
self.clear_error_string()
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(QWindowCapture.Error, str)
def on_window_capture_error_occured(self, error, error_string):
self.set_error_string("QWindowCapture: Error occurred " + error_string)
@Slot(QScreenCapture.Error, str)
def on_screen_capture_error_occured(self, error, error_string):
self.set_error_string("QScreenCapture: Error occurred " + error_string)
def set_error_string(self, t):
self._status_label.setStyleSheet("background-color: rgb(255, 0, 0);")
self._status_label.setText(t)
def clear_error_string(self):
self._status_label.clear()
self._status_label.setStyleSheet("")
@Slot()
def on_start_stop_button_clicked(self):
self.clear_error_string()
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
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
from PySide6.QtGui import QGuiApplication
from PySide6.QtCore import QAbstractListModel, Qt, Slot
class ScreenListModel(QAbstractListModel):
def __init__(self, parent=None):
super().__init__(parent)
app = qApp # noqa: F821
app.screenAdded.connect(self.screens_changed)
app.screenRemoved.connect(self.screens_changed)
app.primaryScreenChanged.connect(self.screens_changed)
def rowCount(self, index):
return len(QGuiApplication.screens())
def data(self, index, role):
screen_list = QGuiApplication.screens()
if role == Qt.ItemDataRole.DisplayRole:
screen = screen_list[index.row()]
w = screen.size().width()
h = screen.size().height()
dpi = screen.logicalDotsPerInch()
return f'"{screen.name()}" {w}x{h}, {dpi}DPI'
return None
def screen(self, index):
return QGuiApplication.screens()[index.row()]
@Slot()
def screens_changed(self):
self.beginResetModel()
self.endResetModel()
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from __future__ import annotations
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.ItemDataRole.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()