Coding for Maemo devices with Qt

Hello World

A simple Hello World program will give you a first look at using Qt inside Scratchbox. The hello_world.cpp file in Listing 1 displays a friendly "Hello" message.

Lines 1 and 2 of Listing 1 include two header files that contain definitions for the QApplication and QLabel type. You will probably include the QApplication header file in all your Qt applications. In lines 5 and 6, the Qt application is instantiated, and a label that is automatically inserted into the Qt application is created. The command in line 7 is used to show the QLabel with the message Hello Linux Magazine!" and, in Line 8, the application's main loop is closed.

To compile and run this application, open a terminal and type the following command to enter the Scratchbox environment:

/scratchbox/login

In vi, create the new file hello_lm.cpp in your home directory:

vi ~/hello_lm.cpp

Paste the contents of Listing 1 and save the file with :x"+Enter.

Listing 1

hello_lm.cpp

01 #include <QApplication>
02 #include <QLabel>
03
04 int main(int argc, char* argv[]) {
05     QApplication app(argc,argv);
06     QLabel *label = new QLabel    ("Hello Linux Magazine!");
07     label->show();
08     return app.exec();
09 }

Next, run the following to make the .pro file (used to generate the makefile) and compile your application:

qmake -project
qmake hello_lm.pro
make

To execute the application on Scratchbox, start the Hildon application framework, which will launch a screen that emulates the Maemo desktop.

af-sb-init.sh start

To execute the Hello World application, you just need to run the command below and your application will show up on the Maemo desktop emulated and opened in the previous step:

run-standalone.sh ./hello_world

The run-standalone.sh script is responsible for correcting the look and feel for Qt applications. The resulting application is shown in Figure 5.

Figure 5: Basic Hello World application in Qt.

A Text Editor

Another example of a simple Qt application is a very limited text editor written in Qt's Python variant PyQt. To implement the text editor, create a new file on your desktop, paste the source code shown in Listing 2, and save the file as texteditor.py [7].

Listing 2

texteditor.py

01 import sys
02 from PyQt4 import QtGui
03 from PyQt4 import QtCore
04
05 class TextEditor(QtGui.QMainWindow):
06
07     def __init__(self, parent=None):
08         QtGui.QMainWindow.__init__(self, parent)
09         self.setWindowTitle('My Text Editor')
10         self.__fileName = ""
11         self.__textEdit = QtGui.QTextEdit()
12         self.setCentralWidget(self.__textEdit)
13         menubar = self.menuBar()
14         action = {"New": self.new, "Open": self.open,                   "Save": self.save, "Exit": self.exit}
15         for (label, action) in action_menu.items():
16             action = QtGui.QAction(label, self)
17             self.connect(action,                 QtCore.SIGNAL('triggered()'), action)
18             menubar.addAction(action)
19
20     def new(self):
21         self.__textEdit.clear()
22         self.__fileName = ""
23
24     def open(self):
25         self.__textEdit.clear()
26         self.__fileName = QtGui.QFileDialog.getOpenFileName          (self, "Open", ".", "Text Files (*.txt)")
27         if self.__userSelectedAFile():
28             try:
29                 fsock = open(self.__fileName, "r")
30                 self.__textEdit.setPlainText(fsock.read())
31                 fsock.close()
32             except Exception, e:
33                 QtGui.QMessageBox.critical                   (self, "Open error", e.message)
34
35     def save(self):
36         if self.__fileName == "":
37             self.__fileName = QtGui.QFileDialog.getSaveFileName(self, "Save As", ".", "Text Files (*.txt)")
38             if not self.__fileName.endsWith               (".txt", QtCore.Qt.CaseInsensitive):
39                 self.__fileName = self.__fileName + ".txt"
40         try:
41             fd = open(self.__fileName, "w")
42             fd.write               (self.__textEdit.document().toPlainText())
43             fd.close()
44         except Exception, e:
45             QtGui.QMessageBox.critical               (self, "Save error", e.message)
46
47 if __name__ == '__main__':
48     app = QtGui.QApplication(sys.argv)
49     text_editor = TextEditor()
50     text_editor.show()
51     sys.exit(app.exec_())

Lines 2 and 3 of Listing 2 import the classes QtGui and QtCore. These classes are used to start the basic Qt development library. Between lines 46 and 50, QApplication and the class TextEditor are instantiated. The TextEditor class, which is defined in lines 5 to 45, is the real implementation for the Text Editor example. Between lines 7 and 18, application constructor is defined, in which the Qt main window is created through the instantiation of the QMainWindow class (line 8). A text edit component represented by QTextEdit is instantiated and added to the application GUI (lines 11 and 12). This component allows users to write text on it.

The application menu is built and added to the application by the commands in lines 13 to 18. Note that each option available in the application menu is linked to a specific method defined after the class constructor. The for loop defined in lines 15 to 18 takes an important role. For example, it links the New menu option to the class method self.new. When the New menu option is clicked by the user, it calls the TextEditor class method self.new(), which executes the operation necessary to create a new file. The same behavior occurs with the Open and Save menu options, which invoke the Text Editor class methods self.open and self.save, respectively. For homework, I will let you study the operations of these methods. The result of the TextEditor Application is shown in the Figure 6.

Figure 6: Text Editor implemented with PyQt.

To execute the application, you just need to transfer the file to the device with the scp command, open the terminal, and execute the command python texteditor.py.

Embedded Google Maps

For the last stage of this Qt journey, I will describe a more advanced example that uses Qt to build an application for viewing maps with the Qt WebKit and the Google Maps API. This mapping tool requires its own application GUI, and for this purpose, Qt offers a powerful tool called Qt Designer (Figure 7), which I'll use to build the application form. The form contains five QPushButton buttons and a QWebView web view. You can add new Qt components inside a form by dragging and dropping them from the Widget Box to the form. Through the Property Editor, you can set some component properties of a Qt component, such as the name and label. You can download and install Qt Designer with your favorite Linux distribution package manager, such as apt-get, yum, or emerge, or you can download it from the Qt official website.

Figure 7: Main window for Qt Designer.

Once you have learned a bit more about Qt Designer, you can create a new Qt application, mapsQtGoogleMapsMaemo, that has a QWebView and five QButton components inserted into it with a customized QWebView component that shows a map inside it via the Google Maps API.

First, I'll create a Qt class to communicate with the Google Maps API, which allows you to retrieve and embed maps inside your own components – in this example, the component is a QWebView. To communicate with the Google Maps API, I define a class called Map and a set of methods (Listing 3). The most important role of the Map class is implemented in the Map::geoCode method (line 20), which receives a name and loads the map of the specified city with Google Maps.

Listing 3

map.cpp

01 #include <QNetworkRequest>
02 #include <QNetworkAccessManager>
03 #include <QNetworkReply>
04 #include <QDomDocument>
05 #include <QDomElement>
06 #include <QWebFrame>
07 #include <QWebPage>
08 #include <QEventLoop>
09 #include <QApplication>
10 #include <math.h>
11 #include "map.h"
12 #include "ui_showmap.h"
13
14 Map::Map(QWidget *parent) : QWebView(parent),   pendingRequests(0) {
15     manager = new QNetworkAccessManager(this);
16     connect(manager, SIGNAL(finished(QNetworkReply*)),             this, SLOT(replyFinished(QNetworkReply*)));
17     connect(this, SIGNAL(reloadMap()),             this,SLOT(loadCoordinates()));
18 }
19
20 void Map::geoCode(QString local) {
21     clearCoordinates();
22     QString requestStr(tr("http://maps.google.com/maps/                         geo?q=%1&output=%2&key=%3")
23             .arg(local)
24             .arg("csv")
25             .arg("GOOGLE_MAPS_KEY"));
26     manager->get(QNetworkRequest(requestStr));
27     ++pendingRequests;
28 }
29
30 void Map::replyFinished(QNetworkReply *reply) {
31     QString replyStr(reply->readAll());
32     QStringList coordinateStrList = replyStr.split(",");
33     if (coordinateStrList.size() == 4) {
34         QPointF coordinate(coordinateStrList[2].           toFloat(),coordinateStrList[3].toFloat());
35         coordinates << coordinate;
36     }
37     --pendingRequests;
38     if (pendingRequests < 1) {
39         emit(reloadMap());
40     }
41 }
42
43 void Map::loadCoordinates() {
44     foreach (QPointF point, coordinates) {
45         this->page()->mainFrame()->evaluateJavaScript(
46                 QString("Open(%1,%2)").arg(point.x()).                         arg(point.y()));
47     }
48 }
49
50 void Map::clearCoordinates() {
51     coordinates.clear();
52 }

The method get, invoked in line 26, generates a response that is handled by the method Map::replyFinished, as defined in line 16. This method parses and stores the latitude and longitude in an array of coordinates when the program emits the Map::reloadMap signal (line 39). As defined in line 17, the Map::reloadMap signal is handled by the method Map::loadCoordinates (line 43). This method uses QtWebKit to invoke a JavaScript function called Open defined in the HTML content loaded in the Map component requested previously in line 26. The Map class definition is shown in Listing 4.

Listing 4

map.h

01 #ifndef HEADER_H
02 #define HEADER_H
03 #include <QWebView>
04
05 class QNetworkAccessManager;
06
07 class Map : public QWebView {
08     Q_OBJECT
09 public:
10         Map(QWidget *parent=0);
11 public slots:
12         void replyFinished(QNetworkReply*);
13         void loadCoordinates();
14         void geoCode(QString);
15         void clearCoordinates();
16 signals:
17         void reloadMap();
18 private:
19         QNetworkAccessManager *manager;
20         QList<QPointF> coordinates;
21         int pendingRequests;
22 };
23 #endif

The next step is to promote the QWebView component to be controlled by the Map class. Open the UI file of the Qt project in a text editor and change the base class of your custom component to QWebView by adding the Map class and its header file and specifying that the Map class extends to the QWebView class (Listing 5). After saving the UI file, open it with Qt Designer and promote QWebView to your custom component, as shown in Figure 8.

Figure 8: Promoting the QWebView component to a Map class.

Listing 5

Extending the Map Class

01 <customwidget>
02      <class>Map</class>
03      <extends>QWebView</extends>
04      <headers>map.h</headers>
05  </customwidget>

The main screen is defined by the class MainScreen, as shown in Listing 6. This class uses the Map component defined previously to show city locations on the map, which is loaded in the QWebView component. Listing 7 shows the MainScreen class definition. Note that the main screen has QWebView promoted to the Map class and five buttons to display city locations for New York, Washington, Palo Alto, Boston, and Las Vegas. You can extend this idea to whatever city you prefer or add a text editor component that allows the user to specify the name of a city. Note in Listing 6 that the click event for each city button invokes the method Map::getCode, passing as an argument the name of the city.

Listing 6

mainscreen.cpp

01 #include <QVBoxLayout>
02 #include <QDir>
03 #include <QWebView>
04 #include <QHeaderView>
05 #include <QDebug>
06 #include <QWebFrame>
07 #include <QWebPage>
08 #include <QPointF>
09 #include <math.h>
10 #include "mainscreen.h"
11 #include "ui.h"
12
13 MainScreen::MainScreen(QWidget *parent):QWidget(parent) {
14     setupUi(this);
15     map->load(QUrl("./index.html") ) ;
16 }
17
18 void MainScreen::on_newyorkbt_clicked() {
19     map->clearCoordinates();
20     map->geoCode("New York");
21 }
22
23 void MainScreen::on_washingtonbt_clicked() {
24      map->clearCoordinates();
25      map->geoCode("Washington");
26 }
27
28 void MainScreen::on_paloaltobt_clicked() {
29       map->clearCoordinates();
30       map->geoCode("Palo Alto");
31 }
32
33 void MainScreen::on_bostonbt_clicked() {
34     map->clearCoordinates();
35     map->geoCode("Boston");
36 }
37
38 void MainScreen::on_lasvegasbt_clicked() {
39     map->clearCoordinates();
40     map->geoCode("Las Vegas");
41 }

Listing 7

mainscreen.h

01 #ifndef  SCREEN_H
02 #define  SCREEN_H
03 #include <QtGui>
04 #include <QWidget>
05 #include <QWebView>
06 #include <QStandardItemModel>
07 #include "ui_showmap.h"
08
09 class MainScreen : public QWidget ,public Ui::Form {
10     Q_OBJECT
11 public :
12     MainScreen(QWidget *parent=0);
13 public slots :
14     void on_newyorkbt_clicked();
15     void on_washinghtonbt_clicked();
16     void on_paloaltobt_clicked();
17     void on_bostonbt_clicked();
18     void on_lasvegasbt_clicked();
19 };
20 #endif

In the MainScreen constructor, specifically (line 15, Listing 6), the application loads an HTML file called index.html that contains JavaScript code to show the city location with the Google Maps API. The JavaScript initialize function embedded in the index.html file instantiates a GMap2 object with a center point at (0, 0) and zoom level 1. In this case, the loaded map is shown entirely in the QWebView component. The JavaScript Open function updates the GMap2 center point to the arguments passed as parameters and the zoom level to 11.

The index.html source code is shown in Listing 8. The Google Maps API is retrieved in line 5, and the JavaScript functions initialize and Open are defined in lines 9 and 16, respectively.

Listing 8

index.html

01 <html>
02   <head>
03     <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
04     <title>Google Maps JavaScript API     Example</title>
05     <script src="http://maps.google.    com/maps?file=api&amp;    v=2&amp;key=GOOGLE_MAPS_KEY"    type="text/javascript"></script>
06     <script type="text/javascript">
07       var map;
08
09       function initialize() {
10         if (GBrowserIsCompatible()) {
11           map = new GMap2          (document.getElementById           ("map"));
12           map.setCenter          ( new GLatLng(0,0),1 );
13         }
14       }
15
16       function Open(x,y) {
17         map.setCenter        ( new GLatLng(x,y),11 );
18       }
19     </script>
20   </head>
21   <body onload="initialize()"   onunload="GUnload()" topmargin="0"  leftmargin="0">
22     <div id="map" style="width:    521px; height: 381px"></div>
23   </body>
24 </html>

To compile and run the map example, make sure you specify the necessary Qt libraries in your Qt project file:

QT += network webkit xml

Now compile the example with Scratchbox by executing the commands qmake mapsQtGoogleMapsMaemo.pro and make, copy the generated binary file and the index.html file to the device with the scp command, and execute it by opening a terminal and typing mapsQtGoogleMapsMaemo. The view should be very similar to Figure 9.

Figure 9: Screenshot of the map app on a Maemo device.

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Universal Plug and Play

    Universal Plug and Play provides an easy framework for seamless integration of network devices. Learn how to build your own UPnP solution using the open source BRisa framework.

  • Cold War at the Eighth KDE PIM Gathering

    The eighth annual KDE PIM developer meeting in Osnabrück, Germany started out with an extended snowball fight among the Scottish, German and Dutch contingencies. That actual work was being done was evidenced by enhancements to Akonadi, KDE 4.4 and 4.5, and planned further development of the Kontact groupware client.

  • Panda3D

    Several free game engines are available for Linux users, but programming with them is often less than intuitive. Panda3D is an easy-to-use engine that is accessible enough for newcomers but still powerful enough for the pros at Disney Studios.

  • MeeGo Repositories: Code Available for Moblin-Maemo Merger

    Imad Sousou, director of Intel's Open Source Technology Center, has released the first parts for developers of the Linux-based MeeGo platform for mobile devices.

  • Qt 4.7 Preview for N900

    Qt developers have packaged a preview for the upcoming version 4.7 of the toolkit for the Maemo N900 smartphone. The software packages are intended for developers.

comments powered by Disqus

Direct Download

Read full article as PDF:

News

njobs Europe
What:
Where:
Country:
Njobs Netherlands Njobs Deutschland Njobs United Kingdom Njobs Italia Njobs France Njobs Espana Njobs Poland
Njobs Austria Njobs Denmark Njobs Belgium Njobs Czech Republic Njobs Mexico Njobs India Njobs Colombia