MeeGo 1.2 Harmattan Developer Documentation Develop for the Nokia N9

explorer.cpp Example File

sensors/sensor_explorer/explorer.cpp
 /****************************************************************************
 **
 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 ** All rights reserved.
 ** Contact: Nokia Corporation (qt-info@nokia.com)
 **
 ** This file is part of the examples of the Qt Mobility Components.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** You may use this file under the terms of the BSD license as follows:
 **
 ** "Redistribution and use in source and binary forms, with or without
 ** modification, are permitted provided that the following conditions are
 ** met:
 **   * Redistributions of source code must retain the above copyright
 **     notice, this list of conditions and the following disclaimer.
 **   * Redistributions in binary form must reproduce the above copyright
 **     notice, this list of conditions and the following disclaimer in
 **     the documentation and/or other materials provided with the
 **     distribution.
 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
 **     the names of its contributors may be used to endorse or promote
 **     products derived from this software without specific prior written
 **     permission.
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

 #include "explorer.h"
 #include <QTimer>
 #include <QDebug>
 #include <qsensor.h>
 #include <QMetaObject>
 #include <QMetaProperty>

 QTM_USE_NAMESPACE

 Explorer::Explorer(QWidget *parent)
     : QMainWindow(parent)
     , m_sensor(0)
     , ignoreItemChanged(false)
 {
     ui.setupUi(this);
 #ifdef MAEMO5
     ui.label->hide();
 #endif
     // 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();

     foreach (const QByteArray &type, QSensor::sensorTypes()) {
         qDebug() << "Found type" << type;
         foreach (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();
     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 crap = typeName.lastIndexOf("::");
         if (crap != -1)
             typeName = typeName.mid(crap + 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 == "sensorid" ||
             //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 crap = typeName.lastIndexOf("::");
         if (crap != -1)
             typeName = typeName.mid(crap + 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;
             foreach (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;
             foreach (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)
 #ifdef MAEMO5
     length += 10; // required for N900 UI
 #endif
     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 crap = typeName.lastIndexOf("::");
         if (crap != -1)
             typeName = typeName.mid(crap + 2);
         QLatin1String name(mo->property(i).name());
         QTableWidgetItem *value = ui.reading->item(row, 3);
         QVariant val = mo->property(i).read(reading);
         if (typeName == "qtimestamp") {
             value->setText(QString("%1").arg(val.value<qtimestamp>()));
         } else 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()));
 }