Symbian-Developers

The Official Developers Section => Symbian Software Development Discussions => The Developers Section => Qt Application Development => Topic started by: pisarz1958 on August 19, 2013, 09:31:34 pm

Title: [TUT] Homescreen Widget in QML, because - why not?
Post by: pisarz1958 on August 19, 2013, 09:31:34 pm
Hi everyone! I promised Allstar that I'll write this tutorial. In past few days, I was resting a bit, and now, let's get to work, shall we? :) So, in this tutorial, I'm going to show you, how to implement homescreen widget in Qt and make necessary QML bindings. I hope you find it useful.

Beginning: What's the problem?
So, why this stuff isn't exactly simple? The problem is, there are no up to date APIs for homescreen publishing in Belle. We're going to use N97 Homescreen Publishing API (Symbian s60v5). But don't worry, it's fairy simple :)

PREPARE YOURSELF!
Before we get started, let's get required files. There is QHSWidget example somewhere, but it required copying files to SDK folder and compiling with s60v5 SDK. Fortunately, someone provided precompiled dll files, which we can simply use without doing all this stuff. You can get them here (https://projects.developer.nokia.com/hswidgetsinqt/files). There are two DLL files, urel and udeb. You need to choose one, which suits your compilation method. If you're building your app with debug option, choose "udeb". If you're building your app with release option, choose "urel".

Then, you need qhswidget.h from the example. You can get it from linked website, or you can also find it in my GitHub repository for Lightbulb, here (https://github.com/ksiazkowicz/lightbulb/blob/master/src/avkon/qhswidget.h). After adding it to your project and copying .dll into projects folder, let's do something fun, finally :D

Preparing project
First, let's rename dll file. It has to be done to avoid name clashing. Good idea would be to put UID there. Then, open qhswidget.h and change line containing DLL name, like this:
Code: [Select]
const QString DLLName("[your DLL name]");Also, you need to update your .pro file, so the DLL would be a part of SIS file. This is, how I did it in Lightbulb.
Code: [Select]
addFiles.pkg_postrules += "\"C:\\Projekty\\Lightbulb\\HSWidgetPlugin0xE22AC278.dll\" - \"!:\\sys\\bin\\HSWidgetPlugin0xE22AC278.dll\""
DEPLOYMENT += addFiles
Replace "C:\Projekty\Lightbulb\HSWidgetPlugin0xE22AC278.dll" with path to your .dll file. Don't forget about double slashes :)
This way, our dll file will be put in !:\sys\bin, where ! is destination drive. You could also want to do last part of the original instruction here (https://projects.developer.nokia.com/hswidgetsinqt/wiki/QHSWidgetPlugin) if you want to prepare your app for publishing. As I haven't done it yet, I'm not explaining this.

You will also need some libraries, so put this stuff in .pro file:
Code: [Select]
#that library should be copied to SDK folder
#LIBS += -lhswidgetpublisher
#Libs for appswitcher
LIBS += -lapgrfx -lcone -lws32
#Libs for CFbsBimap
LIBS += -lbitgdi -lfbscli -laknskins -laknskinsrv -leikcore
Not sure, if all of these is even needed, but nevermind. :D

I hope you added qhswidget.h to project tree in this file. Just add it's name in "headers" section of the project file if you haven't done it yet.

Widget class
Now, let's add new C++ class, the same way it was done in previous tutorial. If you don't remember, how it was done, or haven't read it, a simple reminder:
(http://i.imgur.com/jqXEyaj.png)
This time, use different name, for example "hswidget". Make sure class is derived from QObject, it's somehow important. If you're ready, let's go to cpp file. Add required includes first:
Code: [Select]
#include "hswidget.h"
#include <QDebug>
#include <apgtask.h>
#include <eikenv.h>
QDebug itself isn't required, but if you want to add debug stuff here, you would need it. Then, you have to choose proper type of widget, for example:
Code: [Select]
const QString sw_type = "threerows";There are five layouts available.

(http://i.imgur.com/5OaI2d3.png)
First one, "wideimage". In this widget, image fills the space. You can use it as a template for your own layout, put text there etc. I guess that it can be transparent with svg or png file of your choice, though I've never explored this option.

(http://i.imgur.com/6tA930s.png)
Second one is "onerow". It contains one row of text and an icon. Really basic layout.

(http://i.imgur.com/WFLL8Q0.png)
Need more space for your text? What about "tworows"? What is important, you need to set text in every row by yourself. So, they can contain different informations.

(http://i.imgur.com/dCt4QUf.png)
"threerows". Holds three rows of text and an icon.

(http://i.imgur.com/9xt7k1S.png)
You don't like icons anyway, do you? So, for people like you, there is also "threetextrows" layout, which contains only images.

If you have chosen right layout for your project, we can go further. You need a line that defines widgets name.
Code: [Select]
const QString sw_name = "Amazing and awesome widget";
Also, you should put your UID in the next line. Though, it can be any unique string. Even "ILoveSlicedBread", or whatever. But UID is the best solution against name clashing. :)
Code: [Select]
const QString sw_id = "0xE22AC278"; //can be any unique string
Now, let's specify some rows.
Code: [Select]
const QString sw_image ("image1");
const QString row1 ("text1");
I did it this way. Name of QString doesn't really matter though. This other stuff does. For more rows, just use "text2" and "text3".

We need to add some functions now.
Code: [Select]
HSWidget::HSWidget(QObject *parent) :
    QObject(parent)
{

    widget = QHSWidget::create(sw_type, sw_name, sw_id, this);
    connect(widget, SIGNAL(handleEvent(QHSWidget*, QHSEvent)), this, SLOT(handleEvent(QHSWidget*, QHSEvent) ));
    connect(widget, SIGNAL(handleItemEvent(QHSWidget*, QString, QHSItemEvent)), this, SLOT(handleItemEvent(QHSWidget*, QString, QHSItemEvent)));
}
This way, we're creating QHSWidget and doing necessary connections, so we could handle all the events.

Code: [Select]
void HSWidget::registerWidget()
{
    widget->RegisterWidget();

}
This function will allow us to register widget directly from QML file. This makes widget available for the user and should be done in first run.

Code: [Select]
void HSWidget::publishWidget()
{
    try {
        widget->PublishWidget();
    }
    catch (...) {
    }
}
This function updates widget. Also, I added this "try" stuff, so my app wouldn't crash when widget is not on the screen. I guess I could do it in much better way, but nevermind.

Code: [Select]
void HSWidget::removeWidget()
{
    widget->RemoveWidget();
}
Unregisters the widget, which makes it unavailable, like uninstalling or something.

Code: [Select]
void HSWidget::handleEvent( QHSWidget* /*aSender*/, QHSEvent aEvent )
{
    switch(aEvent)
                    {
                    case EActivate:
                    case EResume:
                            {
                            publishWidget();
                            }
                            break;
                    default:
                            break;
                    }

}
Let's handle some events, ok? In case widget was activated (EActivate) or resumed from suspension (EResume), it would be automatically updated. How it works? When widget is not on currently active homescreen, simply gets suspended, so we can't update it in this time. Also, activation event means that someone added our widget to homescreen. There is also "ESuspend" which let's you know that widget was suspended. For more details, just refer to "Home Screen Publishing API" documentation.

Code: [Select]
void HSWidget::handleItemEvent( QHSWidget* /*aSender*/, QString aTemplateItemName,
    QHSItemEvent aEvent)
{
    if(aTemplateItemName.compare(sw_image)==0)
                    {
                    publishWidget();
                    }
            else
                    {
                        this->bringToFront();
                    }
}
This code is required to bring our app to front if someone tapped it's icon in widget. Every element can have different actions. You can also use signals etc. I haven't tried it yet though, so I won't explain it unfortunately.

Code: [Select]
void HSWidget::bringToFront()
{
    TApaTask task( CEikonEnv::Static()->WsSession() );
    task.SetWgId(CEikonEnv::Static()->RootWin().Identifier());
    task.BringToForeground();
}
And this is a simple function that brings our app to front and automatically founds out, which is our UID. Nice stuff.

Finally, we would like to make a function which would allow us to change displayed data right from QML. This is my, extremely basic approach:

Code: [Select]
void HSWidget::updateWidget( QString icon, QString nRow1 )
{
    widget->SetItem(sw_image, icon);
    widget->SetItem(row1, nRow1);
    publishWidget();
}

I guess this is quite simple, isn't it? :) So... let's get to header file! Just copy it, I guess it doesn't require any explanations.
Code: [Select]
#ifndef HSWIDGET_H
#define HSWIDGET_H

#include <QObject>
#include "qhswidget.h"

class HSWidget : public QObject
{
    Q_OBJECT
public:
    explicit HSWidget(QObject *parent = 0);
    Q_INVOKABLE void registerWidget();
    Q_INVOKABLE void publishWidget();
    Q_INVOKABLE void removeWidget();
    Q_INVOKABLE void updateWidget( QString icon, nRow1 );
    void bringToFront();
public slots:
    void handleEvent(QHSWidget*, QHSEvent aEvent );
    void handleItemEvent(QHSWidget*, QString aTemplateItemName,
                         QHSItemEvent aEvent);
private:
    QHSWidget* widget;
};

#endif // HSWIDGET_H

Super simple stuff :)

QML bindings!
Now, let's go back to main.cpp. Remember, what we did previously? We're going to do the same again! First, include header file of our new class.
Code: [Select]
#include "hswidget.h"
Then, register it as QML object:
Code: [Select]
qmlRegisterType<hswidget>("someVeryAwesomeStuff", 1, 0, "HSWidget");And some explanations, if you haven't read previous tutorial:
Quote
Feel free to replace "someVeryAwesomeStuff" with anything you want. You will use this name later in QML file. Those numbers is version. "GlobalNote" is the name of our new QML object.

Now, just create an object in QML of "HSWidget" type, for example in main.qml, and you can use it, just like that:
Code: [Select]
hswidget.registerWidget()
Code: [Select]
hswidget.publishWidget()
Code: [Select]
hswidget.removeWidget()
Code: [Select]
hswidget.updateWidget(iconPath, "I really love sliced bread")
Thanks for reading, I hope you found it useful.

Sources:
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: huellif on August 19, 2013, 11:54:44 pm
before I used it from pure Qt, but QtQuick is really nice.
Cool tutorial :)
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: Allstar12345 on August 31, 2013, 06:47:50 pm
I get compile errors everytime :(
Code: [Select]
In file included from C:/Users/me/FacebookTouchQt/main.cpp:26:
   C:/Users/me/FacebookTouchQt/hswidget.h: At global scope:
   C:/Users/me/FacebookTouchQt/hswidget.h:15: error: 'nRow1' has not been declared
   C:/Users/me/FacebookTouchQt/main.cpp: In function 'int main(int, char**)':
   C:/Users/me/FacebookTouchQt/main.cpp:135: error: 'hswidget' was not declared in this scope
   C:/Users/me/FacebookTouchQt/main.cpp:135: error: no matching function for call to 'qmlRegisterType(const char [9], int, int, const char [9])'
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: pisarz1958 on August 31, 2013, 07:32:27 pm
PM me your hswidget (cpp and header), I guess you've forgot to include something.
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: Allstar12345 on December 22, 2013, 05:50:50 pm
Still get the same errrors on another project :-/

Edit: fixed
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: matthew on December 23, 2013, 12:08:10 am
Whoa! Thanks for this tutorial, pisarz ;)

Add: This has shed a lot of light on what i have been staring at and wondering about in the HS widget framework for a while. Thanks again :)
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: Allstar12345 on December 24, 2013, 03:37:15 am
Topic Stickied.

Also you don't need to register it then import it, this seems to be working for me:
Code: [Select]
HSWidget hsWidget(&view);
            view.rootContext()->setContextProperty("HSWIDGET", &hsWidget);

Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: pisarz1958 on December 24, 2013, 11:50:06 pm
Sure, I just prefer registering because my app is already using it everywhere.
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: Motaz on December 25, 2013, 02:11:58 am
Thank you pisarz1958 :)

I make some adjustments to HSWidget Plugin and I added this options:
 - Add widget icon
 - Add widget description

Just download the plugin for attachment and change the UID for it. (Use EpocID by CODeRUS (https://sites.google.com/site/icoderus/epocid.zip))

and I make a example that update the widget from QML ;)
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: Allstar12345 on December 25, 2013, 02:19:48 am
Thank you pisarz1958 :)

I make some adjustments to HSWidget Plugin and I added this options:
 - Add widget icon
 - Add widget description

Just download the plugin for attachment and change the UID for it. (Use EpocID by CODeRUS (https://sites.google.com/site/icoderus/epocid.zip))

and I make a example that update the widget from QML ;)

Excellent mate :)
+rep going your way
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: pisarz1958 on December 25, 2013, 03:47:51 am
Thank you pisarz1958 :)

I make some adjustments to HSWidget Plugin and I added this options:
 - Add widget icon
 - Add widget description

Just download the plugin for attachment and change the UID for it. (Use EpocID by CODeRUS (https://sites.google.com/site/icoderus/epocid.zip))

and I make a example that update the widget from QML ;)
+1 rep

Really interesting changes you've made :)
Title: Re: [TUT] Homescreen Widget in QML, because - why not?
Post by: huellif on December 26, 2013, 01:10:35 am
nice work mate :)