aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2021-05-16 10:03:31 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-05-18 11:00:31 +0000
commitbb1887bcc6692f7dfece87fe3df42226d4b83008 (patch)
tree8c57b08db1a323c5c2d67535ac5ee380833b4923
parent558c8d9d1eccdc55bf130da79fd8431bd3c59554 (diff)
Fix the multimedia examples to work
- Port to qtmultimedia/9a4822037def3b9d48abea8bbfd7ea20fd19849b (wip/qt6). - Add Slot decorators - Rename according to snake case conventions - Connect error signals of player and camera - Use theme icons in player Task-number: PYSIDE-1112 Task-number: PYSIDE-1482 Change-Id: Ib79614e56b2b1ad7ea6cd0406efa1d91ce1abdb1 Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io> (cherry picked from commit ec71d250cd11b1bdf7eae9ddb55b79bd08360039) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--examples/charts/audio/audio.py117
-rw-r--r--examples/multimedia/audiooutput/audiooutput.py159
-rw-r--r--examples/multimedia/camera/camera.py218
-rw-r--r--examples/multimedia/player/player.py249
4 files changed, 425 insertions, 318 deletions
diff --git a/examples/charts/audio/audio.py b/examples/charts/audio/audio.py
index ccb6b2e9a..2d313dd8c 100644
--- a/examples/charts/audio/audio.py
+++ b/examples/charts/audio/audio.py
@@ -43,86 +43,87 @@
import sys
from PySide6.QtCharts import QChart, QChartView, QLineSeries, QValueAxis
-from PySide6.QtCore import QPointF
+from PySide6.QtCore import QPointF, Slot
from PySide6.QtMultimedia import (QAudioDeviceInfo, QAudioFormat,
- QAudioInput)
+ QAudioInput, QMediaDevices)
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox
-sampleCount = 2000
-resolution = 4
+
+SAMPLE_COUNT = 2000
+
+
+RESOLUTION = 4
class MainWindow(QMainWindow):
def __init__(self, device):
super().__init__()
- self.series = QLineSeries()
- self.chart = QChart()
- self.chart.addSeries(self.series)
- self.axisX = QValueAxis()
- self.axisX.setRange(0, sampleCount)
- self.axisX.setLabelFormat("%g")
- self.axisX.setTitleText("Samples")
- self.axisY = QValueAxis()
- self.axisY.setRange(-1, 1)
- self.axisY.setTitleText("Audio level")
- self.chart.setAxisX(self.axisX, self.series)
- self.chart.setAxisY(self.axisY, self.series)
- self.chart.legend().hide()
- self.chart.setTitle(f"Data from the microphone ({device.deviceName()})")
-
- formatAudio = QAudioFormat()
- formatAudio.setSampleRate(8000)
- formatAudio.setChannelCount(1)
- formatAudio.setSampleSize(8)
- formatAudio.setCodec("audio/pcm")
- formatAudio.setByteOrder(QAudioFormat.LittleEndian)
- formatAudio.setSampleType(QAudioFormat.UnSignedInt)
-
- self.audioInput = QAudioInput(device, formatAudio, self)
- self.ioDevice = self.audioInput.start()
- self.ioDevice.readyRead.connect(self._readyRead)
-
- self.chartView = QChartView(self.chart)
- self.setCentralWidget(self.chartView)
-
- self.buffer = [QPointF(x, 0) for x in range(sampleCount)]
- self.series.append(self.buffer)
+ self._series = QLineSeries()
+ self._chart = QChart()
+ self._chart.addSeries(self._series)
+ self._axis_x = QValueAxis()
+ self._axis_x.setRange(0, SAMPLE_COUNT)
+ self._axis_x.setLabelFormat("%g")
+ self._axis_x.setTitleText("Samples")
+ self._axis_y = QValueAxis()
+ self._axis_y.setRange(-1, 1)
+ self._axis_y.setTitleText("Audio level")
+ self._chart.setAxisX(self._axis_x, self._series)
+ self._chart.setAxisY(self._axis_y, self._series)
+ self._chart.legend().hide()
+ name = device.description()
+ self._chart.setTitle(f"Data from the microphone ({name})")
+
+ format_audio = QAudioFormat()
+ format_audio.setSampleRate(8000)
+ format_audio.setChannelCount(1)
+ format_audio.setSampleFormat(QAudioFormat.UInt8)
+
+ self._audio_input = QAudioInput(device, format_audio, self)
+ self._io_device = self._audio_input.start()
+ self._io_device.readyRead.connect(self._readyRead)
+
+ self._chart_view = QChartView(self._chart)
+ self.setCentralWidget(self._chart_view)
+
+ self._buffer = [QPointF(x, 0) for x in range(SAMPLE_COUNT)]
+ self._series.append(self._buffer)
def closeEvent(self, event):
- if self.audioInput is not None:
- self.audioInput.stop()
+ if self._audio_input is not None:
+ self._audio_input.stop()
event.accept()
+ @Slot()
def _readyRead(self):
- data = self.ioDevice.readAll()
- availableSamples = data.size() // resolution
+ data = self._io_device.readAll()
+ available_samples = data.size() // RESOLUTION
start = 0
- if (availableSamples < sampleCount):
- start = sampleCount - availableSamples
+ if (available_samples < SAMPLE_COUNT):
+ start = SAMPLE_COUNT - available_samples
for s in range(start):
- self.buffer[s].setY(self.buffer[s + availableSamples].y())
+ self._buffer[s].setY(self._buffer[s + available_samples].y())
- dataIndex = 0
- for s in range(start, sampleCount):
- value = (ord(data[dataIndex]) - 128) / 128
- self.buffer[s].setY(value)
- dataIndex = dataIndex + resolution
- self.series.replace(self.buffer)
+ data_index = 0
+ for s in range(start, SAMPLE_COUNT):
+ value = (ord(data[data_index]) - 128) / 128
+ self._buffer[s].setY(value)
+ data_index = data_index + RESOLUTION
+ self._series.replace(self._buffer)
if __name__ == '__main__':
app = QApplication(sys.argv)
- inputDevice = QAudioDeviceInfo.defaultInputDevice()
- if (inputDevice.isNull()):
+ input_devices = QMediaDevices.audioInputs()
+ if not input_devices:
QMessageBox.warning(None, "audio", "There is no audio input device available.")
sys.exit(-1)
-
- mainWin = MainWindow(inputDevice)
- mainWin.setWindowTitle("audio")
- availableGeometry = app.desktop().availableGeometry(mainWin)
- size = availableGeometry.height() * 3 / 4
- mainWin.resize(size, size)
- mainWin.show()
+ main_win = MainWindow(input_devices[0])
+ main_win.setWindowTitle("audio")
+ available_geometry = main_win.screen().availableGeometry()
+ size = available_geometry.height() * 3 / 4
+ main_win.resize(size, size)
+ main_win.show()
sys.exit(app.exec())
diff --git a/examples/multimedia/audiooutput/audiooutput.py b/examples/multimedia/audiooutput/audiooutput.py
index 0e9a2c10a..6035150c2 100644
--- a/examples/multimedia/audiooutput/audiooutput.py
+++ b/examples/multimedia/audiooutput/audiooutput.py
@@ -42,14 +42,17 @@
"""PySide6 port of the multimedia/audiooutput example from Qt v5.x, originating from PyQt"""
+import sys
from math import pi, sin
from struct import pack
-from PySide6.QtCore import QByteArray, QIODevice, Qt, QTimer, qWarning
+from PySide6.QtCore import (QByteArray, QIODevice, Qt, QSysInfo, QTimer,
+ qWarning, Slot)
from PySide6.QtMultimedia import (QAudio, QAudioDeviceInfo, QAudioFormat,
- QAudioOutput)
+ QAudioOutput, QMediaDevices)
from PySide6.QtWidgets import (QApplication, QComboBox, QHBoxLayout, QLabel,
- QMainWindow, QPushButton, QSlider, QVBoxLayout, QWidget)
+ QMainWindow, QPushButton, QSlider,
+ QVBoxLayout, QWidget)
class Generator(QIODevice):
@@ -60,7 +63,7 @@ class Generator(QIODevice):
self.m_pos = 0
self.m_buffer = QByteArray()
- self.generateData(format, durationUs, sampleRate)
+ self.generate_data(format, durationUs, sampleRate)
def start(self):
self.open(QIODevice.ReadOnly)
@@ -69,44 +72,46 @@ class Generator(QIODevice):
self.m_pos = 0
self.close()
- def generateData(self, fmt, durationUs, sampleRate):
+ def generate_data(self, fmt, durationUs, sampleRate):
pack_format = ''
- if fmt.sampleSize() == 8:
- if fmt.sampleType() == QAudioFormat.UnSignedInt:
+ sample_size = fmt.bytesPerSample() * 8
+ if sample_size == 8:
+ if fmt.sampleFormat() == QAudioFormat.UInt8:
scaler = lambda x: ((1.0 + x) / 2 * 255)
pack_format = 'B'
- elif fmt.sampleType() == QAudioFormat.SignedInt:
+ elif fmt.sampleFormat() == QAudioFormat.Int16:
scaler = lambda x: x * 127
pack_format = 'b'
- elif fmt.sampleSize() == 16:
- if fmt.sampleType() == QAudioFormat.UnSignedInt:
+ elif sample_size == 16:
+ little_endian = QSysInfo.ByteOrder == QSysInfo.LittleEndian
+ if fmt.sampleFormat() == QAudioFormat.UInt8:
scaler = lambda x: (1.0 + x) / 2 * 65535
- pack_format = '<H' if fmt.byteOrder() == QAudioFormat.LittleEndian else '>H'
- elif fmt.sampleType() == QAudioFormat.SignedInt:
+ pack_format = '<H' if little_endian else '>H'
+ elif fmt.sampleFormat() == QAudioFormat.Int16:
scaler = lambda x: x * 32767
- pack_format = '<h' if fmt.byteOrder() == QAudioFormat.LittleEndian else '>h'
+ pack_format = '<h' if little_endian else '>h'
assert(pack_format != '')
- channelBytes = fmt.sampleSize() // 8
- sampleBytes = fmt.channelCount() * channelBytes
+ channel_bytes = fmt.bytesPerSample()
+ sample_bytes = fmt.channelCount() * channel_bytes
- length = (fmt.sampleRate() * fmt.channelCount() * (fmt.sampleSize() // 8)) * durationUs // 100000
+ length = (fmt.sampleRate() * fmt.channelCount() * channel_bytes) * durationUs // 100000
self.m_buffer.clear()
- sampleIndex = 0
+ sample_index = 0
factor = 2 * pi * sampleRate / fmt.sampleRate()
while length != 0:
- x = sin((sampleIndex % fmt.sampleRate()) * factor)
+ x = sin((sample_index % fmt.sampleRate()) * factor)
packed = pack(pack_format, int(scaler(x)))
for _ in range(fmt.channelCount()):
self.m_buffer.append(packed)
- length -= channelBytes
+ length -= channel_bytes
- sampleIndex += 1
+ sample_index += 1
def readData(self, maxlen):
data = QByteArray()
@@ -134,101 +139,100 @@ class AudioTest(QMainWindow):
SUSPEND_LABEL = "Suspend playback"
RESUME_LABEL = "Resume playback"
- DurationSeconds = 1
- ToneSampleRateHz = 600
- DataSampleRateHz = 44100
+ DURATION_SECONDS = 1
+ TONE_SAMPLE_RATE_HZ = 600
+ DATA_SAMPLE_RATE_HZ = 44100
- def __init__(self):
+ def __init__(self, devices):
super().__init__()
- self.m_device = QAudioDeviceInfo.defaultOutputDevice()
+ self.m_devices = devices
+ self.m_device = self.m_devices[0]
self.m_output = None
- self.initializeWindow()
- self.initializeAudio()
+ self.initialize_window()
+ self.initialize_audio()
- def initializeWindow(self):
- layout = QVBoxLayout()
+ def initialize_window(self):
+
+ central_widget = QWidget()
+ layout = QVBoxLayout(central_widget)
self.m_deviceBox = QComboBox()
- self.m_deviceBox.activated[int].connect(self.deviceChanged)
- for deviceInfo in QAudioDeviceInfo.availableDevices(QAudio.AudioOutput):
- self.m_deviceBox.addItem(deviceInfo.deviceName(), deviceInfo)
+ self.m_deviceBox.activated[int].connect(self.device_changed)
+ for deviceInfo in self.m_devices:
+ self.m_deviceBox.addItem(deviceInfo.description(), deviceInfo)
layout.addWidget(self.m_deviceBox)
self.m_modeButton = QPushButton()
- self.m_modeButton.clicked.connect(self.toggleMode)
+ self.m_modeButton.clicked.connect(self.toggle_mode)
self.m_modeButton.setText(self.PUSH_MODE_LABEL)
layout.addWidget(self.m_modeButton)
self.m_suspendResumeButton = QPushButton(
- clicked=self.toggleSuspendResume)
+ clicked=self.toggle_suspend_resume)
self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
layout.addWidget(self.m_suspendResumeButton)
- volumeBox = QHBoxLayout()
- volumeLabel = QLabel("Volume:")
+ volume_box = QHBoxLayout()
+ volume_label = QLabel("Volume:")
self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100,
singleStep=10)
- self.m_volumeSlider.valueChanged.connect(self.volumeChanged)
-
- volumeBox.addWidget(volumeLabel)
- volumeBox.addWidget(self.m_volumeSlider)
+ self.m_volumeSlider.valueChanged.connect(self.volume_changed)
- layout.addLayout(volumeBox)
+ volume_box.addWidget(volume_label)
+ volume_box.addWidget(self.m_volumeSlider)
- window = QWidget()
- window.setLayout(layout)
+ layout.addLayout(volume_box)
- self.setCentralWidget(window)
+ self.setCentralWidget(central_widget)
- def initializeAudio(self):
+ def initialize_audio(self):
self.m_pullTimer = QTimer(self)
- self.m_pullTimer.timeout.connect(self.pullTimerExpired)
+ self.m_pullTimer.timeout.connect(self.pull_timer_expired)
self.m_pullMode = True
self.m_format = QAudioFormat()
- self.m_format.setSampleRate(self.DataSampleRateHz)
+ self.m_format.setSampleRate(self.DATA_SAMPLE_RATE_HZ)
self.m_format.setChannelCount(1)
- self.m_format.setSampleSize(16)
- self.m_format.setCodec('audio/pcm')
- self.m_format.setByteOrder(QAudioFormat.LittleEndian)
- self.m_format.setSampleType(QAudioFormat.SignedInt)
+ self.m_format.setSampleFormat(QAudioFormat.Int16)
- info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
+ info = self.m_devices[0]
if not info.isFormatSupported(self.m_format):
qWarning("Default format not supported - trying to use nearest")
self.m_format = info.nearestFormat(self.m_format)
self.m_generator = Generator(self.m_format,
- self.DurationSeconds * 1000000, self.ToneSampleRateHz, self)
+ self.DURATION_SECONDS * 1000000, self.TONE_SAMPLE_RATE_HZ, self)
- self.createAudioOutput()
+ self.create_audio_output()
- def createAudioOutput(self):
+ def create_audio_output(self):
self.m_audioOutput = QAudioOutput(self.m_device, self.m_format)
- self.m_audioOutput.notify.connect(self.notified)
- self.m_audioOutput.stateChanged.connect(self.handleStateChanged)
+ self.m_audioOutput.stateChanged.connect(self.handle_state_changed)
self.m_generator.start()
self.m_audioOutput.start(self.m_generator)
self.m_volumeSlider.setValue(self.m_audioOutput.volume() * 100)
- def deviceChanged(self, index):
+ @Slot(int)
+ def device_changed(self, index):
self.m_pullTimer.stop()
self.m_generator.stop()
self.m_audioOutput.stop()
self.m_device = self.m_deviceBox.itemData(index)
- self.createAudioOutput()
+ self.create_audio_output()
- def volumeChanged(self, value):
+ @Slot(int)
+ def volume_changed(self, value):
if self.m_audioOutput is not None:
self.m_audioOutput.setVolume(value / 100.0)
+ @Slot()
def notified(self):
bytes_free = self.m_audioOutput.bytesFree()
elapsed = self.m_audioOutput.elapsedUSecs()
@@ -237,17 +241,16 @@ class AudioTest(QMainWindow):
f"elapsedUSecs = {elapsed}, "
f"processedUSecs = {processed}")
- def pullTimerExpired(self):
+ @Slot()
+ def pull_timer_expired(self):
if self.m_audioOutput is not None and self.m_audioOutput.state() != QAudio.StoppedState:
- chunks = self.m_audioOutput.bytesFree() // self.m_audioOutput.periodSize()
- for _ in range(chunks):
- data = self.m_generator.read(self.m_audioOutput.periodSize())
- if data is None or len(data) != self.m_audioOutput.periodSize():
- break
-
+ bytes_free = self.m_audioOutput.bytesFree()
+ data = self.m_generator.read(bytes_free)
+ if data:
self.m_output.write(data)
- def toggleMode(self):
+ @Slot()
+ def toggle_mode(self):
self.m_pullTimer.stop()
self.m_audioOutput.stop()
@@ -263,7 +266,8 @@ class AudioTest(QMainWindow):
self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
- def toggleSuspendResume(self):
+ @Slot()
+ def toggle_suspend_resume(self):
if self.m_audioOutput.state() == QAudio.SuspendedState:
qWarning("status: Suspended, resume()")
self.m_audioOutput.resume()
@@ -279,25 +283,28 @@ class AudioTest(QMainWindow):
elif self.m_audioOutput.state() == QAudio.IdleState:
qWarning("status: IdleState")
- stateMap = {
+ state_map = {
QAudio.ActiveState: "ActiveState",
QAudio.SuspendedState: "SuspendedState",
QAudio.StoppedState: "StoppedState",
QAudio.IdleState: "IdleState"}
- def handleStateChanged(self, state):
- state = self.stateMap.get(state, 'Unknown')
+ @Slot(QAudio.State)
+ def handle_state_changed(self, state):
+ state = self.state_map.get(state, 'Unknown')
qWarning(f"state = {state}")
if __name__ == '__main__':
-
- import sys
-
app = QApplication(sys.argv)
app.setApplicationName("Audio Output Test")
- audio = AudioTest()
+ devices = QMediaDevices.audioOutputs()
+ if not devices:
+ print('No audio outputs found.', file=sys.stderr)
+ sys.exit(-1)
+
+ audio = AudioTest(devices)
audio.show()
sys.exit(app.exec())
diff --git a/examples/multimedia/camera/camera.py b/examples/multimedia/camera/camera.py
index 030840803..d2c6ab187 100644
--- a/examples/multimedia/camera/camera.py
+++ b/examples/multimedia/camera/camera.py
@@ -1,7 +1,7 @@
#############################################################################
##
-## Copyright (C) 2017 The Qt Company Ltd.
+## Copyright (C) 2021 The Qt Company Ltd.
## Contact: http://www.qt.io/licensing/
##
## This file is part of the Qt for Python examples of the Qt Toolkit.
@@ -41,104 +41,131 @@
"""PySide6 Multimedia Camera Example"""
-import os, sys
-from PySide6.QtCore import QDate, QDir, QStandardPaths, Qt, QUrl
+import os
+import sys
+from PySide6.QtCore import QDate, QDir, QStandardPaths, Qt, QUrl, Slot
from PySide6.QtGui import QAction, QGuiApplication, QDesktopServices, QIcon
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel,
QMainWindow, QPushButton, QTabWidget, QToolBar, QVBoxLayout, QWidget)
-from PySide6.QtMultimedia import QCamera, QCameraImageCapture, QCameraInfo
-from PySide6.QtMultimediaWidgets import QCameraViewfinder
+from PySide6.QtMultimedia import (QCamera, QCameraImageCapture,
+ QCameraInfo, QMediaCaptureSession,
+ QMediaDevices)
+from PySide6.QtMultimediaWidgets import QVideoWidget
class ImageView(QWidget):
def __init__(self, previewImage, fileName):
super().__init__()
- self.fileName = fileName
-
- mainLayout = QVBoxLayout(self)
- self.imageLabel = QLabel()
- self.imageLabel.setPixmap(QPixmap.fromImage(previewImage))
- mainLayout.addWidget(self.imageLabel)
-
- topLayout = QHBoxLayout()
- self.fileNameLabel = QLabel(QDir.toNativeSeparators(fileName))
- self.fileNameLabel.setTextInteractionFlags(Qt.TextBrowserInteraction)
-
- topLayout.addWidget(self.fileNameLabel)
- topLayout.addStretch()
- copyButton = QPushButton("Copy")
- copyButton.setToolTip("Copy file name to clipboard")
- topLayout.addWidget(copyButton)
- copyButton.clicked.connect(self.copy)
- launchButton = QPushButton("Launch")
- launchButton.setToolTip("Launch image viewer")
- topLayout.addWidget(launchButton)
- launchButton.clicked.connect(self.launch)
- mainLayout.addLayout(topLayout)
-
+ self._file_name = fileName
+
+ main_layout = QVBoxLayout(self)
+ self._image_label = QLabel()
+ self._image_label.setPixmap(QPixmap.fromImage(previewImage))
+ main_layout.addWidget(self._image_label)
+
+ top_layout = QHBoxLayout()
+ self._file_name_label = QLabel(QDir.toNativeSeparators(fileName))
+ self._file_name_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
+
+ top_layout.addWidget(self._file_name_label)
+ top_layout.addStretch()
+ copy_button = QPushButton("Copy")
+ copy_button.setToolTip("Copy file name to clipboard")
+ top_layout.addWidget(copy_button)
+ copy_button.clicked.connect(self.copy)
+ launch_button = QPushButton("Launch")
+ launch_button.setToolTip("Launch image viewer")
+ top_layout.addWidget(launch_button)
+ launch_button.clicked.connect(self.launch)
+ main_layout.addLayout(top_layout)
+
+ @Slot()
def copy(self):
- QGuiApplication.clipboard().setText(self.fileNameLabel.text())
+ QGuiApplication.clipboard().setText(self._file_name_label.text())
+ @Slot()
def launch(self):
- QDesktopServices.openUrl(QUrl.fromLocalFile(self.fileName))
+ QDesktopServices.openUrl(QUrl.fromLocalFile(self._file_name))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
- self.cameraInfo = QCameraInfo.defaultCamera()
- self.camera = QCamera(self.cameraInfo)
- self.camera.setCaptureMode(QCamera.CaptureStillImage)
- self.imageCapture = QCameraImageCapture(self.camera)
- self.imageCapture.imageCaptured.connect(self.imageCaptured)
- self.imageCapture.imageSaved.connect(self.imageSaved)
- self.currentPreview = QImage()
+ self._capture_session = None
+ self._camera = None
+ self._camera_info = None
+ self._image_capture = None
+
+ available_cameras = QMediaDevices.videoInputs()
+ if available_cameras:
+ self._camera_info = available_cameras[0]
+ self._camera = QCamera(self._camera_info)
+ self._camera.errorOccurred.connect(self._camera_error)
+ self._image_capture = QCameraImageCapture(self._camera)
+ self._image_capture.imageCaptured.connect(self.image_captured)
+ self._image_capture.imageSaved.connect(self.image_saved)
+ self._image_capture.errorOccurred.connect(self._capture_error)
+ self._capture_session = QMediaCaptureSession()
+ self._capture_session.setCamera(self._camera)
+ self._capture_session.setImageCapture(self._image_capture)
+
+ self._current_preview = QImage()
+
+ tool_bar = QToolBar()
+ self.addToolBar(tool_bar)
+
+ file_menu = self.menuBar().addMenu("&File")
+ shutter_icon = QIcon(os.path.join(os.path.dirname(__file__),
+ "shutter.svg"))
+ self._take_picture_action = QAction(shutter_icon, "&Take Picture", self,
+ shortcut="Ctrl+T",
+ triggered=self.take_picture)
+ self._take_picture_action.setToolTip("Take Picture")
+ file_menu.addAction(self._take_picture_action)
+ tool_bar.addAction(self._take_picture_action)
- toolBar = QToolBar()
- self.addToolBar(toolBar)
+ exit_action = QAction(QIcon.fromTheme("application-exit"), "E&xit",
+ self, shortcut="Ctrl+Q", triggered=self.close)
+ file_menu.addAction(exit_action)
- fileMenu = self.menuBar().addMenu("&File")
- shutterIcon = QIcon(os.path.join(os.path.dirname(__file__),
- "shutter.svg"))
- self.takePictureAction = QAction(shutterIcon, "&Take Picture", self,
- shortcut="Ctrl+T",
- triggered=self.takePicture)
- self.takePictureAction.setToolTip("Take Picture")
- fileMenu.addAction(self.takePictureAction)
- toolBar.addAction(self.takePictureAction)
-
- exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit",
- self, shortcut="Ctrl+Q", triggered=self.close)
- fileMenu.addAction(exitAction)
-
- aboutMenu = self.menuBar().addMenu("&About")
- aboutQtAction = QAction("About &Qt", self, triggered=qApp.aboutQt)
- aboutMenu.addAction(aboutQtAction)
-
- self.tabWidget = QTabWidget()
- self.setCentralWidget(self.tabWidget)
-
- self.cameraViewfinder = QCameraViewfinder()
- self.camera.setViewfinder(self.cameraViewfinder)
- self.tabWidget.addTab(self.cameraViewfinder, "Viewfinder")
-
- if self.camera.status() != QCamera.UnavailableStatus:
- name = self.cameraInfo.description()
+ about_menu = self.menuBar().addMenu("&About")
+ about_qt_action = QAction("About &Qt", self, triggered=qApp.aboutQt)
+ about_menu.addAction(about_qt_action)
+
+ self._tab_widget = QTabWidget()
+ self.setCentralWidget(self._tab_widget)
+
+ self._camera_viewfinder = QVideoWidget()
+ self._tab_widget.addTab(self._camera_viewfinder, "Viewfinder")
+
+ if self._camera and self._camera.status() != QCamera.UnavailableStatus:
+ name = self._camera_info.description()
self.setWindowTitle(f"PySide6 Camera Example ({name})")
- self.statusBar().showMessage(f"Starting: '{name}'", 5000)
- self.camera.start()
+ self.show_status_message(f"Starting: '{name}'")
+ self._capture_session.setVideoOutput(self._camera_viewfinder)
+ self._take_picture_action.setEnabled(self._image_capture.isReadyForCapture())
+ self._image_capture.readyForCaptureChanged.connect(self._take_picture_action.setEnabled)
+ self._camera.start()
else:
self.setWindowTitle("PySide6 Camera Example")
- self.takePictureAction.setEnabled(False)
- self.statusBar().showMessage("Camera unavailable", 5000)
+ self._take_picture_action.setEnabled(False)
+ self.show_status_message("Camera unavailable")
+
+ def show_status_message(self, message):
+ self.statusBar().showMessage(message, 5000)
+
+ def closeEvent(self, event):
+ if self._camera and self._camera.status() == QCamera.ActiveStatus:
+ self._camera.stop()
+ event.accept()
- def nextImageFileName(self):
- picturesLocation = QStandardPaths.writableLocation(QStandardPaths.PicturesLocation)
- dateString = QDate.currentDate().toString("yyyyMMdd")
- pattern = f"{picturesLocation}/pyside6_camera_{dateString}_{{:03d}}.jpg"
+ def next_image_file_name(self):
+ pictures_location = QStandardPaths.writableLocation(QStandardPaths.PicturesLocation)
+ date_string = QDate.currentDate().toString("yyyyMMdd")
+ pattern = f"{pictures_location}/pyside6_camera_{date_string}_{{:03d}}.jpg"
n = 1
while True:
result = pattern.format(n)
@@ -147,26 +174,37 @@ class MainWindow(QMainWindow):
n = n + 1
return None
- def takePicture(self):
- self.currentPreview = QImage()
- self.camera.searchAndLock()
- self.imageCapture.capture(self.nextImageFileName())
- self.camera.unlock()
+ @Slot()
+ def take_picture(self):
+ self._current_preview = QImage()
+ self._image_capture.captureToFile(self.next_image_file_name())
+
+ @Slot(int, QImage)
+ def image_captured(self, id, previewImage):
+ self._current_preview = previewImage
+
+ @Slot(int, str)
+ def image_saved(self, id, fileName):
+ index = self._tab_widget.count()
+ image_view = ImageView(self._current_preview, fileName)
+ self._tab_widget.addTab(image_view, f"Capture #{index}")
+ self._tab_widget.setCurrentIndex(index)
- def imageCaptured(self, id, previewImage):
- self.currentPreview = previewImage
+ @Slot(int, QCameraImageCapture.Error, str)
+ def _capture_error(self, id, error, error_string):
+ print(error_string, file=sys.stderr)
+ self.show_status_message(error_string)
- def imageSaved(self, id, fileName):
- index = self.tabWidget.count()
- imageView = ImageView(self.currentPreview, fileName)
- self.tabWidget.addTab(imageView, f"Capture #{index}")
- self.tabWidget.setCurrentIndex(index)
+ @Slot(QCamera.Error, str)
+ def _camera_error(self, error, error_string):
+ print(error_string, file=sys.stderr)
+ self.show_status_message(error_string)
if __name__ == '__main__':
app = QApplication(sys.argv)
- mainWin = MainWindow()
- availableGeometry = app.desktop().availableGeometry(mainWin)
- mainWin.resize(availableGeometry.width() / 3, availableGeometry.height() / 2)
- mainWin.show()
+ main_win = MainWindow()
+ available_geometry = main_win.screen().availableGeometry()
+ main_win.resize(available_geometry.width() / 3, available_geometry.height() / 2)
+ main_win.show()
sys.exit(app.exec())
diff --git a/examples/multimedia/player/player.py b/examples/multimedia/player/player.py
index 44df8853b..359c4ec3d 100644
--- a/examples/multimedia/player/player.py
+++ b/examples/multimedia/player/player.py
@@ -1,7 +1,7 @@
#############################################################################
##
-## Copyright (C) 2017 The Qt Company Ltd.
+## Copyright (C) 2021 The Qt Company Ltd.
## Contact: http://www.qt.io/licensing/
##
## This file is part of the Qt for Python examples of the Qt Toolkit.
@@ -42,118 +42,179 @@
"""PySide6 Multimedia player example"""
import sys
-from PySide6.QtCore import QStandardPaths, Qt
+from PySide6.QtCore import QStandardPaths, Qt, Slot
from PySide6.QtGui import QAction, QIcon, QKeySequence, QScreen
from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,
QMainWindow, QSlider, QStyle, QToolBar)
-from PySide6.QtMultimedia import QMediaPlayer, QMediaPlaylist
+from PySide6.QtMultimedia import QMediaFormat, QMediaPlayer, QMediaPlaylist
from PySide6.QtMultimediaWidgets import QVideoWidget
+AVI = "video/x-msvideo" # AVI
+
+
+MP4 = 'video/mp4'
+
+
+def get_supported_mime_types():
+ result = []
+ for f in QMediaFormat().supportedFileFormats(QMediaFormat.Decode):
+ mime_type = QMediaFormat(f).mimeType()
+ result.append(mime_type.name())
+ return result
+
+
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
- self.playlist = QMediaPlaylist()
- self.player = QMediaPlayer()
-
- toolBar = QToolBar()
- self.addToolBar(toolBar)
-
- fileMenu = self.menuBar().addMenu("&File")
- openAction = QAction(QIcon.fromTheme("document-open"),
- "&Open...", self, shortcut=QKeySequence.Open,
- triggered=self.open)
- fileMenu.addAction(openAction)
- exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit",
- self, shortcut="Ctrl+Q", triggered=self.close)
- fileMenu.addAction(exitAction)
-
- playMenu = self.menuBar().addMenu("&Play")
- playIcon = self.style().standardIcon(QStyle.SP_MediaPlay)
- self.playAction = toolBar.addAction(playIcon, "Play")
- self.playAction.triggered.connect(self.player.play)
- playMenu.addAction(self.playAction)
-
- previousIcon = self.style().standardIcon(QStyle.SP_MediaSkipBackward)
- self.previousAction = toolBar.addAction(previousIcon, "Previous")
- self.previousAction.triggered.connect(self.previousClicked)
- playMenu.addAction(self.previousAction)
-
- pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause)
- self.pauseAction = toolBar.addAction(pauseIcon, "Pause")
- self.pauseAction.triggered.connect(self.player.pause)
- playMenu.addAction(self.pauseAction)
-
- nextIcon = self.style().standardIcon(QStyle.SP_MediaSkipForward)
- self.nextAction = toolBar.addAction(nextIcon, "Next")
- self.nextAction.triggered.connect(self.playlist.next)
- playMenu.addAction(self.nextAction)
-
- stopIcon = self.style().standardIcon(QStyle.SP_MediaStop)
- self.stopAction = toolBar.addAction(stopIcon, "Stop")
- self.stopAction.triggered.connect(self.player.stop)
- playMenu.addAction(self.stopAction)
-
- self.volumeSlider = QSlider()
- self.volumeSlider.setOrientation(Qt.Horizontal)
- self.volumeSlider.setMinimum(0)
- self.volumeSlider.setMaximum(100)
- self.volumeSlider.setFixedWidth(self.screen().availableGeometry().width() / 10)
- self.volumeSlider.setValue(self.player.volume())
- self.volumeSlider.setTickInterval(10)
- self.volumeSlider.setTickPosition(QSlider.TicksBelow)
- self.volumeSlider.setToolTip("Volume")
- self.volumeSlider.valueChanged.connect(self.player.setVolume)
- toolBar.addWidget(self.volumeSlider)
-
- aboutMenu = self.menuBar().addMenu("&About")
- aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)
- aboutMenu.addAction(aboutQtAct)
-
- self.videoWidget = QVideoWidget()
- self.setCentralWidget(self.videoWidget)
- self.player.setPlaylist(self.playlist)
- self.player.stateChanged.connect(self.updateButtons)
- self.player.setVideoOutput(self.videoWidget)
-
- self.updateButtons(self.player.state())
-
+ self._playlist = QMediaPlaylist()
+ self._player = QMediaPlayer()
+ self._player.errorOccurred.connect(self._player_error)
+
+ tool_bar = QToolBar()
+ self.addToolBar(tool_bar)
+
+ file_menu = self.menuBar().addMenu("&File")
+ icon = QIcon.fromTheme("document-open")
+ open_action = QAction(icon, "&Open...", self,
+ shortcut=QKeySequence.Open, triggered=self.open)
+ file_menu.addAction(open_action)
+ icon = QIcon.fromTheme("application-exit")
+ exit_action = QAction(icon, "E&xit", self,
+ shortcut="Ctrl+Q", triggered=self.close)
+ file_menu.addAction(exit_action)
+
+ play_menu = self.menuBar().addMenu("&Play")
+ style = self.style()
+ icon = QIcon.fromTheme("media-playback-start.png",
+ style.standardIcon(QStyle.SP_MediaPlay))
+ self._play_action = tool_bar.addAction(icon, "Play")
+ self._play_action.triggered.connect(self._player.play)
+ play_menu.addAction(self._play_action)
+
+ icon = QIcon.fromTheme("media-skip-backward-symbolic.svg",
+ style.standardIcon(QStyle.SP_MediaSkipBackward))
+ self._previous_action = tool_bar.addAction(icon, "Previous")
+ self._previous_action.triggered.connect(self.previous_clicked)
+ play_menu.addAction(self._previous_action)
+
+ icon = QIcon.fromTheme("media-playback-pause.png",
+ style.standardIcon(QStyle.SP_MediaPause))
+ self._pause_action = tool_bar.addAction(icon, "Pause")
+ self._pause_action.triggered.connect(self._player.pause)
+ play_menu.addAction(self._pause_action)
+
+ icon = QIcon.fromTheme("media-skip-forward-symbolic.svg",
+ style.standardIcon(QStyle.SP_MediaSkipForward))
+ self._next_action = tool_bar.addAction(icon, "Next")
+ self._next_action.triggered.connect(self.next_clicked)
+ play_menu.addAction(self._next_action)
+
+ icon = QIcon.fromTheme("media-playback-stop.png",
+ style.standardIcon(QStyle.SP_MediaStop))
+ self._stop_action = tool_bar.addAction(icon, "Stop")
+ self._stop_action.triggered.connect(self._ensure_stopped)
+ play_menu.addAction(self._stop_action)
+
+ self._volume_slider = QSlider()
+ self._volume_slider.setOrientation(Qt.Horizontal)
+ self._volume_slider.setMinimum(0)
+ self._volume_slider.setMaximum(100)
+ available_width = self.screen().availableGeometry().width()
+ self._volume_slider.setFixedWidth(available_width / 10)
+ self._volume_slider.setValue(self._player.volume())
+ self._volume_slider.setTickInterval(10)
+ self._volume_slider.setTickPosition(QSlider.TicksBelow)
+ self._volume_slider.setToolTip("Volume")
+ self._volume_slider.valueChanged.connect(self._player.setVolume)
+ tool_bar.addWidget(self._volume_slider)
+
+ about_menu = self.menuBar().addMenu("&About")
+ about_qt_act = QAction("About &Qt", self, triggered=qApp.aboutQt)
+ about_menu.addAction(about_qt_act)
+
+ self._video_widget = QVideoWidget()
+ self.setCentralWidget(self._video_widget)
+ self._player.playbackStateChanged.connect(self.update_buttons)
+ self._player.setVideoOutput(self._video_widget)
+
+ self.update_buttons(self._player.playbackState())
+ self._mime_types = []
+
+ def closeEvent(self, event):
+ self._ensure_stopped()
+ event.accept()
+
+ @Slot()
def open(self):
- fileDialog = QFileDialog(self)
- supportedMimeTypes = QMediaPlayer.supportedMimeTypes()
- if not supportedMimeTypes:
- supportedMimeTypes.append("video/x-msvideo") # AVI
- fileDialog.setMimeTypeFilters(supportedMimeTypes)
- moviesLocation = QStandardPaths.writableLocation(QStandardPaths.MoviesLocation)
- fileDialog.setDirectory(moviesLocation)
- if fileDialog.exec() == QDialog.Accepted:
- self.playlist.addMedia(fileDialog.selectedUrls()[0])
- self.player.play()
-
- def previousClicked(self):
+ self._ensure_stopped()
+ file_dialog = QFileDialog(self)
+
+ is_windows = sys.platform == 'win32'
+ if not self._mime_types:
+ self._mime_types = get_supported_mime_types()
+ if (is_windows and AVI not in self._mime_types):
+ self._mime_types.append(AVI)
+
+ file_dialog.setMimeTypeFilters(self._mime_types)
+
+ default_mimetype = AVI if is_windows else MP4
+ if default_mimetype in self._mime_types:
+ file_dialog.selectMimeTypeFilter(default_mimetype)
+
+ movies_location = QStandardPaths.writableLocation(QStandardPaths.MoviesLocation)
+ file_dialog.setDirectory(movies_location)
+ if file_dialog.exec() == QDialog.Accepted:
+ url = file_dialog.selectedUrls()[0]
+ self._playlist.addMedia(url)
+ self._player.setSource(url)
+ self._player.play()
+
+ @Slot()
+ def _ensure_stopped(self):
+ if self._player.playbackState() != QMediaPlayer.StoppedState:
+ self._player.stop()
+
+ @Slot()
+ def previous_clicked(self):
# Go to previous track if we are within the first 5 seconds of playback
# Otherwise, seek to the beginning.
- if self.player.position() <= 5000:
- self.playlist.previous()
+ if self._player.position() <= 5000:
+ self._playlist.previous()
+ self._player.setSource(self._playlist.currentMedia())
else:
- self.player.setPosition(0)
+ self._player.setPosition(0)
+
+ @Slot()
+ def next_clicked(self):
+ self._playlist.next()
+ self._player.setSource(self._playlist.currentMedia())
- def updateButtons(self, state):
- mediaCount = self.playlist.mediaCount()
- self.playAction.setEnabled(mediaCount > 0
+ def update_buttons(self, state):
+ media_count = self._playlist.mediaCount()
+ self._play_action.setEnabled(media_count > 0
and state != QMediaPlayer.PlayingState)
- self.pauseAction.setEnabled(state == QMediaPlayer.PlayingState)
- self.stopAction.setEnabled(state != QMediaPlayer.StoppedState)
- self.previousAction.setEnabled(self.player.position() > 0)
- self.nextAction.setEnabled(mediaCount > 1)
+ self._pause_action.setEnabled(state == QMediaPlayer.PlayingState)
+ self._stop_action.setEnabled(state != QMediaPlayer.StoppedState)
+ self._previous_action.setEnabled(self._player.position() > 0)
+ self._next_action.setEnabled(media_count > 1)
+
+ def show_status_message(self, message):
+ self.statusBar().showMessage(message, 5000)
+
+ @Slot(QMediaPlayer.Error, str)
+ def _player_error(self, error, error_string):
+ print(error_string, file=sys.stderr)
+ self.show_status_message(error_string)
if __name__ == '__main__':
app = QApplication(sys.argv)
- mainWin = MainWindow()
- availableGeometry = mainWin.screen().availableGeometry()
- mainWin.resize(availableGeometry.width() / 3, availableGeometry.height() / 2)
- mainWin.show()
+ main_win = MainWindow()
+ available_geometry = main_win.screen().availableGeometry()
+ main_win.resize(available_geometry.width() / 3,
+ available_geometry.height() / 2)
+ main_win.show()
sys.exit(app.exec())