Adding the VolumeModule C++ class

The VolumeModule class acts as the interface between the QML-based UI and the QPPS library. This C++ class exposes the volume level, which is read through PPS, as a Q_PROPERTY consumable by QML.

To add the VolumeModule class:
  1. In the Projects view, right-click the QtHmi folder, then choose Add New....
  2. In the New File dialog, select C++ in the Files and Classes list, then C++ Class in the list of file types (shown in the middle), then click Choose...
  3. In the Location page of the resulting dialog, name the file VolumeModule, then click Next.
  4. In the Summary page, click Finish.
    Two new files are added to the project (volumemodule.h and volumemodule.cpp).
  5. Edit volumemodule.h and replace the contents with the following code:
    #ifndef VOLUMEMODULE_H
    #define VOLUMEMODULE_H
    
    #include <QObject>
    #include "qpps/object.h"
    
    class VolumeModule : public QObject
    {
        Q_OBJECT
    
        // Volume setting, accessible by QML
        Q_PROPERTY(double volume READ volume WRITE setVolume
                   NOTIFY volumeChanged)
    
    public:
        // Constructor
        explicit VolumeModule(QObject *parent = NULL);
    
        Q_INVOKABLE double volume() const;
    
        Q_INVOKABLE void setVolume(const double value) const;
    
    public Q_SLOTS:
        // Updates volume level when volume change is reported by PPS
        void audioStatusChanged(const QString &name, 
                                const QPps::Variant &attribute);
    
    Q_SIGNALS:
        // Emitted when the volume level changes
        void volumeChanged();
    
    private:
        // Reference to PPS object containing audio volume level
        QPps::Object *m_ppsAudioStatus;
    
        // Volume setting
        double m_volume;
    };
    
    #endif // VOLUMEMODULE_H
    
    Note: In this code excerpt, the include path for the header file that defines the QObject class is a relative path (qpps/object.h). This is because in our example, we copied the QPPS header files to the qpps subdirectory within the project directory. If you unpackaged the QPPS library code to a different location, you must adjust the include path accordingly.
  6. Edit volumemodule.cpp and replace the contents with the following code:
    #include "volumemodule.h"
    
    VolumeModule::VolumeModule(QObject *parent)
        : QObject(parent)
    {
        // Access PPS object that stores audio device status
        m_ppsAudioStatus = new QPps::Object(
            QStringLiteral("/pps/services/audio/status"),
            QPps::Object::PublishAndSubscribeMode, false, this);
    
        if (!m_ppsAudioStatus->isValid()) {
            // Print error message if unable to read audio device
            // status through PPS
            qCritical("%s Could not open %s: %s", Q_FUNC_INFO, 
                    qPrintable(m_ppsAudioStatus->path()), 
                    qPrintable(m_ppsAudioStatus->errorString()));
        }
        else {
            // Connect signal for changed attribute in PPS object
            // to handler for audio status changes
            connect(m_ppsAudioStatus, 
                SIGNAL(attributeChanged(QString,QPps::Variant)), 
                this,
                SLOT(audioStatusChanged(QString,QPps::Variant)));
        }
    }
    
    double VolumeModule::volume() const {
        return m_volume;
    }
    
    void VolumeModule::setVolume(const double value) const {
        if (value == m_volume) {
            //Don't set the volume if it's already set to that
            return;
        }
        if (!m_ppsAudioStatus->isValid()) {
            qCritical("%s Could not write %s: %s", Q_FUNC_INFO, 
                    qPrintable(m_ppsAudioStatus->path()), 
                    qPrintable(m_ppsAudioStatus->errorString()));
            return;
        }
        if (!m_ppsAudioStatus->setAttribute(
                "output.speaker.volume", value)) {
            qWarning("%s SetAttribute failed %s: %s", Q_FUNC_INFO, 
                    qPrintable(m_ppsAudioStatus->path()), 
                    qPrintable(m_ppsAudioStatus->errorString()));
        }
    }
    
    void VolumeModule::audioStatusChanged(
                            const QString &name, 
                            const QPps::Variant &attribute)
    {
        if (name == QStringLiteral("output.speaker.volume")) {
            m_volume = attribute.toDouble();
            emit volumeChanged();
        }
    }
    
  7. Edit main.cpp to contain the following code:
    Note: In the excerpt below, the sections of new code are indicated by comments containing the words NEW CODE.
    #include <QtGui/QGuiApplication>
    #include <QtQuick/QQuickView>
    #include <QScreen>
    #include <QQmlContext>
    #include <qqml.h>
    
    // BEGIN NEW CODE
    #include "volumemodule.h"
    
    void setupVolumeModule(QQuickView* view)
    {
        // Register with the Qt Metatype system
        qmlRegisterUncreatableType<VolumeModule>(
                        "com.mycompany.hmi", 
                        1, 0, "VolumeModule", 
                        QStringLiteral("Access to object"));
    
        // By passing in the view as a parent object, the 
        // VolumeModule will be deleted when its parent is deleted
        VolumeModule* volumeModule = new VolumeModule(view);
    
        // Give the view access to the VolumeModule
        view->rootContext()->setContextProperty(
                                QStringLiteral("_volumeModule"),
                                volumeModule);
    }
    // END NEW CODE
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        // Get the screens so we can dynamically size our display
        QList<QScreen*> screens = QGuiApplication::screens();
    
        // Quit if no screen is connected
        if (screens.empty()) {
            return 1;
        }
    
        // Get the width and height of the display
        int w = screens[0]->size().width();
        int h = screens[0]->size().height();
    
        QQuickView view;
    
        // BEGIN NEW CODE
        // Set up the volume control
        setupVolumeModule(&view);
        // END NEW CODE
    
        // Set the main QML UI file to this view
        view.setSource(QUrl("qrc:/qml/main.qml"));
    
        // Set up the view to have the proper size
        view.setResizeMode(QQuickView::SizeRootObjectToView);
        view.resize(w, h);
    
        // Show our user interface
        view.show();
    
        return app.exec();
    }
    
    This code gives the application access (through the view) to the VolumeModule class. Although this class isn't coded as a singleton at the Qt level, from the QML layer, the class is accessed by a singleton object called _volumeModule.
The C++ code needed for the volume control is complete. Next, you can define the UI components that allow the user to adjust the volume.