Viadeo Twitter Google Bookmarks ! Facebook Digg del.icio.us MySpace Yahoo MyWeb Blinklist Netvouz Reddit Simpy StumbleUpon Bookmarks Windows Live Favorites 
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Toutes les fonctions  ·  Vues d'ensemble  · 

The Webkit Bridge Tutorial - Hybrid Client Application

Files:

In this example, we will show how to write a hybrid application using QtWebKit Bridge, which distinguishes itself from a thin client in that it performs heavy calculations on the client side in C++, like a native application, but presents nothing more than a QWebView for its user interface, displaying web content written in HTML/JavaScript. The application uses QtConcurrent to distribute its work across as many CPU cores as are available from the system, so it can process each image in parallel.

For the full reference documentation of QtWebKit hybrid development, see The QtWebKit Bridge.

Initially, you will see a user interface with an empty list of images. Clicking on some of the images in the lower pane below adds them to the list view above, as shown in the screenshot below.

Now, we can click on Analyze, and each image is analyzed using some computationally intensive C++ function, in parallel and on different cores. Progress is shown while the analysis is proceeding.

and in the end, we will see something like this, where the average RGB values of each image are shown.

The MainWindow is defined in C++, and creates a QNetworkDiskCache and a QWebView, and tells the QWebView to load the starting page, providing us with a user interface for the client.

 MainWin::MainWin(QWidget * parent) : QWebView(parent)
 {
     m_network = new QNetworkAccessManager(this);
     m_cache = new QNetworkDiskCache(this);
     m_cache->setCacheDirectory(QDesktopServices::storageLocation(QDesktopServices::CacheLocation) + "/imageanalyzer");
     m_cache->setMaximumCacheSize(1000000); //set the cache to 10megs
     m_network->setCache(m_cache);
     page()->setNetworkAccessManager(m_network);

     m_analyzer = new ImageAnalyzer(m_cache, this);

     // Signal is emitted before frame loads any web content:
     QObject::connect(page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
                      this, SLOT(addJSObject()));

     // qrc:// URLs refer to resources. See imagenalayzer.qrc
     QUrl startURL = QUrl("qrc:/index.html");

     // Load web content now!
     setUrl(startURL);
 }

In this example, the sample content is addressed with the qrc:/index.html URL. qrc:/ indicates that the file is stored as a Qt resource (attached to the executable). In a real-world application, the content and images would likely be retrieved from the network rather than from resources.

We wish to initialize an object reference in the JavaScript web page to point to our ImageAnalyzer before any other scripts are run. To do this, we connect the javaScriptWindowObjectCleared() signal to a slot which does the object creation and handoff to JavaScript.

 void MainWin::addJSObject() {
     // Add pAnalyzer to JavaScript Frame as member "imageAnalyzer".
     page()->mainFrame()->addToJavaScriptWindowObject(QString("imageAnalyzer"), m_analyzer);
 }

The ImageAnalyzer object is created and added to a JavaScript object on the web page's mainFrame with addToJavaScriptWindowObject().

The start page is resources/index.html. In one of its <div> regions, we have images, each with an onClick() handler that calls addImage().

 <div id=imagediv style="clear:both; text-align:center">
 <hr/>
     <img src="images/mtRainier.jpg" height=150px onclick='return addImage(this);' />
     <img src="images/bellaCoola.jpg" height=150px onclick='return addImage(this);'/>
     <img src="images/seaShell.jpg" height=150px onclick='return addImage(this);'/>

Clicking an image adds it to an images list.

 function addImage(newimg) {
     var imglist = document.getElementById('imglist');
     var curChildren = imglist.childNodes;
     var newline = document.createElement('option');
     newline.innerHTML = newimg.src.substring(newimg.src.lastIndexOf('/')+1);
     newline.value = newimg.src;
     imglist.appendChild(newline);
 }

The Analyze button at the bottom of the image list is clicked when we want to start the analysis:

     <div style="float:right; width:50%; border-left: solid 1px black">
         <div id="listdiv" align="center">
             <h5>Images to be analyzed:</h5>
             <select multiple size=10 id=imglist style="width:80%"></select>
             <br />
             <input type="button" id="evalbutton" value="Analyze" onclick="analyzeImages()" />
         </div>
     </div>

When the user clicks the Analyze button, analyzeImages() is called, another regular JavaScript method, shown below. Notice it assumes the imageAnalyzer object is already defined and initialized in JavaScript space, but we guaranteed that by connecting our setup slot to the appropriate signal, javaScriptWindowObjectCleared().

 function analyzeImages() {
     connectSlots();
     var imglist = document.getElementsByTagName('option');
     if (imglist.length > 0) {
         stringlist = [];
         for(var i=0; i<imglist.length; i++) {
             stringlist[i]=imglist[i].value;
         }
         if (!imageAnalyzer.busy) {
             remaining = stringlist.length;
             imageAnalyzer.startAnalysis(stringlist);
         } else {
             alert("Processing, please wait until finished.");
         }

The only methods on ImageAnalyzer that we can or do call from JavaScript are those which are exposed through {The Meta-Object System}{Qt's MetaObject} system: property getter/setter methods, public signals and slots, and other Q_INVOKABLE functions.

 class ImageAnalyzer : public QObject
 {
     Q_OBJECT
 public:
     ImageAnalyzer(QNetworkDiskCache * netcache, QObject * parent=0);

     QRgb lastResults();
     float lastRed();
     float lastGreen();
     float lastBlue();
     bool isBusy();
     Q_PROPERTY(bool busy READ isBusy);
     Q_PROPERTY(float red READ lastRed);
     Q_PROPERTY(float green READ lastGreen);
     Q_PROPERTY(float blue READ lastBlue);
     ~ImageAnalyzer();

 public slots:
     /*! initiates analysis of all the urls in the list */
     void startAnalysis(const QStringList & urls);

 signals:
     void finishedAnalysis();
     void updateProgress(int completed, int total);
     ...
 private:
 QNetworkAccessManager* m_network;
 QNetworkDiskCache* m_cache;
 QStringList m_URLQueue;
 QList<QImage> m_imageQueue;
 int m_outstandingFetches;
 QFutureWatcher<QRgb> * m_watcher;

Most of the members are set up in the constructor:

 ImageAnalyzer::ImageAnalyzer(QNetworkDiskCache* netcache, QObject* parent)
     : QObject(parent), m_cache(netcache), m_outstandingFetches(0)
 {
     /*  ImageAnalyzer only wants to receive http responses
         for requests that it makes, so that's why it has its own
         QNetworkAccessManager. */
     m_network = new QNetworkAccessManager(this);
     m_watcher = new QFutureWatcher<QRgb>(this);
     /*  We want to share a cache with the web browser,
         in case it has some images we want: */
     m_network->setCache(m_cache);

     QObject::connect(m_network, SIGNAL(finished(QNetworkReply*)),
                      this, SLOT(handleReply(QNetworkReply*)));
     QObject::connect(m_watcher, SIGNAL(finished()),
                      this, SLOT(doneProcessing()));
     QObject::connect(m_watcher, SIGNAL(progressValueChanged(int)),
                      this, SLOT(progressStatus(int)));
 }

Back on the JavaScript side, we want to connect signals from this object to JavaScript functions on our web page, after the web page is loaded, but before the images are analyzed.

From connectSlots(), we can see how to connect signals from the imageAnalyzer object to regular JavaScript functions, which can also behave like slots. We use this to monitor and display progress from the C++ side.

 function connectSlots()
 {
     if ( !connected ) {
         connected = true;
         imageAnalyzer.finishedAnalysis.connect(this, finished);
         imageAnalyzer.updateProgress.connect(this, updateProg);
     }
 }

The only public slot is startAnalysis(), called to place a list of URLs into the image analyzer's QtConcurrent processing queue from JavaScript space.

 void ImageAnalyzer::startAnalysis(const QStringList & urls)
 {
     m_URLQueue = urls;
     fetchURLs();
 }

The images need to be loaded again now, which is why fetchURLs first checks the cache to see if we can save an extra network get.

 void ImageAnalyzer::fetchURLs()
 {
     while (!m_URLQueue.isEmpty())
     {
         QString url = m_URLQueue.takeFirst();
         QUrl URL = QUrl(url);
         QIODevice * pData = m_cache->data(URL);
         // Is image already loaded in cache?
         if (pData == 0) {
             // HTTP Get image over network.
             m_outstandingFetches++;
             QNetworkRequest request = QNetworkRequest(URL);
             request.setRawHeader("User-Agent", "Nokia - Custom QT app");
             m_network->get(request);
         } else {
             // Get image from cache
             QImage image;
             image.load(pData, 0);
             if (!image.isNull())
                 queueImage(image);
             delete(pData);
         }
     }
 }

For the images that were not in the cache, handleReply() will load them into a QImage when the data is ready.

 void ImageAnalyzer::handleReply(QNetworkReply * pReply)
 {
     m_outstandingFetches--;
     if (pReply->error()) {
         qDebug() << "Error code" << pReply->error();
         qDebug() << "Http code" << pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
         return;
     }
     QImage image;
     image.load(pReply, 0);
     pReply->deleteLater();
     if (image.isNull()) {
         qDebug() << "bad image";
         qDebug() << pReply->rawHeaderList();
         foreach(QByteArray element, pReply->rawHeaderList()) {
             qDebug() << element << " = " << pReply->rawHeader(element);
         }
         return;
     }
     queueImage(image);
 }

After the images are loaded, they are queued up in preparation to be sent in a batch for analysis to a QFutureWatcher, which will distribute the processing across multiple threads and cores, depending on how many are available.

 void ImageAnalyzer::queueImage(QImage img)
 {
     if (!img.isNull())
         m_imageQueue << img;

     if (m_outstandingFetches == 0 && m_URLQueue.isEmpty()) {
         m_watcher->setFuture(QtConcurrent::mapped(m_imageQueue, averageRGB));
     }
 }

The function that gets performed on each image is averageRGB(), as specified in argument 2 to the QtConcurrent::mapped() function. Notice it repeats the same calculations 100 times on each pixel to keep the CPU very busy. This is done only for the purposes of the demo so that the analysis takes a noticeable time to complete.

 QRgb averageRGB(const QImage &img)
 {
     int pixelCount = img.width() * img.height();
     int rAvg, gAvg, bAvg;

     // We waste some time here:
     for (int timeWaster=0; timeWaster < 100; timeWaster++) {
         quint64 rTot = 0;
         quint64 gTot = 0;
         quint64 bTot = 0;
         for (int i=0; i < img.width(); i++) {
             for (int j=0; j < img.height(); j++) {
                 QRgb pixel = img.pixel(i,j);
                 rTot += qRed(pixel);
                 gTot += qGreen(pixel);
                 bTot += qBlue(pixel);
             }
         }
         rAvg = (rTot)/(pixelCount);
         gAvg = (gTot)/(pixelCount);
         bAvg = (bTot)/(pixelCount);
     }
     return qRgb(rAvg, gAvg, bAvg);
 }
Publicité

Best Of

Actualités les plus lues

Semaine
Mois
Année
  1. Microsoft ouvre aux autres compilateurs C++ AMP, la spécification pour la conception d'applications parallèles C++ utilisant le GPU 22
  2. Les développeurs ignorent-ils trop les failles découvertes dans leur code ? Prenez-vous en compte les remarques des autres ? 17
  3. RIM : « 13 % des développeurs ont gagné plus de 100 000 $ sur l'AppWord », Qt et open-source au menu du BlackBerry DevCon Europe 0
  4. « Quelque chose ne va vraiment pas avec les développeurs "modernes" », un développeur à "l'ancienne" critique la multiplication des bibliothèques 12
  5. BlackBerry 10 : premières images du prochain OS de RIM qui devrait intégrer des widgets et des tuiles inspirées de Windows Phone 0
  6. Adieu qmake, bienvenue qbs : Qt Building Suite, un outil déclaratif et extensible pour la compilation de projets Qt 17
  7. Quelles nouveautés de C++11 Visual C++ doit-il rapidement intégrer ? Donnez-nous votre avis 10
Page suivante

Le Qt Developer Network au hasard

Logo

Applications mobiles modernes avec Qt et QML

Le Qt Developer Network est un réseau de développeurs Qt anglophone, où ils peuvent partager leur expérience sur le framework. Lire l'article.

Communauté

Ressources

Liens utiles

Contact

  • Vous souhaitez rejoindre la rédaction ou proposer un tutoriel, une traduction, une question... ? Postez dans le forum Contribuez ou contactez-nous par MP ou par email (voir en bas de page).

Qt dans le magazine

Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt 4.7-snapshot
Copyright © 2012 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
 
 
 
 
Partenaires

Hébergement Web