diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/manual/CMakeLists.txt | 3 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_qml/CMakeLists.txt | 32 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_qml/main.cpp | 20 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_qml/qml.qrc | 5 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_qml/sensor_explorer.qml | 119 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_qml/sensormodels.cpp | 185 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_qml/sensormodels.h | 61 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_widgets/CMakeLists.txt (renamed from tests/manual/sensor_explorer/CMakeLists.txt) | 2 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_widgets/explorer.cpp (renamed from tests/manual/sensor_explorer/explorer.cpp) | 0 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_widgets/explorer.h (renamed from tests/manual/sensor_explorer/explorer.h) | 0 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_widgets/explorer.ui (renamed from tests/manual/sensor_explorer/explorer.ui) | 0 | ||||
-rw-r--r-- | tests/manual/sensor_explorer_widgets/main.cpp (renamed from tests/manual/sensor_explorer/main.cpp) | 0 |
12 files changed, 425 insertions, 2 deletions
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index a23fbcd8..8686f854 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -2,8 +2,9 @@ # SPDX-License-Identifier: BSD-3-Clause if(TARGET Qt::Widgets) - add_subdirectory(sensor_explorer) + add_subdirectory(sensor_explorer_widgets) endif() if (TARGET Qt::Quick) add_subdirectory(sensorclerk) + add_subdirectory(sensor_explorer_qml) endif() diff --git a/tests/manual/sensor_explorer_qml/CMakeLists.txt b/tests/manual/sensor_explorer_qml/CMakeLists.txt new file mode 100644 index 00000000..e68fce62 --- /dev/null +++ b/tests/manual/sensor_explorer_qml/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(tst_manual_sensor_explorer_qml LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) + +find_package(Qt6 REQUIRED COMPONENTS Qml Quick Sensors) + +qt_add_executable(tst_manual_sensor_explorer_qml + main.cpp + sensormodels.cpp sensormodels.h +) + +set_target_properties(tst_manual_sensor_explorer_qml PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(tst_manual_sensor_explorer_qml PUBLIC + Qt::Qml + Qt::Quick + Qt::Sensors +) + +qt_add_qml_module(tst_manual_sensor_explorer_qml + URI SensorModels + VERSION 1.0 + QML_FILES sensor_explorer.qml + NO_RESOURCE_TARGET_PATH +) diff --git a/tests/manual/sensor_explorer_qml/main.cpp b/tests/manual/sensor_explorer_qml/main.cpp new file mode 100644 index 00000000..4eeaa147 --- /dev/null +++ b/tests/manual/sensor_explorer_qml/main.cpp @@ -0,0 +1,20 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtQml> +#include <QtQuick/QQuickWindow> +#include <QtGui/QGuiApplication> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine(QUrl("qrc:///sensor_explorer.qml")); + QObject *topLevel = engine.rootObjects().value(0); + QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel); + if (!window) { + qWarning("Error: Your root item has to be a Window."); + return -1; + } + window->show(); + return app.exec(); +} diff --git a/tests/manual/sensor_explorer_qml/qml.qrc b/tests/manual/sensor_explorer_qml/qml.qrc new file mode 100644 index 00000000..df6160ae --- /dev/null +++ b/tests/manual/sensor_explorer_qml/qml.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>sensor_explorer.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/sensor_explorer_qml/sensor_explorer.qml b/tests/manual/sensor_explorer_qml/sensor_explorer.qml new file mode 100644 index 00000000..f1c4d3cb --- /dev/null +++ b/tests/manual/sensor_explorer_qml/sensor_explorer.qml @@ -0,0 +1,119 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtQuick.Controls +import QtQuick.Layouts + +import SensorModels + +Window { + id: window + width: 400 + height: 600 + + AvailableSensorsModel { + id: availableSensorsModel + } + + ColumnLayout { + + GroupBox { + id: availableSensorsModelGroup + title: qsTr("Available Sensors") + Layout.preferredWidth: window.width - 4 // 4 = 2x2 margins + Layout.preferredHeight: window.height * 0.4 + Layout.margins: 2 + + ListView { + id: sensorsView + anchors.fill: parent + currentIndex: -1 // no initial selection + spacing: 1 + clip: true + model: availableSensorsModel + delegate: Item { + id: sensorRow + width: sensorsView.width + height: 30 + property color rowColor: { + if (sensorsView.currentIndex == index) + return "lightsteelblue" // highlight + return (index % 2 == 0) ? "#CCCCCC" : "#AAAAAA" + } + RowLayout { + spacing: 1 + anchors.fill: parent + Rectangle { + color: sensorRow.rowColor + Layout.preferredWidth: sensorRow.width * 0.8 + Layout.preferredHeight: sensorRow.height + Text { + anchors.centerIn: parent + text: display.type + "::" + display.identifier + } + } + Rectangle { + color: sensorRow.rowColor + Layout.preferredWidth: sensorRow.width * 0.2 + Layout.preferredHeight: sensorRow.height + Text { + anchors.centerIn: parent + text: display.active ? qsTr("Active") : qsTr("Inactive") + } + } + } + MouseArea { + anchors.fill: parent + onClicked: sensorsView.currentIndex = index + } + } + } + } + + SensorPropertyModel { + id: propertyModel + sensor: availableSensorsModel.get(sensorsView.currentIndex) + } + + Button { + id: activateButton + Layout.preferredHeight: 30 + Layout.alignment: Qt.AlignCenter + enabled: propertyModel.sensor + text: !propertyModel.sensor ? qsTr("Select sensor") + : (propertyModel.sensor.active ? qsTr("Deactivate sensor") + : qsTr("Activate sensor")) + onClicked: propertyModel.sensor.active = !propertyModel.sensor.active + } + + GroupBox { + title: qsTr("Selected sensor's properties") + Layout.preferredWidth: window.width - 4 // 4 = 2x2 margins + Layout.preferredHeight: window.height * 0.55 - activateButton.height + Layout.margins: 2 + enabled: sensorsView.currentIndex != -1 + + TableView { + id: propertyView + anchors.fill: parent + model: propertyModel + columnSpacing: 1 + rowSpacing: 1 + boundsMovement: Flickable.StopAtBounds + clip: true + + delegate: Rectangle { + implicitHeight: 30 + implicitWidth: propertyView.width * 0.5 + color: (model.row % 2 == 0) ? "#CCCCCC" : "#AAAAAA" + Text { + anchors.centerIn: parent + text: display + } + } + } + } + } +} diff --git a/tests/manual/sensor_explorer_qml/sensormodels.cpp b/tests/manual/sensor_explorer_qml/sensormodels.cpp new file mode 100644 index 00000000..ed365e51 --- /dev/null +++ b/tests/manual/sensor_explorer_qml/sensormodels.cpp @@ -0,0 +1,185 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "sensormodels.h" +#include "qsensor.h" +#include "qorientationsensor.h" +#include <QtCore/QDebug> +#include <qsensor.h> +#include <QMetaObject> +#include <QMetaProperty> + +QT_BEGIN_NAMESPACE + +QByteArray rangelistToByteArray(const qrangelist& list) +{ + QStringList ranges; + for (const qrange &r : list) { + if (r.first == r.second) + ranges << QString("%1 Hz").arg(r.first); + else + ranges << QString("%1-%2 Hz").arg(r.first).arg(r.second); + } + if (ranges.size() > 0) + return ranges.join(", ").toLatin1(); + return "-"; +} + +QByteArray outputrangelistToByteArray(const qoutputrangelist& list) +{ + QStringList ranges; + for (const qoutputrange &r : list) { + ranges << QString("(%1, %2) += %3").arg(r.minimum).arg(r.maximum).arg(r.accuracy); + } + if (ranges.size() > 0) + return ranges.join(", ").toLatin1(); + return "-"; +} + +AvailableSensorsModel::AvailableSensorsModel(QObject* parent) : QAbstractListModel(parent) +{ + // Some valuetypes do not convert nicely to presentable strings, add converters for them + QMetaType::registerConverter<qrangelist, QByteArray>(rangelistToByteArray); + QMetaType::registerConverter<qoutputrangelist, QByteArray>(outputrangelistToByteArray); + + // Populate the available sensors list + loadSensors(); +} + +/* + Load all available sensors and store them in a list. +*/ +void AvailableSensorsModel::loadSensors() +{ + beginResetModel(); + m_availableSensors.clear(); + + for (const QByteArray &type : QSensor::sensorTypes()) { + for (const QByteArray &identifier : QSensor::sensorsForType(type)) { + QSensor* sensor = new QSensor(type, this); + sensor->setIdentifier(identifier); + // Don't put in sensors we can't connect to + if (!sensor->connectToBackend()) + continue; + m_availableSensors.append(sensor); + } + } + endResetModel(); +} + +int AvailableSensorsModel::rowCount(const QModelIndex&) const +{ + return m_availableSensors.size(); +} + +QVariant AvailableSensorsModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + return QVariant::fromValue<QSensor*>(m_availableSensors.at(index.row())); +} + +QSensor* AvailableSensorsModel::get(int index) const +{ + if (index < 0 || index >= m_availableSensors.size()) + return nullptr; + return m_availableSensors[index]; +} + +// -- SensorPropertyModel + +static QSet<QByteArray> ignoredProperties = {"reading", "identifier", "active", + "connectedToBackend", "busy"}; + +SensorPropertyModel::SensorPropertyModel(QObject* parent) : QAbstractTableModel(parent) +{ +} + +int SensorPropertyModel::rowCount(const QModelIndex&) const +{ + if (!m_sensor) + return 0; + return m_values.size(); +} + +int SensorPropertyModel::columnCount(const QModelIndex&) const +{ + return 2; // 2 = property name + value columns +} + +QVariant SensorPropertyModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + return (index.column() == 0) ? std::get<0>(m_values.at(index.row())) + : std::get<1>(m_values.at(index.row())); +} + +void SensorPropertyModel::setSensor(QSensor *sensor) +{ + if (m_sensor == sensor) + return; + if (m_sensor) + m_sensor->disconnect(this); + m_sensor = sensor; + + beginResetModel(); + m_values.clear(); + if (m_sensor) { + // Use metobject to read the available properties. This allows the model to support all + // available sensors without knowing their properties in advance / compile-time. + + // 1. Read properties of the 'reading' object if available + int firstProperty = QSensorReading::staticMetaObject.propertyOffset(); + QSensorReading *reading = m_sensor->reading(); + if (reading) { + const QMetaObject *mo = reading->metaObject(); + for (int i = firstProperty; i < mo->propertyCount(); ++i) { + QByteArray name = mo->property(i).name(); + m_values.append(std::tuple<QByteArray, QByteArray> + (name, reading->property(name).toByteArray())); + } + } + + // 2. Read properties of the 'sensor' object + const QMetaObject *mo1 = m_sensor->metaObject(); + firstProperty = QSensorReading::staticMetaObject.propertyOffset(); + for (int i = firstProperty; i < mo1->propertyCount(); ++i) { + QByteArray name = mo1->property(i).name(); + if (ignoredProperties.contains(name)) + continue; + m_values.append(std::tuple<QByteArray, QByteArray> + (name, m_sensor->property(name).toByteArray())); + } + QObject::connect(m_sensor, &QSensor::readingChanged, + this, &SensorPropertyModel::onReadingChanged); + } + endResetModel(); + emit sensorChanged(); +} + +QSensor* SensorPropertyModel::sensor() const +{ + return m_sensor; +} + +void SensorPropertyModel::onReadingChanged() +{ + QSensorReading *reading = m_sensor->reading(); + const QMetaObject *mo = reading->metaObject(); + int firstProperty = QSensorReading::staticMetaObject.propertyOffset(); + + int valueMapIndex = 0; + for (int i = firstProperty; i < mo->propertyCount(); ++i) { + QByteArray name = mo->property(i).name(); + // Update the value and signal the change. Note: here we rely that the "reading" + // properties are first on the m_values, and in same order as after the initial + // population. This should be true as we access the static metabobject (dynamic + // property changes shouldn't impact) + m_values[valueMapIndex++] = std::tuple<QByteArray, QByteArray> + (name, reading->property(name).toByteArray()); + } + emit dataChanged(createIndex(0,1), createIndex(valueMapIndex,1), {Qt::DisplayRole}); +} + +QT_END_NAMESPACE diff --git a/tests/manual/sensor_explorer_qml/sensormodels.h b/tests/manual/sensor_explorer_qml/sensormodels.h new file mode 100644 index 00000000..80f4bcbb --- /dev/null +++ b/tests/manual/sensor_explorer_qml/sensormodels.h @@ -0,0 +1,61 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef QSEONSOREXPLORER_H +#define QSEONSOREXPLORER_H + +#include <QtSensors/qsensor.h> + +#include <QtQml/qqml.h> +#include <QtCore/QAbstractListModel> +#include <QtCore/QAbstractTableModel> + +QT_BEGIN_NAMESPACE + +class AvailableSensorsModel: public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT +public: + explicit AvailableSensorsModel(QObject* parent = nullptr); + int rowCount(const QModelIndex & = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + Q_INVOKABLE QSensor* get(int index) const; + +private: + void loadSensors(); + QList<QSensor*> m_availableSensors; +}; + +class SensorPropertyModel: public QAbstractTableModel +{ + Q_OBJECT + Q_PROPERTY(QSensor* sensor READ sensor WRITE setSensor NOTIFY sensorChanged) + QML_ELEMENT + +public: + explicit SensorPropertyModel(QObject* parent = nullptr); + + int rowCount(const QModelIndex & = QModelIndex()) const override; + int columnCount(const QModelIndex & = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + + void setSensor(QSensor* sensor); + QSensor* sensor() const; + +signals: + void sensorChanged(); + +private slots: + void onReadingChanged(); + +private: + QSensor* m_sensor = nullptr; + // m_values is used to cache sensor property values to avoid + // full metaobject iteration on every sensor reading change + QList<std::tuple<QByteArray, QByteArray>> m_values; +}; + +QT_END_NAMESPACE + +#endif // QSEONSOREXPLORER_H diff --git a/tests/manual/sensor_explorer/CMakeLists.txt b/tests/manual/sensor_explorer_widgets/CMakeLists.txt index 2f32dc91..357eb02e 100644 --- a/tests/manual/sensor_explorer/CMakeLists.txt +++ b/tests/manual/sensor_explorer_widgets/CMakeLists.txt @@ -5,7 +5,7 @@ ## sensor_explorer Binary: ##################################################################### -qt_internal_add_manual_test(tst_manual_sensor_explorer +qt_internal_add_manual_test(tst_manual_sensor_explorer_widgets GUI SOURCES explorer.cpp explorer.h explorer.ui diff --git a/tests/manual/sensor_explorer/explorer.cpp b/tests/manual/sensor_explorer_widgets/explorer.cpp index e6353c01..e6353c01 100644 --- a/tests/manual/sensor_explorer/explorer.cpp +++ b/tests/manual/sensor_explorer_widgets/explorer.cpp diff --git a/tests/manual/sensor_explorer/explorer.h b/tests/manual/sensor_explorer_widgets/explorer.h index 92aa6f47..92aa6f47 100644 --- a/tests/manual/sensor_explorer/explorer.h +++ b/tests/manual/sensor_explorer_widgets/explorer.h diff --git a/tests/manual/sensor_explorer/explorer.ui b/tests/manual/sensor_explorer_widgets/explorer.ui index 166e9c36..166e9c36 100644 --- a/tests/manual/sensor_explorer/explorer.ui +++ b/tests/manual/sensor_explorer_widgets/explorer.ui diff --git a/tests/manual/sensor_explorer/main.cpp b/tests/manual/sensor_explorer_widgets/main.cpp index 0e2ea93d..0e2ea93d 100644 --- a/tests/manual/sensor_explorer/main.cpp +++ b/tests/manual/sensor_explorer_widgets/main.cpp |