Skip to content

Commit 7888102

Browse files
author
Ece Cinucen
committed
Example: Port qtcharts audio example to qtgraphs
Task-number: PYSIDE-841 Pick-to: 6.8 6.9 Change-Id: Id8a81c06078c147eb50f1f1d688841e8a6bab18b Reviewed-by: Friedemann Kleint <[email protected]> Reviewed-by: Cristian Maureira-Fredes <[email protected]>
1 parent 9ad9727 commit 7888102

File tree

6 files changed

+143
-0
lines changed

6 files changed

+143
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (C) 2025 The Qt Company Ltd.
2+
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3+
4+
import QtQuick
5+
import QtQuick.Controls
6+
import QtGraphs
7+
8+
ApplicationWindow {
9+
visible: true
10+
width: 1000
11+
height: 800
12+
title: "Data from the microphone (" + device_name + ")"
13+
14+
GraphsView {
15+
id: graph
16+
anchors.fill: parent
17+
18+
LineSeries {
19+
id: audio_series
20+
width: 2
21+
color: "#007acc"
22+
}
23+
24+
axisX: ValueAxis {
25+
min: 0
26+
max: 2000
27+
tickInterval : 500
28+
labelFormat: "%g"
29+
titleText: "Samples"
30+
}
31+
32+
axisY: ValueAxis {
33+
min: -1
34+
max: 1
35+
tickInterval : 0.5
36+
labelFormat: "%0.1f"
37+
titleText: "Audio level"
38+
}
39+
}
40+
41+
Connections {
42+
target: audio_bridge
43+
function onDataUpdated(buffer) {
44+
audio_series.clear()
45+
for (let i = 0; i < buffer.length; ++i) {
46+
audio_series.append(buffer[i])
47+
}
48+
}
49+
}
50+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module GraphsAudio
2+
Main 1.0 Main.qml
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
GraphsAudio Example
2+
===================
3+
4+
This example shows the drawing of dynamic data (microphone input) using QtGraphs and Qml.
5+
6+
.. image:: graphsaudio.webp
7+
:width: 400
8+
:alt: GraphsAudio Screenshot
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"files": ["main.py", "GraphsAudio/Main.qml", "GraphsAudio/qmldir"]
3+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Copyright (C) 2025 The Qt Company Ltd.
2+
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3+
from __future__ import annotations
4+
5+
import sys
6+
from pathlib import Path
7+
from PySide6.QtCore import QObject, QPointF, Slot, Signal
8+
from PySide6.QtMultimedia import QAudioFormat, QAudioSource, QMediaDevices
9+
from PySide6.QtWidgets import QMessageBox
10+
from PySide6.QtQml import QQmlApplicationEngine
11+
from PySide6.QtGui import QGuiApplication
12+
13+
14+
SAMPLE_COUNT = 2000
15+
RESOLUTION = 4
16+
17+
18+
class Audio(QObject):
19+
dataUpdated = Signal(list)
20+
21+
def __init__(self, device):
22+
super().__init__()
23+
24+
format_audio = QAudioFormat()
25+
format_audio.setSampleRate(8000)
26+
format_audio.setChannelCount(1)
27+
format_audio.setSampleFormat(QAudioFormat.UInt8)
28+
29+
self.device_name = device.description()
30+
31+
self._audio_input = QAudioSource(device, format_audio, self)
32+
self._io_device = self._audio_input.start()
33+
self._io_device.readyRead.connect(self._readyRead)
34+
35+
self._buffer = [QPointF(x, 0) for x in range(SAMPLE_COUNT)]
36+
37+
def closeEvent(self, event):
38+
if self._audio_input is not None:
39+
self._audio_input.stop()
40+
event.accept()
41+
42+
@Slot()
43+
def _readyRead(self):
44+
data = self._io_device.readAll()
45+
available_samples = data.size() // RESOLUTION
46+
start = 0
47+
if (available_samples < SAMPLE_COUNT):
48+
start = SAMPLE_COUNT - available_samples
49+
for s in range(start):
50+
self._buffer[s].setY(self._buffer[s + available_samples].y())
51+
52+
data_index = 0
53+
for s in range(start, SAMPLE_COUNT):
54+
value = (ord(data[data_index]) - 128) / 128
55+
self._buffer[s].setY(value)
56+
data_index = data_index + RESOLUTION
57+
58+
self.dataUpdated.emit(self._buffer)
59+
60+
61+
if __name__ == '__main__':
62+
app = QGuiApplication(sys.argv)
63+
engine = QQmlApplicationEngine()
64+
65+
input_devices = QMediaDevices.audioInputs()
66+
if not input_devices:
67+
QMessageBox.warning(None, "audio", "There is no audio input device available.")
68+
sys.exit(-1)
69+
70+
audio_bridge = Audio(input_devices[0])
71+
engine.rootContext().setContextProperty("audio_bridge", audio_bridge)
72+
73+
device = input_devices[0]
74+
device_name = device.description()
75+
engine.rootContext().setContextProperty("device_name", device_name)
76+
77+
engine.addImportPath(Path(__file__).parent)
78+
engine.loadFromModule("GraphsAudio", "Main")
79+
80+
sys.exit(app.exec())

0 commit comments

Comments
 (0)