Qt and Web Content

Tags: Programming, Qt

Qt is a really nice framework to develop applications that need to be cross platform, and where you have the raw power of C++ (or even optimised C/Assembly libraries) at your fingertips. However, more and more we see web applications and web languages taking over from the traditional desktop applications. Often though, what you really need is a mix between the two: the raw power and responsiveness that a desktop application provides, while interacting with web API's and displaying web content as part of the desktop application. Good thing then that Qt allows just that... 

Qt Logo

Some people might think that Qt is aimed at mobile applications since it was purchased by Nokia some time ago from TrollTech. In the mean time, Nokia went with Windows Phone as its smartphone platform and developers from the Maemo, (no wait Meego, or is it Tizen?) community were left without a clear path for their platform of choice. Not only that, but Nokia basically killed the Symbian platform. Thing is, the original plan Nokia had for Qt was really cool: use Qt across all Nokia devices as a unified environment for application development. In other words, the aim was to be able to build applications ranging from entry level Symbian devices to high end MeeGo devices all using the same tool. Even more, it would allow for applications to be portable between different vendors and different non-Nokia operating systems. You can find some videos demonstrating this at the following links, among others:

http://www.youtube.com/watch?v=Qhba1lhBllM
http://www.youtube.com/watch?v=bjNeWwsNNEg

In any case, Qt is not limited to mobile platforms. It started its life as a cross platform toolkit, and keeps doing that today resulting in an extensive number of software applications. Together with a nice IDE called QtCreator, it is a powerful tool that allows one to build cross platform applications with rich graphical interfaces and the integration of various technologies such as database access, networking, XML, Multimedia, GL, and much more - and indeed, web content integration. 

As an example, let's develop a simple application (see screenshot below) that could form the base of a GPS tracking client, just like the one I wrote a while back.

Qt Map

The application needs to support a certain set of actions:

  • Display a map (e.g., a Google Map, combination of HTML and JavaScript) into part of its main window
  • Allow clicking on the map to get latitude and longitude coordinates (the numbers in the textboxes)
  • Allow actions on the map, in this case placing markers, from the Qt buttons
  • Clicking on a marker generates an info balloon with some information generated from the Qt side

In other words, the C++ code needs to interact with the JavaScript API of Google Maps, and data needs to be transferred from the JavaScript side to the C++ side and vice versa. To do this, Qt allows one to embed a C++ object in the JavaScript code and can also execute JavaScript functions from within the C++ code. I'll briefly show how this is done, and you can check it out in detail with the attached source code (and project to open in QtCreator).

Let's start with a couple of functions from the JavaScript side. These are present in the index.html file in the attached source. The index.html file is later on loaded into the QWebView element and displayed, and is responsible for initialising the Google Maps API among others. You could also load a URL into QWebView from a web server, or compile the iindex.html into your application by turning it into a resource.

  1.      
  2. function init(lat, lng) {        
  3.   if (GBrowserIsCompatible()) {              
  4.     map = new GMap2(document.getElementById("map"));        
  5.     map.setUIToDefault();            
  6.     map.setCenter(new GLatLng(lat, lng),15);                 
  7.  
  8.     GEvent.addListener(map, "click", function(overlay, point) {                
  9.       if (overlay){ // marker clicked                  
  10.         overlay.openInfoWindowHtml(overlay.infowindow); // open InfoWindow                
  11.       }else if (point) { // background clicked           
  12.         myObject.set(point.y,point.x); // myObject is a C++ object. It is embedded from the Qt side
  13.       }            
  14.     });        
  15.   }      
  16. }

The init() function is responsible to initialize the map and adding an event listener for clicks on the map. If we click on a marker, the info window is opened and a message is displayed (see screenshot above). Should we click anywhere else on the map, the coordinate (latitude and longitude) are set in myObject. Here, myObject comes from the C++ side - we will see how we get that one there in a little while.

  1.    
  2. function markers(one, two, message) { // the coordinates and message come from the C++ side      
  3.   if (GBrowserIsCompatible()) {
  4.     var marker = new GMarker(new GLatLng(one, two));
  5.     marker.infowindow = message;
  6.     map.addOverlay(marker);      
  7.   }    
  8. }      
  9.  
  10. function removemarkers() {
  11.   if (GBrowserIsCompatible()) {                
  12.     map.clearOverlays();      
  13.   }    
  14. }

This second function markers() will be called from the C++ side every time we want to add a marker to a set coordinate with the "Add Marker" button. The message could be any text string one wants to put into the info balloon. Similarly, the third function removemarkers() will clear the map from the markers and is called when the "Clear Markers" button.

Time to look at some of the C++ code. First thing we want to do is load the index.html file into the QWebView widget.

  1.  
  2. QString content;    
  3. QString fileName = ":/index.html";    
  4. QFile file(fileName);      
  5.  
  6.   if( file.open(QFile::ReadOnly) ){        
  7.     content = QString(file.readAll());
  8.   }else{
  9.     QMessageBox::information(this, tr("GPS Mapper"),tr("Cannot load %1.").arg(fileName));
  10.     qFatal("Error opening index html file.");
  11.   }    
  12.  
  13. file.close();      
  14.  
  15. QWebView *frame;      
  16.  
  17. frame = ui->webView;    
  18. frame->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
  19. frame->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);    
  20. frame->setHtml(content);

As said, it would be better to turn the index.html into a resource since it provides a more platform independent mechanism for storing files. I'll leave that as an exercise for the reader. The ui->webView is the QWebView widget which we added with the design component of QtCreator and named webView. We set some of its properties to disable scroll bars to make it look better.

We then have to add an object to the JavaScript side to facilitate data exchange between the two environments. This is done using the addToJavaScriptWindowObject() method. The first argument to this method is the name under which it will be available on the JavaScript side, the second is the actual C++ object you want to embed.

  1.      
  2. myObject = new JavascriptObject(this);
  3. frame->page()->mainFrame()->addToJavaScriptWindowObject("myObject",myObject);

The JavaScriptObject is a simple object used to provide an interface to both sides of the application. It contains a QPointF variable where latitude and longitude are stored together with a method to return these to the main application as well as the set() method we've seen before which will invoke a method on the C++ side to update the two QLineEdit widgets displaying the latitude and longitude values in the user interface.

Finally, we have to be able to execute the JavaScript functions when the Qt buttons are pressed. This is done as follows:

  1.  
  2. void MainWindow::on_PutMarker_clicked() {
  3.   QString marker = QString("markers(%1,%2,'You clicked me'); null")
  4.                                                       .arg(myObject->getLatLng().rx())
  5.                                                       .arg(myObject->getLatLng().ry());
  6.   ui->webView->page()->mainFrame()->evaluateJavaScript(marker);
  7. }  
  8.  
  9. void MainWindow::on_ClearMarker_clicked() {    
  10.   QString marker = "removemarkers(); null";    
  11.   ui->webView->page()->mainFrame()->evaluateJavaScript(marker);
  12. }

As you can see, the QWebView widget contains a method called evaluateJavaScript() which allows us to do just that. All we have to do is build up a string with the required arguments and name of the JavaScript functions we want to call and pass these to the evaluateJavaScript() function. While we set the message that will appear in the info balloon here as a constant, we could just as well get the required text from another QLineEdit widget.

That's it! Have a look at the attached code and have fun building your own mixed applications. Be aware that the code is deliberately kept simple and certain things could be done better by using other Qt features such as signals and slots, etc.

EDIT: the Google Maps API has undergone some changes since this entry was written. I've attached a new version of the index.html code to reflect those changes. Just replace the original one in the archive with the updated one and things should work again. 

Attachments

Comments

Great!

I have read a lot about Qt which is a framework for applications that need to be deployed cross platform. This platform allows the application to be flexible with almost all platforms and same web content can be made available for all irrespective of the platform they use.

 

Preview comment | PurpleAlienPlanet

Wow! This blog looks exactly like myy old one! It's on a completely different topic but it has pretty much the same layout and
design. Superb choice of colors!

Thanks!

Thanks, but it's basically just a default template with some minor changes.

Dear Mr.Johan Dams, I doing

Dear Mr.Johan Dams,

I doing google map project use qt.

I read your tutorial and I loaded google map use QWebview but in html I can't  call Qt function. 

I tried a  lot of way but it did't work.

Can you explain me why ?

Thank you.

Hi. Are you using Qt5? Things

Hi.

Are you using Qt5? Things changed a bit there... You might want to have a look at this: http://qt-project.org/doc/qt-5/qtwebkitwidgets-index.html. Make sure you use Qt Webkit Widgets.

 

Johan.

Preview comment | PurpleAlienPlanet

Great ste you have got here.. It's difdicult to find quality writing like yours nowadays.
I honestly appreciate individuals like you! Take care!!

Preview comment | PurpleAlienPlanet

Hi, Neat post.

There's an issue with your website in internet explorer, would test this? IE nonetheless is thee market leader and a huge part of other folks will omit your fanfastic writing because of this problem.

Here iis my web-site :: yatesyruc.edublogs.org   - http://yatesyruc.edublogs.org  

Hi. What issue are you seeing

Hi.

What issue are you seeing in IE? What version? I guess IE6 or some old version might have issues, but it should work fine otherwise. 

Johan.

Thanks :)

Thanks :)

Yes , I'm using Qt5. I added

Yes ,

I'm using Qt5.

I added this line in my file .pro:

QT += webkitwidgets

But I only load google map and then I can't call any Qt function in " javascriptobject" class.

I don't know why ?  Can I post my code ?

Thank you  very much !!

Hi. It is not enough to just

Hi.

It is not enough to just add webkitwidgets. Please read this: http://qt-project.org/doc/qt-5/qtwebkitwidgets-index.html. Also, please have a look at these links:

http://qt-project.org/doc/qt-5/qtwebkit-bridge.html
http://qt-project.org/doc/qt-5/qtwebkitexamples-webkitwidgets-imageanalyzer-example.html

You can post code, but I can't promise I will have time to look into it...

 

Johan.

Sorry I'm rebuild my code and

Sorry I'm rebuild my code and it work well. 

If I want to contact you about problem programming. Can I contact you though Email,Skype or your blog ?

Thank you very much for your help !!

Best Regards,

Hai Cao Thanh

Yeah ,  I called Qt slot

Yeah , 

I called Qt slot function in html  file but when Ioad google map inside html file I can't  call Qt function slot.

/* index.html */

<html>

<head>

<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />

<style type="text/css">

html { height: 100% }

body { height: 100%; margin: 0px; padding: 0px }

#map_canvas { height: 100% }

</style>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"> </script>

<script type="text/javascript">

var gMap;

var markersArray = [];

var infoWindow = new google.maps.InfoWindow();

function initialize()

{

var latlng = new google.maps.LatLng(10.779999, 106.690002);

var myOptions = {

zoom: 16,

center: latlng,

mapTypeId: google.maps.MapTypeId.ROADMAP

};

var lat = latlng.lat();

var lng = latlng.lng();

gMap = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

google.maps.event.addListener(gMap, 'click', function(e) {

place_marker(e.latLng,gMap);

myoperations.display();///// I want to call Qt slot function to display message but it doesn't work.

});

}

function place_marker(position, map){

var marker = new google.maps.Marker({

position: position,

map: map,

draggable: true

});

map.panTo(position);

}

</script>

</head>

<body onload="initialize()">

<div id="map_canvas" style="width:100%; height:100%"></div>

</body>

</html>

Thank you so much.

Preview comment | PurpleAlienPlanet

hello there and thank you for your info – I've definitely picked up something new from
right here. I did however expertise some technical issues using this web site, since I experienced to reload the website lots
of times previous to I could get it too load properly.

I had been wondering if your web hosting is OK?
Not that I'm complaining, but slow loading insttances times will sometimes affect your placement in google
and ccan damage your quality score if advertising and
marketing with Adwords. Well I'm adding this RSS to my email and can look out for much more
of your respective interesting content. Make sure you update this again verey soon.

Hi. This site should be

Hi.

This site should be available very well, with no hosting issues. The server this runs on does aggressively block brute force attempts, so it might be that your IP range was temporarily blocked. Where are you located?

In any case, I'm glad the content was useful!

 

Johan.

Preview comment | PurpleAlienPlanet

Hi there, I enjoy reading through your article. I wanted to wtite a little comment tto spport you.

Thanks! :-)

Thanks! :-)