QML Tuner Example
This Example shows how to use the Tuner API from QML.
This Example shows how to use the Tuner API from QML.
First an AmFmTuner object is created. By default, the auto discovery is used to search for a plugin that implements QIfAmFmTunerBackendInterface. Depending on the selection of the band radio buttons the tuner band is selected.
AmFmTuner { id: tuner band: bandGroup.checkedButton.text === "AM" ? AmFmTuner.AMBand : AmFmTuner.FMBand onScanStarted: { console.log("A Station SCAN has been started") } onScanStopped: { console.log("A Station SCAN has been stopped") } }
Station Information
In the left third of the UI we want to display information about the current radio station as well as providing some buttons to change the stations or start a scan through all stations.
ColumnLayout { RowLayout { Label { text: "Station:" } Label { text: tuner.station.stationName } } RowLayout { Label { text: "Frequency:" } Label { text: tuner.frequency } } RowLayout { Label { text: "Band:" } RadioButton { text: "AM"; ButtonGroup.group: bandGroup; checked: tuner.band === AmFmTuner.AMBand } RadioButton { text: "FM"; ButtonGroup.group: bandGroup; checked: tuner.band === AmFmTuner.FMBand } } GroupBox { title: "Band settings" ColumnLayout { RowLayout { Label { text: "Step Size:" } Label { text: tuner.stepSize } } RowLayout { Label { text: "Minimum Frequency:" } Label { text: tuner.minimumFrequency } } RowLayout { Label { text: "Maximum Frequency::" } Label { text: tuner.maximumFrequency } } } } ButtonGroup { id: bandGroup } RowLayout { Button { text: "<-" onClicked: tuner.seekDown() } Button { text: "<" onClicked: tuner.stepDown() } Button { text: "Scan" checkable: true onClicked: { if (checked) tuner.startScan(); else tuner.stopScan(); } } Button { text: ">" onClicked: tuner.stepUp() } Button { text: "->" onClicked: tuner.seekUp() } } Item { Layout.fillHeight: true } }
The station property of AmFmTuner exposes the station you are currently listening to, which can be empty as well, if the frequency property was manually changed to a frequency no station is broadcasting on.
Station List
The middle part of the UI shows a list of all the available radio stations. Every item of the list shows the name and the frequency of a station. By clicking on one of the list items, the current station will be changed to this station. On the right side of every station is a +
button which can be used to save this station into the preset list.
ListView { spacing: 8 clip: true width: 300 Layout.fillHeight: true model: FilterAndBrowseModel { serviceObject: tuner.serviceObject contentType: "station" } delegate: Rectangle { width: ListView.view.width height: column.height color: "#efefef" MouseArea { anchors.fill: parent onClicked: { tuner.tune(model.item) } } Row { anchors.fill: parent Column { id: column width: parent.width * 9 / 10 Text { text: "Name: " + model.item.stationName } Text { text: "Type: " + model.item.frequency } } Button { id: addButton text: "+" width: parent.width / 10 height: parent.height onClicked: { presetsModel.insert(0, model.item) } function checkExists() { presetsModel.indexOf(model.item).then(function (index) { addButton.enabled = (index === -1) }) } Component.onCompleted: { checkExists() } Connections { target: presetsModel function onCountChanged() { addButton.checkExists() } } } } } }
To fill the ListView with all available stations, the FilterAndBrowseModel model is used. As the FilterAndBrowseModel is a generic model, it needs to know where the data should come from. This is done by passing the service object of the AmFmTuner to the model. The model will then use the QIfFilterAndBrowseModelInterface exposed by the same backend which is used by AmFmTuner. Because the tuner backend could expose multiple different lists, the contentType needs to be selected: in this case the contentType is set to station
, which provides all available stations.
model: FilterAndBrowseModel { serviceObject: tuner.serviceObject contentType: "station" }
To change the currently playing station the AmFmTuner::tune method is used by calling it in an onClicked handler
MouseArea { anchors.fill: parent onClicked: { tuner.tune(model.item) } }
Preset List
The preset list occupies the right third of the UI and shows all favorite stations. This list is sorted and maintained by the user. A press on the +
button of the station list will add a station to this list, the X
button will remove the item and the arrow buttons can be used to change the order of the stations.
ListView { spacing: 8 clip: true Layout.fillWidth: true model: FilterAndBrowseModel { id: presetsModel serviceObject: tuner.serviceObject contentType: "presets" } delegate: Rectangle { width: ListView.view.width height: column.height color: "#efefef" MouseArea { anchors.fill: parent onClicked: { tuner.tune(model.item) } } Row { anchors.fill: parent Column { id: column width: parent.width * 7 / 10 Text { text: "Name: " + model.item.stationName } Text { text: "Type: " + model.item.frequency } } Button { text: "\u2227" width: parent.width / 10 height: parent.height enabled: index > 0 onClicked: { presetsModel.move(index, index - 1) } } Button { text: "\u2228" width: parent.width / 10 height: parent.height enabled: index < presetsModel.count -1 onClicked: { presetsModel.move(index, index + 1) } } Button { text: "X" width: parent.width / 10 height: parent.height onClicked: { presetsModel.remove(index) } } } } }
Similar to the station list, the FilterAndBrowseModel is used as a model, but the contentType was changed to presets
. For maintaining the list, the move and remove functions of FilterAndBrowseModel are used.
Favorite Button
The +
button of the station list should be enabled if the station is not already part of the preset list. This is done by using the FilterAndBrowseModel::indexOf function which will search for the passed item and call the callback function passed as second argument with the result. Depending on whether the index is valid, the button will be enabled or disabled. This asynchronous approach is needed, as the preset list might be pretty big and the data might come from a different process which maintains the tuner state.
Button { id: addButton text: "+" width: parent.width / 10 height: parent.height onClicked: { presetsModel.insert(0, model.item) } function checkExists() { presetsModel.indexOf(model.item).then(function (index) { addButton.enabled = (index === -1) }) } Component.onCompleted: { checkExists() } Connections { target: presetsModel function onCountChanged() { addButton.checkExists() } } }
If not already part of the preset list, the station is added to the list by using the FilterAndBrowseModel::insert method, which is passed 0
as the first parameter to add it on top of the list.
© 2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.