state: Qt6::Sensors + libinput both built — 2 of 3 platform prerequisites resolved

Platform prerequisite status:
- Qt6::Sensors: BUILT (v6.11.0, 520KB pkgar, dummy backend)
- libinput: BUILT (v1.30.2, with libevdev v1.13.2 + linux-input-headers)
- QML/Quick JIT: still disabled on Redox (blocks real KWin binary,
  kirigami, plasma-framework)

KWin: now attempts real cmake build with Sensors + libinput deps
enabled. Falls back to redbear-compositor shim on cmake failure
(QML/Quick gate). Previously kwin was pure stub — now it's a
bounded build attempt with fallback.

Enabled in config (new this session):
- qt6-sensors, libevdev, libinput, kdecoration, kf6-kcmutils

Previously OOTB dependencies now resolved:
- libevdev → libinput → KWin real build path opened
- linux-input-headers → libevdev → libinput chain
- qt6-sensors → KWin Sensors dependency satisfied
This commit is contained in:
2026-04-30 08:47:15 +01:00
parent c54dff033d
commit 8fa62d20c9
457 changed files with 106858 additions and 382 deletions
@@ -0,0 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(TARGET Qt::Widgets)
add_subdirectory(sensor_explorer_widgets)
endif()
if (TARGET Qt::Quick)
add_subdirectory(sensorclerk)
add_subdirectory(sensor_explorer_qml)
endif()
@@ -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
)
@@ -0,0 +1,20 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#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();
}
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>sensor_explorer.qml</file>
</qresource>
</RCC>
@@ -0,0 +1,119 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
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
}
}
}
}
}
}
@@ -0,0 +1,185 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#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
@@ -0,0 +1,61 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#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
@@ -0,0 +1,18 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## sensor_explorer Binary:
#####################################################################
qt_internal_add_manual_test(tst_manual_sensor_explorer_widgets
GUI
SOURCES
explorer.cpp explorer.h explorer.ui
main.cpp
LIBRARIES
Qt::Sensors
Qt::Widgets
ENABLE_AUTOGEN_TOOLS
uic
)
@@ -0,0 +1,406 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "explorer.h"
#include <QTimer>
#include <QDebug>
#include <qsensor.h>
#include <QMetaObject>
#include <QMetaProperty>
Explorer::Explorer(QWidget *parent)
: QMainWindow(parent)
, m_sensor(0)
, ignoreItemChanged(false)
{
ui.setupUi(this);
// Clear out example data from the .ui file
ui.sensors->clear();
clearSensorProperties();
clearReading();
// Force types to be registered
(void)QSensor::sensorTypes();
// Listen for changes to the registered types
QSensor *sensor = new QSensor(QByteArray(), this);
connect(sensor, SIGNAL(availableSensorsChanged()), this, SLOT(loadSensors()));
}
Explorer::~Explorer()
{
}
void Explorer::loadSensors()
{
qDebug() << "Explorer::loadSensors";
// Clear out anything that's in there now
ui.sensors->clear();
for (const QByteArray &type : QSensor::sensorTypes()) {
qDebug() << "Found type" << type;
for (const QByteArray &identifier : QSensor::sensorsForType(type)) {
qDebug() << "Found identifier" << identifier;
// Don't put in sensors we can't connect to
QSensor sensor(type);
sensor.setIdentifier(identifier);
if (!sensor.connectToBackend()) {
qDebug() << "Couldn't connect to" << identifier;
continue;
}
qDebug() << "Adding identifier" << identifier;
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << QString::fromLatin1(identifier));
item->setData(0, Qt::UserRole, QString::fromLatin1(type));
ui.sensors->addTopLevelItem(item);
}
}
if (ui.sensors->topLevelItemCount() == 0) {
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << tr("No Sensors Found"));
item->setData(0, Qt::UserRole, QString());
ui.sensors->addTopLevelItem(item);
}
ui.sensors->setCurrentItem(0);
ui.scrollArea->hide();
resizeSensors();
}
void Explorer::resizeSensors()
{
ui.sensors->resizeColumnToContents(0);
int length = ui.sensors->header()->length() + 4;
ui.sensors->setFixedWidth(length);
}
void Explorer::on_sensors_currentItemChanged()
{
qDebug() << "Explorer::sensorSelected";
// Clear out anything that's in there now
if (m_sensor) {
delete m_sensor;
m_sensor = 0;
}
clearSensorProperties();
clearReading();
ui.scrollArea->hide();
// Check that we've selected an item
QTreeWidgetItem *item = ui.sensors->currentItem();
if (!item) {
qWarning() << "Didn't select an item!";
return;
}
QByteArray type = item->data(0, Qt::UserRole).toString().toLatin1();
QByteArray identifier = item->data(0, Qt::DisplayRole).toString().toLatin1();
if (type.isEmpty()) {
// Uh oh, there aren't any sensors.
// The user has clicked the dummy list entry so just ignore it.
return;
}
// Connect to the sensor so we can probe it
m_sensor = new QSensor(type, this);
connect(m_sensor, SIGNAL(readingChanged()), this, SLOT(sensor_changed()));
m_sensor->setIdentifier(identifier);
if (!m_sensor->connectToBackend()) {
delete m_sensor;
m_sensor = 0;
qWarning() << "Can't connect to the sensor!";
return;
}
ui.scrollArea->show();
loadSensorProperties();
loadReading();
adjustTableColumns(ui.sensorprops);
adjustTableColumns(ui.reading);
QTimer::singleShot(100, this, SLOT(adjustSizes()));
}
void Explorer::clearReading()
{
ui.reading->setRowCount(0);
}
void Explorer::loadReading()
{
// Probe the reading using Qt's meta-object facilities
QSensorReading *reading = m_sensor->reading();
if (!reading)
return;
const QMetaObject *mo = reading->metaObject();
int firstProperty = QSensorReading::staticMetaObject.propertyOffset();
ui.reading->setRowCount(mo->propertyCount() - firstProperty);
for (int i = firstProperty; i < mo->propertyCount(); ++i) {
int row = i - firstProperty;
QTableWidgetItem *index;
if (row == 0)
// timestamp is not available via index
index = new QTableWidgetItem(QLatin1String("N/A"));
else
index = new QTableWidgetItem(QVariant(row - 1).toString());
QTableWidgetItem *prop = new QTableWidgetItem(mo->property(i).name());
QString typeName = QLatin1String(mo->property(i).typeName());
int delimiter = typeName.lastIndexOf("::");
if (delimiter != -1)
typeName = typeName.mid(delimiter + 2);
QTableWidgetItem *type = new QTableWidgetItem(typeName);
QTableWidgetItem *value = new QTableWidgetItem();
index->setFlags(value->flags() ^ Qt::ItemIsEditable);
prop->setFlags(value->flags() ^ Qt::ItemIsEditable);
type->setFlags(value->flags() ^ Qt::ItemIsEditable);
value->setFlags(value->flags() ^ Qt::ItemIsEditable);
ui.reading->setItem(row, 0, index);
ui.reading->setItem(row, 1, prop);
ui.reading->setItem(row, 2, type);
ui.reading->setItem(row, 3, value);
}
}
void Explorer::clearSensorProperties()
{
ui.sensorprops->setRowCount(0);
}
void Explorer::loadSensorProperties()
{
ignoreItemChanged = true;
// Probe the sensor using Qt's meta-object facilities
const QMetaObject *mo = m_sensor->metaObject();
int firstProperty = QSensor::staticMetaObject.propertyOffset();
int rows = mo->propertyCount() - firstProperty;
ui.sensorprops->setRowCount(rows);
int offset = 0;
for (int i = firstProperty; i < mo->propertyCount(); ++i) {
int row = i - firstProperty - offset;
QLatin1String name(mo->property(i).name());
if (name == "identifier" ||
//name == "type" ||
name == "reading" ||
name == "connected" ||
name == "running" ||
name == "supportsPolling") {
++offset;
continue;
}
QTableWidgetItem *prop = new QTableWidgetItem(name);
QString typeName = QLatin1String(mo->property(i).typeName());
int delimiter = typeName.lastIndexOf("::");
if (delimiter != -1)
typeName = typeName.mid(delimiter + 2);
QTableWidgetItem *type = new QTableWidgetItem(typeName);
QVariant v = mo->property(i).read(m_sensor);
QString val;
if (typeName == "qrangelist") {
qrangelist rl = v.value<qrangelist>();
QStringList out;
for (const qrange &r : rl) {
if (r.first == r.second)
out << QString("%1 Hz").arg(r.first);
else
out << QString("%1-%2 Hz").arg(r.first).arg(r.second);
}
val = out.join(", ");
} else if (typeName == "qoutputrangelist") {
qoutputrangelist rl = v.value<qoutputrangelist>();
QStringList out;
for (const qoutputrange &r : rl)
out << QString("(%1, %2) += %3").arg(r.minimum).arg(r.maximum).arg(r.accuracy);
val = out.join(", ");
} else {
val = v.toString();
}
QTableWidgetItem *value = new QTableWidgetItem(val);
prop->setFlags(value->flags() ^ Qt::ItemIsEditable);
type->setFlags(value->flags() ^ Qt::ItemIsEditable);
if (!mo->property(i).isWritable()) {
// clear the editable flag
value->setFlags(value->flags() ^ Qt::ItemIsEditable);
}
ui.sensorprops->setItem(row, 0, prop);
ui.sensorprops->setItem(row, 1, type);
ui.sensorprops->setItem(row, 2, value);
}
// We don't add all properties
ui.sensorprops->setRowCount(rows - offset);
ignoreItemChanged = false;
}
void Explorer::showEvent(QShowEvent *event)
{
// Once we're visible, load the sensors
// (don't delay showing the UI while we load plugins, connect to backends, etc.)
QTimer::singleShot(0, this, SLOT(loadSensors()));
QMainWindow::showEvent(event);
}
// Resize columns to fit the space.
// This shouldn't be so hard!
void Explorer::adjustTableColumns(QTableWidget *table)
{
if (table->rowCount() == 0) {
table->setFixedHeight(0);
return;
}
// At least this is easy to do
table->resizeColumnsToContents();
int length = table->verticalHeader()->length();
length += (length / static_cast<qreal>(table->verticalHeader()->count())); // Add 1 more (the header itself)
table->setFixedHeight(length);
int columns = table->columnCount();
QList<int> width;
int suggestedWidth = 0;
for (int i = 0; i < columns; ++i) {
int cwidth = table->columnWidth(i);
width << cwidth;
suggestedWidth += cwidth;
}
int actualWidth = table->size().width();
//qDebug() << "suggestedWidth" << suggestedWidth << "actualWidth" << actualWidth;
// We only scale the columns up, we don't scale down
if (actualWidth <= suggestedWidth)
return;
qreal multiplier = actualWidth / static_cast<qreal>(suggestedWidth);
int currentSpace = 4;
for (int i = 0; i < columns; ++i) {
width[i] = multiplier * width[i];
currentSpace += width[i];
}
// It ends up too big due to cell decorations or something.
// Make things smaller one pixel at a time in round robin fashion until we're good.
int i = 0;
while (currentSpace > actualWidth) {
--width[i];
--currentSpace;
i = (i + 1) % columns;
}
for (int i = 0; i < columns; ++i) {
table->setColumnWidth(i, width[i]);
}
table->setMinimumWidth(suggestedWidth);
}
void Explorer::adjustSizes()
{
adjustTableColumns(ui.reading);
adjustTableColumns(ui.sensorprops);
}
void Explorer::resizeEvent(QResizeEvent *event)
{
resizeSensors();
adjustSizes();
QMainWindow::resizeEvent(event);
}
void Explorer::on_start_clicked()
{
m_sensor->start();
QTimer::singleShot(0, this, SLOT(loadSensorProperties()));
}
void Explorer::on_stop_clicked()
{
m_sensor->stop();
QTimer::singleShot(0, this, SLOT(loadSensorProperties()));
}
void Explorer::sensor_changed()
{
QSensorReading *reading = m_sensor->reading();
filter(reading);
}
bool Explorer::filter(QSensorReading *reading)
{
const QMetaObject *mo = reading->metaObject();
int firstProperty = QSensorReading::staticMetaObject.propertyOffset();
for (int i = firstProperty; i < mo->propertyCount(); ++i) {
int row = i - firstProperty;
QString typeName = QLatin1String(mo->property(i).typeName());
int delimiter = typeName.lastIndexOf("::");
if (delimiter != -1)
typeName = typeName.mid(delimiter + 2);
QLatin1String name(mo->property(i).name());
QTableWidgetItem *value = ui.reading->item(row, 3);
QVariant val = mo->property(i).read(reading);
if (typeName == "LightLevel") {
QString text;
switch (val.toInt()) {
case 1:
text = "Dark";
break;
case 2:
text = "Twilight";
break;
case 3:
text = "Light";
break;
case 4:
text = "Bright";
break;
case 5:
text = "Sunny";
break;
default:
text = "Undefined";
break;
}
value->setText(text);
} else {
value->setText(val.toString());
}
}
adjustTableColumns(ui.reading);
//QTimer::singleShot(0, this, SLOT(adjustSizes()));
return false;
}
void Explorer::on_sensorprops_itemChanged(QTableWidgetItem *item)
{
if (ignoreItemChanged)
return;
if (!(item->flags() & Qt::ItemIsEditable))
return;
int row = item->row();
QString name = ui.sensorprops->item(row, 0)->text();
QVariant value = item->text();
qDebug() << "setProperty" << name << value;
m_sensor->setProperty(name.toLatin1().constData(), QVariant(value));
QTimer::singleShot(0, this, SLOT(loadSensorProperties()));
}
@@ -0,0 +1,47 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef EXPLORER_H
#define EXPLORER_H
#include <QMainWindow>
#include <ui_explorer.h>
#include <qsensor.h>
class Explorer : public QMainWindow, public QSensorFilter
{
Q_OBJECT
public:
Explorer(QWidget *parent = 0);
~Explorer();
bool filter(QSensorReading *reading) override;
private slots:
void loadSensors();
void on_sensors_currentItemChanged();
void on_sensorprops_itemChanged(QTableWidgetItem *item);
void on_start_clicked();
void on_stop_clicked();
void sensor_changed();
void adjustSizes();
void loadSensorProperties();
private:
void showEvent(QShowEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void clearReading();
void loadReading();
void clearSensorProperties();
void adjustTableColumns(QTableWidget *table);
void resizeSensors();
Ui::Explorer ui;
QSensor *m_sensor;
bool ignoreItemChanged;
};
#endif
@@ -0,0 +1,406 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Explorer</class>
<widget class="QMainWindow" name="Explorer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>760</width>
<height>636</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>9</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Sensors that were detected on the device are listed in the list on the left, grouped by type. The reading properties for the sensor will be presented on the right.</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Sensor</string>
</property>
<property name="buddy">
<cstring>sensors</cstring>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="sensors">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">Sensor</string>
</property>
</column>
<item>
<property name="text">
<string notr="true">dummy.accelerometer</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>477</width>
<height>556</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>9</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget_4" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>9</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Sensor Properties</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="sensorprops">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="columnCount">
<number>3</number>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<row>
<property name="text">
<string notr="true">1</string>
</property>
</row>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string notr="true">supportedIntervals</string>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string notr="true">qrangelist</string>
</property>
</item>
<item row="0" column="2">
<property name="text">
<string/>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>9</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="start">
<property name="text">
<string>start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stop">
<property name="text">
<string>stop</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_5" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>9</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Reading Properties</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="reading">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="columnCount">
<number>4</number>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<row>
<property name="text">
<string notr="true">1</string>
</property>
</row>
<row>
<property name="text">
<string notr="true">2</string>
</property>
</row>
<column>
<property name="text">
<string>Index</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string notr="true">0</string>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string notr="true">x</string>
</property>
</item>
<item row="0" column="2">
<property name="text">
<string notr="true">qreal</string>
</property>
</item>
<item row="0" column="3">
<property name="text">
<string>9.8</string>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string notr="true">0</string>
</property>
</item>
<item row="1" column="1">
<property name="text">
<string notr="true">changeOfBeingEaten</string>
</property>
</item>
<item row="1" column="2">
<property name="text">
<string notr="true">bool</string>
</property>
</item>
<item row="1" column="3">
<property name="text">
<string>true</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>18</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "explorer.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Explorer explorer;
explorer.show();
return app.exec();
}
@@ -0,0 +1,28 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## sensorclerk Binary:
#####################################################################
qt_internal_add_manual_test(tst_manual_sensor_clerk
GUI
SOURCES
collector.cpp collector.h
main.cpp
LIBRARIES
Qt::Gui
Qt::Quick
Qt::Sensors
)
set(qml_files
"qml/main.qml"
)
qt_internal_add_resource(tst_manual_sensor_clerk "qml_files"
PREFIX
"/"
FILES
${qml_files}
)
@@ -0,0 +1,153 @@
// Copyright (C) 2017 Lorn Potter.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/qqml.h>
#include <QtCore/QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QtSensors>
#include <QDir>
#include <QtSensors/QAccelerometer>
#include <QtSensors/QIRProximitySensor>
#include <QtSensors/QOrientationSensor>
#include <QtSensors/QProximitySensor>
#include <QDebug>
#include "collector.h"
Collector::Collector(QObject *parent)
: QObject(parent),
accel(0),
orientation(0),
proximity(0),
irProx(0),
tapSensor(0),
dataFile(QDir::tempPath()+"/sensordump_0.dat")
, isActive(0),
fileCounter(0)
{
accel = new QAccelerometer(this);
accel->connectToBackend();
accel->setDataRate(100);
connect(accel,SIGNAL(readingChanged()),this,SLOT(accelChanged()));
orientation = new QOrientationSensor(this);
orientation->connectToBackend();
orientation->setDataRate(100);
connect(orientation,SIGNAL(readingChanged()),this,SLOT(orientationChanged()));
proximity = new QProximitySensor(this);
proximity->connectToBackend();
connect(proximity,SIGNAL(readingChanged()),this,SLOT(proximityChanged()));
irProx = new QIRProximitySensor(this);
irProx->connectToBackend();
irProx->setDataRate(50);
connect(irProx,SIGNAL(readingChanged()),this,SLOT(irProximityChanged()));
tapSensor = new QTapSensor(this);
tapSensor->connectToBackend();
connect(tapSensor,SIGNAL(readingChanged()),this,SLOT(tapChanged()));
}
Collector::~Collector()
{
}
void Collector::accelChanged()
{
const qreal x = accel->reading()->x();
const qreal y = accel->reading()->y();
const qreal z = accel->reading()->z();
const quint64 ts = accel->reading()->timestamp();
QTextStream out(&dataFile);
out << QString("accelerometer: %1,%2,%3,%4").arg(ts).arg(x).arg(y).arg(z) << "\n";
}
void Collector::orientationChanged()
{
const QOrientationReading *orientationReading = orientation->reading();
QOrientationReading::Orientation o = orientationReading->orientation();
const quint64 ts = orientationReading->timestamp();
QTextStream out(&dataFile);
out << QString("orientation: %1,%2").arg(ts).arg(o) << "\n";
}
void Collector::proximityChanged()
{
const QProximityReading *proximityReading = proximity->reading();
const quint64 ts = proximityReading->timestamp();
const bool prox = proximityReading->close();
QTextStream out(&dataFile);
out << QString("proximity: %1,%2").arg(ts).arg(prox) << "\n";
}
void Collector::irProximityChanged()
{
const QIRProximityReading *irProximityReading = irProx->reading();
const quint64 ts = irProximityReading->timestamp();
const qreal ref = irProximityReading->reflectance();
QTextStream out(&dataFile);
out << QString("irProximity: %1,%2").arg(ts).arg(ref) << "\n";
}
void Collector::tapChanged()
{
const QTapReading *tapReading = tapSensor->reading();
const quint64 ts = tapReading->timestamp();
const bool dTap = tapReading->isDoubleTap();
QTextStream out(&dataFile);
out << QString("tap: %1,%2").arg(ts).arg(dTap) << "\n";
}
void Collector::startCollecting()
{
if (dataFile.exists()) {
fileCounter++;
for (size_t i = 0; i < fileCounter; i++) {
if (!QFileInfo(QString(QDir::tempPath()+"/sensordump_%1.dat").arg(fileCounter)).exists())
dataFile.setFileName(QString(QDir::tempPath()+"/sensordump_%1.dat").arg(fileCounter));
break;
fileCounter++;
}
}
if (!dataFile.exists()) {
if (dataFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
accel->start();
orientation->start();
proximity->start();
irProx->start();
tapSensor->start();
isActive = true;
} else {
qDebug() << "dump file not opened";
}
} else {
startCollecting();
}
}
void Collector::stopCollecting()
{
if (isActive) {
accel->stop();
orientation->stop();
proximity->stop();
irProx->stop();
tapSensor->stop();
isActive = !isActive;
}
if (dataFile.isOpen())
dataFile.close();
}
QML_DECLARE_TYPE(Collector)
@@ -0,0 +1,52 @@
// Copyright (C) 2017 Lorn Potter.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef COLLECTOR_H
#define COLLECTOR_H
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QFile>
class QAccelerometer;
class QOrientationSensor;
class QProximitySensor;
class QIRProximitySensor;
class QTapSensor;
class Collector : public QObject
{
Q_OBJECT
public:
explicit Collector(QObject *parent = 0);
~Collector();
public slots:
void startCollecting();
void stopCollecting();
private Q_SLOTS:
void accelChanged();
void orientationChanged();
void proximityChanged();
void irProximityChanged();
void tapChanged();
private:
QAccelerometer *accel;
QOrientationSensor *orientation;
QProximitySensor *proximity;
QIRProximitySensor *irProx;
QTapSensor *tapSensor;
QFile dataFile;
bool isActive;
size_t fileCounter;
Q_DISABLE_COPY(Collector)
};
#endif // COLLECTOR_H
@@ -0,0 +1,22 @@
// Copyright (C) 2017 Lorn Potter.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QGuiApplication>
#include <QtQuick/QQuickView>
#include <QtQml/qqml.h>
#include <QDebug>
#include "collector.h"
int main( int argc, char** argv )
{
QGuiApplication app( argc, argv );
qmlRegisterType<Collector>("Collector", 1, 0, "Collector");
QQuickView view;
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:qml/main.qml"));
view.show();
return app.exec();
}
@@ -0,0 +1,39 @@
// Copyright (C) 2017 Lorn Potter.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Controls
import Collector
import QtSensors
Rectangle {
Collector {
id: writer
}
Text {
id: label
text: "Sensor Clerk<br> push to start and stop<br> sensor dump";
anchors.horizontalCenter: parent.horizontalCenter
}
Button {
id: startCollectingButton
text: depressed ? "Stop" : "Start"
property bool depressed: false
anchors.top: label.bottom
enabled: true;
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
if (!depressed) {
writer.startCollecting()
depressed = true
} else {
writer.stopCollecting()
depressed = false
}
}
}
}