Различные статьи по Qt и c++

Сенсоры и геолокация в Qt для мобильных платформ: Qt Sensors и Qt Positioning

Мобильные приложения часто нуждаются в данных о положении устройства в пространстве (акселерометр, гироскоп) и его географических координатах. В Qt для этого есть два модуля: Qt Sensors — для работы с физическими датчиками устройства, и Qt Positioning — для определения геолокации через GPS, Wi-Fi или сотовые сети.

Qt Sensors: чтение акселерометра

Подключение модуля в CMake:

find_package(Qt6 REQUIRED COMPONENTS Sensors Qml Quick)
target_link_libraries(MyApp PRIVATE Qt6::Sensors Qt6::Qml Qt6::Quick)

Пример на QML — чтение данных акселерометра:

import QtQuick
import QtSensors

Item {
    id: root
    width: 400; height: 400

    Accelerometer {
        id: accel
        active: true
        dataRate: 50 // частота опроса в Гц

        onReadingChanged: {
            console.log("x:", reading.x, "y:", reading.y, "z:", reading.z)
        }
    }

    // Визуализация наклона устройства
    Rectangle {
        width: 40; height: 40
        color: "tomato"
        radius: 20
        x: root.width / 2 - width / 2 + accel.reading.x * 10
        y: root.height / 2 - height / 2 + accel.reading.y * 10
    }
}

Тот же пример на C++:

#include <QAccelerometer>
#include <QAccelerometerReading>

QAccelerometer *sensor = new QAccelerometer(this);
sensor->setDataRate(50);

QObject::connect(sensor, &QAccelerometer::readingChanged, [sensor]() {
    QAccelerometerReading *reading = sensor->reading();
    qDebug() << "x:" << reading->x()
              << "y:" << reading->y()
              << "z:" << reading->z();
});

sensor->start();

Пояснения к коду

  • dataRate задаёт желаемую частоту опроса в герцах; реальная частота зависит от возможностей конкретного датчика устройства.
  • Свойство reading обновляется автоматически и доступно через сигнал readingChanged.
  • Помимо Accelerometer, доступны Gyroscope, Magnetometer, LightSensor, ProximitySensor, RotationSensor и другие — все они следуют одинаковому паттерну API.

Qt Positioning: получение геолокации

Подключение модуля:

find_package(Qt6 REQUIRED COMPONENTS Positioning Qml Quick)
target_link_libraries(MyApp PRIVATE Qt6::Positioning Qt6::Qml Qt6::Quick)

Пример на QML:

import QtQuick
import QtPositioning

Item {
    PositionSource {
        id: positionSource
        updateInterval: 5000  // запрашивать координаты каждые 5 секунд
        active: true

        onPositionChanged: {
            var coord = positionSource.position.coordinate
            console.log("Широта:", coord.latitude,
                        "Долгота:", coord.longitude,
                        "Точность:", positionSource.position.horizontalAccuracy)
        }

        onSourceErrorChanged: {
            if (sourceError !== PositionSource.NoError)
                console.log("Ошибка геолокации:", sourceError)
        }
    }

    Text {
        anchors.centerIn: parent
        text: positionSource.position.coordinate.latitude + ", " +
              positionSource.position.coordinate.longitude
    }
}

Аналог на C++:

#include <QGeoPositionInfoSource>
#include <QGeoPositionInfo>

QGeoPositionInfoSource *source = QGeoPositionInfoSource::createDefaultSource(this);
if (source) {
    source->setUpdateInterval(5000);

    connect(source, &QGeoPositionInfoSource::positionUpdated,
            this, [](const QGeoPositionInfo &info) {
        QGeoCoordinate coord = info.coordinate();
        qDebug() << "Координаты:" << coord.latitude() << coord.longitude();
    });

    source->startUpdates();
}

Пояснения к коду

  • PositionSource автоматически выбирает наилучший доступный источник координат на устройстве (GPS, сеть, эмулятор позиции в desktop-режиме).
  • QGeoPositionInfoSource::createDefaultSource() возвращает nullptr, если на платформе нет доступного провайдера позиционирования — это обязательно нужно проверять перед использованием.
  • horizontalAccuracy показывает радиус точности в метрах — полезно для отображения пользователю достоверности координат.

Запрос геокодирования (адрес ↔ координаты)

#include <QGeoCodingManager>
#include <QGeoServiceProvider>

QGeoServiceProvider provider("osm"); // OpenStreetMap-провайдер
QGeoCodingManager *geocodingManager = provider.geocodingManager();

if (geocodingManager) {
    QGeoCodeReply *reply = geocodingManager->geocode("Москва, Красная площадь");
    QObject::connect(reply, &QGeoCodeReply::finished, [reply]() {
        if (reply->error() == QGeoCodeReply::NoError && !reply->locations().isEmpty()) {
            QGeoCoordinate coord = reply->locations().first().coordinate();
            qDebug() << "Найдены координаты:" << coord;
        }
    });
}

Разрешения на мобильных платформах

На Android и iOS доступ к датчикам и геолокации требует явного запроса разрешений у пользователя в runtime. В Qt6 для этого предусмотрен модуль Qt Core с классом QPermission:

#include <QCoreApplication>
#include <QPermissions>

QLocationPermission locationPermission;
locationPermission.setAccuracy(QLocationPermission::Precise);

switch (qApp->checkPermission(locationPermission)) {
case Qt::PermissionStatus::Undetermined:
    qApp->requestPermission(locationPermission, [](const QPermission &permission) {
        if (qApp->checkPermission(permission) == Qt::PermissionStatus::Granted)
            qDebug() << "Доступ к геолокации разрешён";
    });
    break;
case Qt::PermissionStatus::Denied:
    qDebug() << "Доступ к геолокации запрещён пользователем";
    break;
case Qt::PermissionStatus::Granted:
    qDebug() << "Доступ уже есть";
    break;
}

Также не забудьте добавить соответствующие разрешения в AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Подводные камни

  • Эмуляторы и desktop без GPS. На десктопе QGeoPositionInfoSource может возвращать координаты только если настроен симулятор позиции (например, через QGeoPositionInfoSourceFactory с тестовым backend) — не рассчитывайте на реальные данные при разработке на ПК.
  • Различная точность датчиков на разных устройствах. Не все Android-устройства имеют гироскоп или магнитометр — всегда проверяйте QSensor::isFeatureSupported() или соответствующий метод перед использованием.
  • Энергопотребление. Частый опрос GPS (updateInterval меньше 1000 мс) серьёзно увеличивает расход батареи — выбирайте интервал обновления исходя из реальных потребностей приложения.
  • Разрешения нужно запрашивать асинхронно и обрабатывать случай отказа пользователя gracefully — без падения приложения и с понятным сообщением о том, почему функциональность недоступна.
  • iOS требует дополнительных строк в Info.plist (NSLocationWhenInUseUsageDescription и т.п.) — без них приложение будет отклонено при публикации в App Store.

Совет

Перед запросом доступа к датчикам или геолокации объясните пользователю, зачем это нужно приложению (например, через диалог-объяснение до системного запроса разрешения) — это заметно повышает процент пользователей, дающих согласие, по сравнению с прямым системным запросом без контекста.