Saturday, December 18, 2010

[Qt] Push-ups Counter: Step2-Look&Feel

So ... It looks like I lack that talent of Look&Feel stuff. I've been trying to make something fancy but I just couldn't.

Anyways, this is the last version of the application. It has only one screen. It contains the option of "Silent Mode" which disables the beep during exercise (the beep of reaching the up/down level). And when stopping the counter, it shows a message with the time elapsed since starting.


Here is the code in case you want to take a look: (Download)



Creative Commons License
Blog Example by Mahmoud Adly Ezzat is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Saturday, December 11, 2010

[Qt] Push-ups Counter: Step1-Functionality

I made the first version of my Push-ups counter using Qt. It is based on using the accelerometer to sense the movement of the mobile. In this version:
- It has a settings screen with only one option till now: Mute Sound.
- The next screen counts the push-ups depending on some internally-set variables (the positions of up/down)
- It makes a beep when I reach the up/down position, so I know if I'm up/down enough.
= I noticed that if my arm shakes, it may count extra points.(to be fixed)




The main important points in the application are how to read the device's position and how to count.
To read the position, I read the "x" value of the accelerometer:
I use two classes: QSensor, QSensorReading

QSensor *sensor = new QSensor("QAccelerometer");
sensor->start();
QSensorReading *reading = sensor->reading();
int value = abs(reading->property("x").value<qreal>()/1);

Some notes:
- The abs() method is because the value can be +ve or -ve depending on which arm I use, so I just take the absolute value.
- The value<qreal>()/1 is a simple way of moving any noise. If I get the value as int, it will be in a big range (I can't remember it right now), and it may be useful in sensitive applications but not in this case. The range I get is 0-9. And the division by 1 is to neglect the fractions.


I added two flags to detect if the next calculation to for going up or down. An a timer to calculate the value of the accelerometer every period of time. I know that the QSensor class has a signal of readingChanged(), but it is very rapid and will exhaust the processor.

void CounterWindow::readSensorValue()
{
    int value = abs(reading->property("x").value<qreal>()/1);

    if(nextUp)
    {
        if(value <= upValue)
        {
            if(!muted) QApplication::beep();
            nextUp = false;
            nextDown = true;
            score++;
            counterLabel->setText(QString::number(score));
        }
    }
    else if(nextDown)
    {
        if(value >= downValue)
        {
            if(!muted) QApplication::beep();
            nextUp = true;
            nextDown = false;
        }
    }
}


I also had to add some Symbian code to disable the change of orientation when the mobile rotates and force the Portrait orientation.

[PushupsCounter.pro]
symbian {
    LIBS += -lcone -leikcore -lavkon
}

[main.cpp]
// Needed Symbian specific headers
#ifdef Q_OS_SYMBIAN
#include <eikenv.h>
#include <eikappui.h>
#include <aknenv.h>
#include <aknappui.h>
#endif

and add this code before calling the window
[main.cpp]
StartingWindow w;
#if defined(Q_OS_SYMBIAN)
    CAknAppUi* appUi = dynamic_cast<CAknAppUi*>(CEikonEnv::Static()->AppUi());
        if(appUi){
            QT_TRAP_THROWING(appUi ->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait));
        }
#endif
    w.showMaximized();


So... the next step is for the Look&Feel and fixing any bug that may appear.

Download v1.0



Creative Commons License
Blog Example by Mahmoud Adly Ezzat is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

Saturday, December 4, 2010

Singleton Download Queue for Qt

Every now and then, I need to use the QNetworkAccessManager class in my applications, and have some synchronization problems every now and then.

So, I used the same behavior I find in the fast food restaurant. I make an order and take a ticket with a number. When an order is ready, they call for the number of it's ticket. If it is mine, I go and get it.

Here are the important methods:
[DownloadQueue]
public:
    int         addUrl(QUrl url);
    bool        removeUrl(int index);

    QByteArray  getData(int index);
    bool        removeData(int index);

    int         getCurrentIndex();
    bool        clear();

signals:
    void downloadFinished(int index);

When I want to make a new download, I add the url to the DownloadQueue class and get an index to the added url. I can remove the url if I need, using the same index.

Whenever a download is finished, the class emits a signal with its index, so I should make a slot to listen for these 'announcements'. If the announced index is the one I ordered, I can get its ready data by using getData() method.

Beware that the received data resides in the memory until you remove it, even though you called getData().

Another method is for checking the current processed index, just to know if my order is taking long or going to be next...etc. And the clear method to empty all the queue.

Another twist is made to make the same queue accessible by all classes. So the class is a singleton. And an instance is made like this
//example
DownloadQueue *d = DownloadQueue::getInstance();
Thanks to Mahmoud Mamdouh for helping me with the singleton thing.

Here is the complete class:
(Download)

[DownloadQueue.h]
#ifndef DOWNLOADQUEUE_H
#define DOWNLOADQUEUE_H

#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QObject>
#include <QTimer>
#include <QUrl>

class DownloadQueue : public QObject
{
    Q_OBJECT

public:
    static DownloadQueue* getInstance();

    int         addUrl(QUrl url);
    bool        removeUrl(int index);

    QByteArray  getData(int index);
    bool        removeData(int index);

    int         getCurrentIndex();
    bool        clear();

private:
    DownloadQueue(QObject *parent = 0);
    static DownloadQueue *_instance;
    int _index;
    QTimer _timer;
    QUrl _url;
    QList<QUrl> _urlList;
    QList<QByteArray> _dataList;
    QNetworkAccessManager _manager;

signals:
    void downloadFinished(int index);

private slots:
    void handleNetworkReply(QNetworkReply *reply);
    void downloadNext();

};

#endif // DOWNLOADQUEUE_H

[DownloadQueue.cpp]
#include "downloadqueue.h"

DownloadQueue * DownloadQueue::_instance = 0;

DownloadQueue* DownloadQueue::getInstance()
{
    if(_instance == 0) _instance = new DownloadQueue();
    return _instance;
}

DownloadQueue::DownloadQueue(QObject *parent):
        QObject(parent)
{
    _urlList.clear();
    _dataList.clear();
    connect(&_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleNetworkReply(QNetworkReply*)));

    _index = 0;

    connect(&_timer, SIGNAL(timeout()), this, SLOT(downloadNext()));
    _timer.setInterval(500);
    _timer.start();
}

int DownloadQueue::addUrl(QUrl url)
{
    _urlList << url;
    QByteArray data;
    _dataList << data;
    return _urlList.length()-1;
}

bool DownloadQueue::removeUrl(int index)
{
    if((index < _urlList.length()) || (index != _index))
    {
        _urlList.replace(index, QUrl(""));
        return true;
    }

    return false;
}

QByteArray DownloadQueue::getData(int index)
{
    QByteArray data;
    if(index < _dataList.length())
        data = _dataList.at(index);

    return data;
}

bool DownloadQueue::removeData(int index)
{
    if(index < _dataList.length())
    {
        QByteArray data;
        _dataList.replace(index, data);
        return true;
    }

    return false;
}

int DownloadQueue::getCurrentIndex()
{
    return _index;
}

bool DownloadQueue::clear()
{
    _timer.stop();
    _urlList.clear();
    _dataList.clear();
    return true;
}

void DownloadQueue::handleNetworkReply(QNetworkReply *reply)
{
    QByteArray data = reply->readAll();
    _dataList.replace(_index, data);

    emit downloadFinished(_index);
    _index++;
    _timer.start();
}

void DownloadQueue::downloadNext()
{
    if(_index < _urlList.length())
    {
        _timer.stop();
        _manager.get(QNetworkRequest(_urlList.at(_index)));
    }
}

Here is a dummy example to show the basic use of the class.(click me)


Creative Commons License
Blog Example by Mahmoud Adly Ezzat is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.