×
Namespaces

Variants
Actions
(Difference between revisions)

Rotating QWidget based applications on MeeGo Harmattan

From Nokia Developer Wiki
Jump to: navigation, search
gnuton (Talk | contribs)
(Gnuton -)
 
hamishwillee (Talk | contribs)
m (Text replace - "<code cpp>" to "<code cpp-qt>")
 
(30 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[Category:Draft]]
+
[[Category:Qt]][[Category:MeeGo Harmattan]][[Category:Qt Quick]][[Category:Qt Mobility]][[Category:Qt C++ UI]][[Category:UI]]
{{Abstract|This article explains how to make change orientation QtWidget-based application working on on Harmattan... }}
+
{{Abstract|This article explains how to rotate {{Qapiname|QWidget}}-based applications on MeeGo Harmattan. This useful when porting apps from desktop if you don't want to re-write the UI in QML.}}
 
+
 
{{ArticleMetaData
 
{{ArticleMetaData
|sourcecode= [[Media: https://gitorious.org/harmattanwidgetproxy/harmattanwidgetproxy/archive-tarball/master]]
+
|sourcecode= [https://gitorious.org/harmattanwidgetproxy/harmattanwidgetproxy/archive-tarball/master CannedSolution.tgz]
 
+
 
|devices= Nokia N9, Nokia N950
 
|devices= Nokia N9, Nokia N950
 
|sdk= [http://www.developer.nokia.com/info/sw.nokia.com/id/da8df288-e615-443d-be5c-00c8a72435f8/Qt_SDK.html Nokia Qt SDK 1.1.3 or above]
 
|sdk= [http://www.developer.nokia.com/info/sw.nokia.com/id/da8df288-e615-443d-be5c-00c8a72435f8/Qt_SDK.html Nokia Qt SDK 1.1.3 or above]
 
|platform= MeeGo 1.2 Harmattan
 
|platform= MeeGo 1.2 Harmattan
|keywords= QWidget, Orientation
+
|keywords= QWidget, Orientation, Rotation, MeeGo, Components
 
|review-by=<!-- After re-review: [[User:username]] -->
 
|review-by=<!-- After re-review: [[User:username]] -->
 
|review-timestamp=<!-- After re-review: YYYYMMDD -->
 
|review-timestamp=<!-- After re-review: YYYYMMDD -->
 
|update-by=<!-- After significant update: [[User:username]]-->
 
|update-by=<!-- After significant update: [[User:username]]-->
 
|update-timestamp=<!-- After significant update: YYYYMMDD -->
 
|update-timestamp=<!-- After significant update: YYYYMMDD -->
|creationdate= <!-- Format YYYYMMDD -->
+
|creationdate=20111024
 
|author= [[User:gnuton]]
 
|author= [[User:gnuton]]
 
}}
 
}}
  
 
== Introduction ==
 
== Introduction ==
Qt is a framework used to develop application software with a graphical user interface (GUI). It allows developers to write the application GUI using QWidget(pure C++), QtComponents(C++/QML/JS) or HTML(C++/HTML/JS).
+
Qt is a framework used to develop application software with a graphical user interface (GUI). It allows developers to write the application GUI using QWidget (pure C++), QtComponents (C++/QML/JS) or HTML(C++/HTML/JS).
  
MeeGo 1.2 Harmattan, the newest Nokia platform in the market, offers to application developers a set of QML Components which can be used to design amazing interfaces where touch input, fluid animations and user experience are crucial.
+
MeeGo 1.2 Harmattan, Nokia's latest platform in the market, offers to application developers a set of QML Components which can be used to design amazing interfaces where touch input, fluid animations and user experience are crucial.
QML Components have been optimized to squeeze out all the GPU power, providing to developers an easy way to code which isn't prone to performance loss as QWidget was.
+
For this reason, Harmattan doesn't really support QWidgets. Porting a Symbian or Maemo QWidget based application to the new platform involves the re-writting of the entire UI.
+
Write a UI with QML is much more easy than with QWidget and the porting doesn't really take so much time.
+
Anyway some developers don't really want to spend time with QML and they want to run the app on MeeGo as it is.
+
  
== QWidgets on Harmattan limitations ==
+
QML Components have been optimized to squeeze out all the GPU power, providing developers with an easy way of coding which doesn't cause performance loss as QWidget sometimes does.
QWidgets on Harmattan suffers of many limitations, where the most important are:
+
For this and other reasons Harmattan doesn't support QWidgets. Porting a Symbian or Maemo QWidget-based application to the new platform requires the re-writing of the entire UI.
* No MeeGo Style
+
Writing a UI with QML is much easier than with QWidget and the porting doesn't really take so much time.
 +
 
 +
However, some developers don't really want to spend time on QML and they want to run the app on MeeGo as it is.
 +
 
 +
== Limitations of QWidgets on Harmattan ==
 +
QWidgets have many limitations on Harmattan; the most important are:
 
* Application orientation locked in landscape
 
* Application orientation locked in landscape
 +
* No MeeGo Style
 +
In this article we will talk about the first limitation and how it can be worked around.
 +
 +
== Why don't QWidget-based apps rotate on Harmattan? ==
 +
A mobile application can be locked in landscape or potrait or even be left free to change according to the device orientation value provided by the accelerometer sensor.
  
In this article we will talk about the first limitation and the way how it can be worked around.
+
{{Icode|QWidget}} class provides a {{Icode|setAttribute()}} method that was largely used on Symbian and Maemo to set the application orientation.
 +
{{Icode|QWidget::setAttribute(orientation)}} on Harmattan doesn't work anymore because X11 server has been built without RandR extension ( [http://en.wikipedia.org/wiki/RandR X Resize, Rotate and Reflect] ). This means that the screen cannot rotate anymore and therefore it is the application itself which has to rotate.
  
== Why my QWidget-Based app doesn't rotate on Harmattan? ==
+
This is an important feature, allowing MeeGo to have a fancy and smooth transition effect when an application switches from one orientation mode to another.
QWidget class provides a setAttribute method that was largely used on Symbian and Maemo to set the application orientation.
+
A mobile application, in fact could be locked in landscape or potrait, or could even be left free to change orientation according to
+
device orientation, value which is provided by the accelerometer sensor.
+
On Harmattan QWidget::setAttribute(orientation) doesn't work anymore because X11 server has been built without the X Resize, Rotate and Reflect extension called [RandR http://en.wikipedia.org/wiki/RandR], so in few words the screen doesn't rotate anymore; is the application itself which has to rotate.
+
This is an important fact that lets MeeGo to have a fancy and smooth transition effect when an application switches from one orientation mode to another.
+
  
 
== How to work around this problem ==
 
== How to work around this problem ==
Missing rotation support on the graphics server forces the application to handle the rotation. But Unfortuantely QWidgets, which have been designed originally for Desktop, are not able to rotate.
+
In order to make the application work in portrait mode QWidgets need to be rotated. Unfortuantely {{Icode|QWidget}}s, originally designed for desktop, are not able to rotate.
The only way to rotate a QWidget is to draw it in a canvas and rotate the drawn. As you maybe know, the Qt canvas is called QGraphicsScene. The scene is displayed to the user by a QGraphicsView, and the items in the QGraphicsScene are called QGraphicsItem.
+
The only way to rotate a {{Icode|QWidget}} is to paint it in a canvas and rotate it. Qt canvas is called {{Qapiname|QGraphicsScene}}. The scene is displayed to the user by a {{Qapiname|QGraphicsView}} and the items in the {{Icode|QGraphicsScene}} are {{Qapiname|QGraphicsItems}}.
To draw a QWidget in a QGraphicsScene, we need a [http://doc.qt.nokia.com/latest/qgraphicsproxywidget.html QGraphicsProxyWidget].
+
So if we create a simple application which display as a GraphicsView as main widget and we draw the QMainWindow inside the a GraphicsView we should be able to rotate our app.
+
It seems easy but this is not really the best way, because we should write the code to animate the transition between the two orientation modes and other little things.
+
  
So a way better option is to make the graphics item which stores the QWidget, child of a MeeGo Component element.
+
In order to paint a {{Icode|QWidget}} in a QGraphicsScene we need a [http://doc.qt.nokia.com/latest/qgraphicsproxywidget.html QGraphicsProxyWidget].
 +
The easiest solution to rotate the app would be to paint the main window on {{Icode|QGraphicsScene}} and then rotate the scene. Unfortunately this is not really the best solution because developers would need to write the animation code of the transition between the two orientation modes and to adapt the application so that it displays the system bar on the top part of the screen.
  
== The solution: "Embedding" a QWidget in a MeeGo Component based application==
+
A better option would be to transform the {{Icode|QWidget}} graphics item into a QML element.
As it has been said before, MeeGo application uses MeeGo QML components and our goal is to have our application working with different orientation.
+
 
A MeeGo component application is made by a [Window] and some [Pages]. So to make our application scalable, we will to make the main window of our application child of a  Page.
+
== The solution: "Embedding" a QWidget in a MeeGo Components-based application==
For this purpose we have to "transform" our MainWindow widget in QDeclarataiveItem as the code below does.
+
As we said before, MeeGo applications use MeeGo QML components.
 +
 
 +
A MeeGo Components-based application is made up of a [Window] and some [Pages]. To rotate the application we need to paint the QWidget main view on the page. Therefore we have to "transform" the MainWindow widget into a QDeclarataiveItem as shown in the code below.
 +
 
 +
<code cpp-qt>
 +
ProxyWidget::ProxyWidget(QDeclarativeItem *parent) :
 +
    QDeclarativeItem(parent),
 +
    mEmbeddedWidget(new MainWindow())
 +
{
 +
    mEmbeddedWidget->setStyleSheet(STYLE);
 +
    mProxyWidget = new QGraphicsProxyWidget(this);
 +
    mProxyWidget->setWidget(mEmbeddedWidget);
 +
 
 +
    //mEmbeddedWidget->setAutoFillBackground(false);
 +
    mEmbeddedWidget->setAttribute(Qt::WA_TranslucentBackground);
 +
}
 +
 
 +
void ProxyWidget::geometryChanged(const QRectF &newGeometry, const QRectF & oldGeometry)
 +
{
 +
    Q_UNUSED(oldGeometry)
 +
    mProxyWidget->setGeometry(newGeometry);
 +
}
  
<code>
 
INSERT CODE HERE
 
 
</code>
 
</code>
  
Then the new object is registered and made available to the declarative view
+
The new object type is registered and available to the declarative view
<code>
+
<code cpp-qt>
 
  qmlRegisterType<ProxyWidget>("ProxyWidget", 1, 0, "ProxyWidgetItem");
 
  qmlRegisterType<ProxyWidget>("ProxyWidget", 1, 0, "ProxyWidgetItem");
 
</code>
 
</code>
  
As said before we could comment the following line since it doesn't really work:
+
The object is created in QML as shown below
<code>
+
<code javascript>
 +
import ProxyWidget 1.0
 +
 
 +
Page {
 +
    tools: commonTools
 +
    Label {
 +
        id: label
 +
        anchors.centerIn: parent
 +
        text: qsTr("Hello world!")
 +
        visible: false
 +
    }
 +
    Button{
 +
        anchors {
 +
            horizontalCenter: parent.horizontalCenter
 +
            top: label.bottom
 +
            topMargin: 10
 +
        }
 +
        text: qsTr("Click here!")
 +
        onClicked: label.visible = true
 +
    }
 +
    ProxyWidgetItem {
 +
        anchors.fill: parent
 +
    }
 +
}
 +
</code>
 +
This code is all you need to rotate the application; the line below can be removed from main.cpp, because it doesn't do anything.
 +
<code cpp-qt>
 
viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
 
viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
 
</code>
 
</code>
  
<code>
+
In case you want to lock your app in portrait mode  you need to set orientationLock property of the Page element to PageOrientation.LockPortrait as follow
import ProxyWidget 1.0
+
<code javascript>
 +
Page {
 +
    orientationLock: PageOrientation.LockPortrait
 +
</code>
  
Page {    tools: commonTools    Label {        id: label        anchors.centerIn: parent        text: qsTr("Hello world!")        visible: false    }    Button{        anchors {            horizontalCenter: parent.horizontalCenter            top: label.bottom            topMargin: 10        }        text: qsTr("Click here!")        onClicked: label.visible = true    }    ProxyWidgetItem {        anchors.fill: parent    }}
+
More information about controlling rotation is available at [http://harmattan-dev.nokia.com/docs/library/html/guide/html/Developer_Library_Developing_for_Harmattan_Controlling_rotation.html this page].
</code>
+
  
 
== Canned Solution ==
 
== Canned Solution ==
To make your application working in  easier I made a template which you could reuse. The instruction to follow in order to use it are [here].
+
To make it easier for you, I made a template which you could re-use.
Basically the canned example works in this way. Your application is built as static library and its main widget is loaded by the proxy widget we have seen before.
+
 
 +
Step-by-step instructions are available [https://gitorious.org/harmattanwidgetproxy/harmattanwidgetproxy/blobs/master/qmlproxy/README here].
 +
The code is divided into 2 parts: the first one contains the procedure described previously and the second one should contain the code of your application. This code is built as static library and its main widget is used by the proxy widget described before.
  
 
== Limitations ==
 
== Limitations ==
Biggest limitation of this solution are:
+
The main limitations of this solution are:
* QTreeWidget::setItemWidget doesn't work as expected: the bug has been reported here
+
* {{Qapiname|QTreeWidget::setItemWidget()}} doesn't work as expected: the bug has been reported [https://bugreports.qt.nokia.com/browse/QTBUG-12253 here].
* If a WebView microfocus doesn't work correctly. The virtual keyboard doesn't disappear when input html element lose the focus: SOLUTION
+
* QDialogs should not be used.
So far those are the current issue. Please edit this page or leave a comment in case you find new ones. Thanks.
+
* Rotation doesn't really work on the Qt SDK Simulator.
 +
* WebView microfocus doesn't work correctly. The virtual keyboard doesn't disappear when input html elements lose the focus. Solution is described in the block below:
 +
* The virtual keyboard cannot be closed by tapping outside the text field (QLineEdit): The following code snippet fix this problem too.  
  
 
+
In applications developed with Qt WebKit the virtual keyboard is not automatically hidden when it is unfocused. A workaround consists of adding an event filter to the affected view instance:
''Add categories below. Remove Category:Draft when the page is complete or near complete''
+
<code cpp-qt>
 
+
class EventFilter : public QObject
 
+
{   
====
+
protected:
 +
    bool eventFilter(QObject *obj, QEvent *event) {
 +
        QInputContext *ic = qApp->inputContext();
 +
        if (ic) {
 +
            if (ic->focusWidget() == 0 && prevFocusWidget) {
 +
                QEvent closeSIPEvent(QEvent::CloseSoftwareInputPanel);
 +
                ic->filterEvent(&closeSIPEvent);
 +
            } else if (prevFocusWidget == 0 && ic->focusWidget()) {
 +
                QEvent openSIPEvent(QEvent::RequestSoftwareInputPanel);
 +
                ic->filterEvent(&openSIPEvent);
 +
            }
 +
            prevFocusWidget = ic->focusWidget();
 +
        }
 +
        return QObject::eventFilter(obj,event);
 +
    }
 +
 +
private:
 +
    QWidget *prevFocusWidget;
 +
};
 +
</code>
 +
This filter can be installed on a QGraphicsView or (in QML) on a {{Icode|QDeclarativeView}} instance:
 +
<code cpp-qt>
 +
EventFilter ef;
 +
view.installEventFilter(&ef);
 +
</code>

Latest revision as of 04:13, 11 October 2012

This article explains how to rotate QWidget-based applications on MeeGo Harmattan. This useful when porting apps from desktop if you don't want to re-write the UI in QML.

Article Metadata
Code Example
Source file: CannedSolution.tgz
Tested with
Devices(s): Nokia N9, Nokia N950
Compatibility
Platform(s): MeeGo 1.2 Harmattan
Article
Keywords: QWidget, Orientation, Rotation, MeeGo, Components
Created: gnuton (24 Oct 2011)
Last edited: hamishwillee (11 Oct 2012)

Contents

[edit] Introduction

Qt is a framework used to develop application software with a graphical user interface (GUI). It allows developers to write the application GUI using QWidget (pure C++), QtComponents (C++/QML/JS) or HTML(C++/HTML/JS).

MeeGo 1.2 Harmattan, Nokia's latest platform in the market, offers to application developers a set of QML Components which can be used to design amazing interfaces where touch input, fluid animations and user experience are crucial.

QML Components have been optimized to squeeze out all the GPU power, providing developers with an easy way of coding which doesn't cause performance loss as QWidget sometimes does. For this and other reasons Harmattan doesn't support QWidgets. Porting a Symbian or Maemo QWidget-based application to the new platform requires the re-writing of the entire UI. Writing a UI with QML is much easier than with QWidget and the porting doesn't really take so much time.

However, some developers don't really want to spend time on QML and they want to run the app on MeeGo as it is.

[edit] Limitations of QWidgets on Harmattan

QWidgets have many limitations on Harmattan; the most important are:

  • Application orientation locked in landscape
  • No MeeGo Style

In this article we will talk about the first limitation and how it can be worked around.

[edit] Why don't QWidget-based apps rotate on Harmattan?

A mobile application can be locked in landscape or potrait or even be left free to change according to the device orientation value provided by the accelerometer sensor.

QWidget class provides a setAttribute() method that was largely used on Symbian and Maemo to set the application orientation. QWidget::setAttribute(orientation) on Harmattan doesn't work anymore because X11 server has been built without RandR extension ( X Resize, Rotate and Reflect ). This means that the screen cannot rotate anymore and therefore it is the application itself which has to rotate.

This is an important feature, allowing MeeGo to have a fancy and smooth transition effect when an application switches from one orientation mode to another.

[edit] How to work around this problem

In order to make the application work in portrait mode QWidgets need to be rotated. Unfortuantely QWidgets, originally designed for desktop, are not able to rotate. The only way to rotate a QWidget is to paint it in a canvas and rotate it. Qt canvas is called QGraphicsScene. The scene is displayed to the user by a QGraphicsView and the items in the QGraphicsScene are QGraphicsItems.

In order to paint a QWidget in a QGraphicsScene we need a QGraphicsProxyWidget. The easiest solution to rotate the app would be to paint the main window on QGraphicsScene and then rotate the scene. Unfortunately this is not really the best solution because developers would need to write the animation code of the transition between the two orientation modes and to adapt the application so that it displays the system bar on the top part of the screen.

A better option would be to transform the QWidget graphics item into a QML element.

[edit] The solution: "Embedding" a QWidget in a MeeGo Components-based application

As we said before, MeeGo applications use MeeGo QML components.

A MeeGo Components-based application is made up of a [Window] and some [Pages]. To rotate the application we need to paint the QWidget main view on the page. Therefore we have to "transform" the MainWindow widget into a QDeclarataiveItem as shown in the code below.

ProxyWidget::ProxyWidget(QDeclarativeItem *parent) :
QDeclarativeItem(parent),
mEmbeddedWidget(new MainWindow())
{
mEmbeddedWidget->setStyleSheet(STYLE);
mProxyWidget = new QGraphicsProxyWidget(this);
mProxyWidget->setWidget(mEmbeddedWidget);
 
//mEmbeddedWidget->setAutoFillBackground(false);
mEmbeddedWidget->setAttribute(Qt::WA_TranslucentBackground);
}
 
void ProxyWidget::geometryChanged(const QRectF &newGeometry, const QRectF & oldGeometry)
{
Q_UNUSED(oldGeometry)
mProxyWidget->setGeometry(newGeometry);
}

The new object type is registered and available to the declarative view

 qmlRegisterType<ProxyWidget>("ProxyWidget", 1, 0, "ProxyWidgetItem");

The object is created in QML as shown below

import ProxyWidget 1.0
 
Page {
tools: commonTools
Label {
id: label
anchors.centerIn: parent
text: qsTr("Hello world!")
visible: false
}
Button{
anchors {
horizontalCenter: parent.horizontalCenter
top: label.bottom
topMargin: 10
}
text: qsTr("Click here!")
onClicked: label.visible = true
}
ProxyWidgetItem {
anchors.fill: parent
}
}

This code is all you need to rotate the application; the line below can be removed from main.cpp, because it doesn't do anything.

viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);

In case you want to lock your app in portrait mode you need to set orientationLock property of the Page element to PageOrientation.LockPortrait as follow

Page {
orientationLock: PageOrientation.LockPortrait

More information about controlling rotation is available at this page.

[edit] Canned Solution

To make it easier for you, I made a template which you could re-use.

Step-by-step instructions are available here. The code is divided into 2 parts: the first one contains the procedure described previously and the second one should contain the code of your application. This code is built as static library and its main widget is used by the proxy widget described before.

[edit] Limitations

The main limitations of this solution are:

  • QTreeWidget::setItemWidget() doesn't work as expected: the bug has been reported here.
  • QDialogs should not be used.
  • Rotation doesn't really work on the Qt SDK Simulator.
  • WebView microfocus doesn't work correctly. The virtual keyboard doesn't disappear when input html elements lose the focus. Solution is described in the block below:
  • The virtual keyboard cannot be closed by tapping outside the text field (QLineEdit): The following code snippet fix this problem too.

In applications developed with Qt WebKit the virtual keyboard is not automatically hidden when it is unfocused. A workaround consists of adding an event filter to the affected view instance:

class EventFilter : public QObject 
{
protected:
bool eventFilter(QObject *obj, QEvent *event) {
QInputContext *ic = qApp->inputContext();
if (ic) {
if (ic->focusWidget() == 0 && prevFocusWidget) {
QEvent closeSIPEvent(QEvent::CloseSoftwareInputPanel);
ic->filterEvent(&closeSIPEvent);
} else if (prevFocusWidget == 0 && ic->focusWidget()) {
QEvent openSIPEvent(QEvent::RequestSoftwareInputPanel);
ic->filterEvent(&openSIPEvent);
}
prevFocusWidget = ic->focusWidget();
}
return QObject::eventFilter(obj,event);
}
 
private:
QWidget *prevFocusWidget;
};

This filter can be installed on a QGraphicsView or (in QML) on a QDeclarativeView instance:

EventFilter ef; 
view.installEventFilter(&ef);
This page was last modified on 11 October 2012, at 04:13.
141 page views in the last 30 days.
×