.

Tags:

Here is another proof that Qt is not as ubiqutious as I would like it to be. When I ask around what developers use to develop desktop apps using web technologies (HTML, JavaScript, CSS), the choice is either Adobe AIR or Titanium Desktop. Seems that a lot of people overlook Qt and its built-in WebKit integration.

Rather than ranting, I will start a bunch of blog posts which shows how to bring rich web apps into the desktop. This will be the first now. For branding purposes, we will call it hybrid approach because this literally brings web technologies and native solution in the same plate.

I have talked about this hybrid stuff before at MeeGo Conf 2011 (see the video) and will do it again for Intel Elements 2011. However, for that matter, I think blog posts still serve as a better written reference. It also gives me a chance to clarify some prejudices, confusions, and myths that some people still have about web technologies.

For this post, I’ll show you how to implement something like this screenshot:


It’s a code editor (supporting JavaScript mode) based on CodeMirror, an excellent web-based editing widget written using JavaScript and DOM. There are other similar projects out there, for this example I found out that CodeMirror is the easiest to work with. Note that we will use CodeMirror2, a complete rewrite which is way better and faster than the old version of CodeMirror.

The entire code is available in the usual X2 git repo (or the alternative repo), look under webkit/codemirror (you need Qt 4.6 or later versions). It essentially contains of two major classes, Editor which is the main window representing the application and CodeMirror which is the wrapper around CodeMirror’s JavaScript implementation. The latter is just a subclass of QWebView where we host CodeMirror actual logic.

Because we do not want to run CodeMirror from the file system and we want the editor to work even without Internet connection, we use the technique of storing all the necessary content in the resource system provided by Qt. This is what assets.qrc all about, i.e. as a central location to find the following:

index.html
codemirror.js
codemirror.css
javascript.js
default.css
cobalt.css
elegant.css
neat.css
night.css

Many still mistakenly believe that using web technologies means that the app requires constant network connection. This is not necessarily anymore with the said packaging approach. It is important to note that now we can access any of the files stored inside the resource using the special qrc scheme. This is exactly what the constructor is doing, i.e. loading qrc:/index.html right into the web view:

CodeMirror::CodeMirror(QWidget *parent)
    : QWebView(parent)
{
    load(QUrl("qrc:/index.html"));
    changeTheme("default");
 
    m_external = new External(this);
    page()->mainFrame()->addToJavaScriptWindowObject("External", m_external);
}

The main application class, Editor, has all the important code to setup native menu (we have three main menu items: File, Edit, Theme) and file dialog if the user opens a new file or save the text. Of course, when we save to a file, we need to get the text out of our hosted CodeMirror and write it using QFile. This is where we work with bridging the two different worlds: native and web. There is a whole section about QtWebKit bridge in the Qt documentation.

Looking at index.html, you would find that the CodeMirror’s editor object is created as (surprise!) editor. Thus, in order to get the content we have to call editor.getValue (see CodeMirror manual for the details), hence effectively transferring the data from the web world to the native world:

QString CodeMirror::text() const
{
    return page()->mainFrame()->evaluateJavaScript("editor.getValue()").toString();
}

What about the other way around? We could simply evaluate a JavaScript code that sets the editor’s value, e.g. editor.setValue(content). Rather than passing content as a string (and thus we need to escape it properly), let’s use another nice feature of QtWebKit bridge: built-in integration with QObject. We setup a simple QObject subclass called as External whose purpose is only to hold a string property:

class External: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString data READ data)
 
public:
    External(QObject *parent = 0): QObject(parent) {}
 
    QString text;
    QString data() const { return text; }
};

An instance of this class is inserted into our web page during the construction of CodeMirror class (see the previous code fragment), under the name External. From now on, anytime in the web world we call window.External.data, we would get the same exact value as what we put into the data property of our instance. It is doing what we want to do: transferring the data from the native world to the web world.

This seems to be complicated but check out at how simple the function finally looks like:

void CodeMirror::setText(const QString &text)
{
    m_external->text = text;
    page()->mainFrame()->evaluateJavaScript("editor.setValue(External.data)");
}

Arguably, the special function evaluateJavaScript is really critical to this hybrid approach! In addition to assist the bridging, it is also useful to trigger some action in the web world from the native world:

void CodeMirror::undo()
{
    page()->mainFrame()->evaluateJavaScript("editor.undo()");
}
 
void CodeMirror::redo()
{
    page()->mainFrame()->evaluateJavaScript("editor.redo()");
}

This function is also necessary for the theming support. All the styles needed for the different themes are available from the resource (the last few CSS files in the previous list), but we need to activate it (bonus exercise, why the last line dealing with class name is necessary?):

void CodeMirror::changeTheme(const QString &theme)
{
    QWebFrame *frame = page()->mainFrame();
    frame->evaluateJavaScript(QString("editor.setOption('theme', '%1')").arg(theme));
    frame->evaluateJavaScript(QString("document.body.className = 'cm-s-%1'").arg(theme));
}

Of course, since we use Qt, the same editor example runs beautifully on other platforms (also here showing different color schemes available for CodeMirror):

With a pretty thin wrapper (sloccount gives around 250 lines), we bring CodeMirror to the desktop and build a simple editor around it, with menu, file, editing, and theme support. Of course, credits should go to the brave WebKit team, Qt folks, and CodeMirror developers, whose hard work is leveraged in this demo.

Obviously, being an example, this hybrid editor still lacks a lot of standard features and hence can’t be really used as a killer application. However, if someone wants to pursue the path of productized hybrid app, don’t hesitate to use it as a starting basis.

This short demo highlights just two points. First, we can package the web content using the resource system, thus it does not need to reside as individual files on the disk or even a remote server. Second, transferring the data between the web world and the native world is possible by leveraging the bridge provided by QtWebKit.

In the coming installments, hopefully we can explore more about the bridging mechanism. Of course, feel free to propose a specific topic or concern that you want me to tackle first!

  • No’am Rosenthal

    Cool stuff!

    • Jeff Mitchell

      I think this will be a useful starting point for me someday… 🙂

      Thanks for it.

  • No’am Rosenthal

    Cool stuff!

    • Jeff Mitchell

      I think this will be a useful starting point for me someday… 🙂

      Thanks for it.

  • Cheung

    Hi Ariya. Thanks for the effort.

    However, the webkit2 api in Qt5 will lack such feature, and I don’t see any sign that Nokia will spend much resources on said feature in Qt5. I am really worried about the future of such hybrid application using Qt.

    I did read No’am’s Qt labs post on this topic a few days ago. With all due respect to No’am and his team, it didn’t look too promising to me…

    • ariya

      Personally I am not worried too much about Qt 5. It will be in the flux state for quite some time and nobody knows what kind of hybrid solution will appear there.

      • Ecir

        Hello,

        I would like to try out Qt to build an editor exactly like the one you wrote above. I’m new to Qt – is it possible to use Qt5, now that it is out, for the same task?

  • Cheung

    Hi Ariya. Thanks for the effort.

    However, the webkit2 api in Qt5 will lack such feature, and I don’t see any sign that Nokia will spend much resources on said feature in Qt5. I am really worried about the future of such hybrid application using Qt.

    I did read No’am’s Qt labs post on this topic a few days ago. With all due respect to No’am and his team, it didn’t look too promising to me…

    • ariya

      Personally I am not worried too much about Qt 5. It will be in the flux state for quite some time and nobody knows what kind of hybrid solution will appear there.

  • Great stuff.

    Now I’m waiting for the other articles :p We’ve just started to build a multi-platform desktop app for a client, based on web technologies and Qt. But we are facing some problems:
    -is it possible to build a standalone .exe on Windows (including *everything* inside? our html, css, javascript, Qt and QtWebkit). We can’t find how. There is always something missing to install separately to launch our .exe.
    -Is it possible to include custom font-face? On Mac and Windows (Linux not tested) our app doesn’t recognize .woff and .ttf fonts included trough font-face…

    Thanks!

    • ariya

      Stand alone .exe is easy, check out my project PhantomJS for that. On Windows, it has a static version that contains everything. From the licensing point, you have to be careful with it because WebKit is LGPL and certain restrictions apply. Consult your attorney for details.

      As for the font problem, there was a bug entry about that. I don’t have it handy. Beside, you should engage the QtWebKit team as I can’t afford to make my blog space as a bug tracking system 🙂

  • Great stuff.

    Now I’m waiting for the other articles :p We’ve just started to build a multi-platform desktop app for a client, based on web technologies and Qt. But we are facing some problems:
    -is it possible to build a standalone .exe on Windows (including *everything* inside? our html, css, javascript, Qt and QtWebkit). We can’t find how. There is always something missing to install separately to launch our .exe.
    -Is it possible to include custom font-face? On Mac and Windows (Linux not tested) our app doesn’t recognize .woff and .ttf fonts included trough font-face…

    Thanks!

    • ariya

      Stand alone .exe is easy, check out my project PhantomJS for that. On Windows, it has a static version that contains everything. From the licensing point, you have to be careful with it because WebKit is LGPL and certain restrictions apply. Consult your attorney for details.

      As for the font problem, there was a bug entry about that. I don’t have it handy. Beside, you should engage the QtWebKit team as I can’t afford to make my blog space as a bug tracking system 🙂

  • Noli

    I have been trying to create Hybrid app (i.e. Map app with OpenLayers) for iOS using jQtouch + Quickconnect + Xcode in iPhone. I use Quickconnect (Objetive-C and JS) for Sqlite3 / spatialite (GIS) database. It works well.

    I like to do the same with Meego N9 using Qt and Sqlite/Spatialite as database with OpenLayers (js mapping).

    I hope to see some example / demo how to use Qt and Javascript with Sqlite3 database (not HTML5 web database). Do you have any example that use Qt, Javascript and Sqlite3?

    You presented this (below), where can get a copy / download a demo for Meego Qt and javascript i.e. Hybrid App for Meego?

    http://sf2011.meego.com/program/sessions/hybrid-apps-native-web-using-webkit

    I guess you have an example for Hybrid for Meego (Qt and Javascript) you presented in this conference.

    Thanks.

    • ariya

      I did not delve into specific examples in that presentation. However, watch this series as I cover more about the native-web bridging stuff in the near future.

      • Noli

        I look at your presentation paper (i.e. the pdf file) after I posted my comment. I realized your don’t have real Hybrid Meego apps.

        Probably you can do a Sencha Touch + MeeGo (Qt) + Sqlite Hybrid app demo.

        P.S.

        BTW, for people looking for example for Hybrid apps in Mac OS X Desktop and iOS, QuickConnect has plenty of working demo including Sqlite3 as native database.

      • Noli

        I am not trolling here but just like to inform how advance QuickConnect in Hybrid app in iOS and Android. The older version of QuickConnect works in Mac OS X desktop as well. I hope Qt will do the same especially in MeeGo.

        See this new release of Hybrid QuickConnect framework 2.1 – 10 September

        http://tetontech.wordpress.com/

        http://www.quickconnectfamily.org/QCJSLibAPI/index.html

        All the examples of Hybrid QC have been upgraded as well to this release.

        Ariya, I am looking forward for MeeGO, Sqlite3 and Qt demo, similar to QuickConnect iOS NativeDB, Map, Email Example demo. The map and email example is relevant but just NativeDB.

  • Noli

    I have been trying to create Hybrid app (i.e. Map app with OpenLayers) for iOS using jQtouch + Quickconnect + Xcode in iPhone. I use Quickconnect (Objetive-C and JS) for Sqlite3 / spatialite (GIS) database. It works well.

    I like to do the same with Meego N9 using Qt and Sqlite/Spatialite as database with OpenLayers (js mapping).

    I hope to see some example / demo how to use Qt and Javascript with Sqlite3 database (not HTML5 web database). Do you have any example that use Qt, Javascript and Sqlite3?

    You presented this (below), where can get a copy / download a demo for Meego Qt and javascript i.e. Hybrid App for Meego?

    http://sf2011.meego.com/program/sessions/hybrid-apps-native-web-using-webkit

    I guess you have an example for Hybrid for Meego (Qt and Javascript) you presented in this conference.

    Thanks.

    • ariya

      I did not delve into specific examples in that presentation. However, watch this series as I cover more about the native-web bridging stuff in the near future.

      • Noli

        I look at your presentation paper (i.e. the pdf file) after I posted my comment. I realized your don’t have real Hybrid Meego apps.

        Probably you can do a Sencha Touch + MeeGo (Qt) + Sqlite Hybrid app demo.

        P.S.

        BTW, for people looking for example for Hybrid apps in Mac OS X Desktop and iOS, QuickConnect has plenty of working demo including Sqlite3 as native database.

      • Noli

        I am not trolling here but just like to inform how advance QuickConnect in Hybrid app in iOS and Android. The older version of QuickConnect works in Mac OS X desktop as well. I hope Qt will do the same especially in MeeGo.

        See this new release of Hybrid QuickConnect framework 2.1 – 10 September

        http://tetontech.wordpress.com/

        http://www.quickconnectfamily.org/QCJSLibAPI/index.html

        All the examples of Hybrid QC have been upgraded as well to this release.

        Ariya, I am looking forward for MeeGO, Sqlite3 and Qt demo, similar to QuickConnect iOS NativeDB, Map, Email Example demo. The map and email example is relevant but just NativeDB.

  • MySchizoBuddy

    DailyJs does this series called “let’s make a framework”. An excellent detailed series. http://dailyjs.com/tags.html#lmaf

    You should do something similar, start from scratch and build a proper hybrid application.

    I have some confusion regarding protecting my code. Can hybrid apps build on QT protect my js code from being exposed.

    • ariya

      What is the anticipated way to expose your code?

      • MySchizoBuddy

        If i create a hybrid app as oppose to a web app. In a web app my frontend js code is exposed, and is viewable to anyone. In a hybrid app my js code is sandboxed inside QTWebkit correct. Is my js code blocked from being viewed?

        • ariya

          That’s exactly my question. When you build your hybrid app, is there a chance for the user to view your code? If yes, how and why?

          Note that this is different for each case.

          BTW, read also http://ariya.ofilabs.com/2009/01/qt-not-qt.html.

        • ariya

          As a more concrete example, in the CodeMirror example above, all the code is packaged in the resource system and delivered with the executables. If you want to avoid someone to view the executable in a hex editor and thereby being able to extract the code, then just apply the usual anti-tampering techniques (encryption etc).

          Nothing is specific to the fact that it’s the application code running inside WebKit. You would apply the same technique to hide any sensitive data in the application.

  • MySchizoBuddy

    DailyJs does this series called “let’s make a framework”. An excellent detailed series. http://dailyjs.com/tags.html#lmaf

    You should do something similar, start from scratch and build a proper hybrid application.

    I have some confusion regarding protecting my code. Can hybrid apps build on QT protect my js code from being exposed.

    • ariya

      What is the anticipated way to expose your code?

      • MySchizoBuddy

        If i create a hybrid app as oppose to a web app. In a web app my frontend js code is exposed, and is viewable to anyone. In a hybrid app my js code is sandboxed inside QTWebkit correct. Is my js code blocked from being viewed?

        • ariya

          That’s exactly my question. When you build your hybrid app, is there a chance for the user to view your code? If yes, how and why?

          Note that this is different for each case.

          BTW, read also http://ariya.ofilabs.com/2009/01/qt-not-qt.html.

        • ariya

          As a more concrete example, in the CodeMirror example above, all the code is packaged in the resource system and delivered with the executables. If you want to avoid someone to view the executable in a hex editor and thereby being able to extract the code, then just apply the usual anti-tampering techniques (encryption etc).

          Nothing is specific to the fact that it’s the application code running inside WebKit. You would apply the same technique to hide any sensitive data in the application.

  • Петър Петров

    Why GPL? Anyway, it’s already thing I now how to make by myself.

  • Pingback: 開発などブログ » QtでMacOSXアプリケーションをつくる()