by Jeff Hanson

Java desktop development with Qt Jambi

news
Aug 9, 200719 mins

A sophisticated alternative to GUI development with Swing and Java 2D

Qt Jambi is a new and noteworthy alternative to Swing and Java 2D for developing rich, cross-platform desktop-application interfaces. In this article JavaWorld contributor Jeff Hanson introduces the recently released Qt Jambi, explains how it works under the hood, and presents programming examples that will familiarize you with the framework’s drag-and-drop API and integrated development tools.

Qt Jambi, released in early June 2007, is a development framework that integrates the Java programming language with Trolltech’s popular C++ GUI development framework, Qt. Released under a dual open-source/commercial license, Qt Jambi enables Java developers using Java Standard Edition 5.0 (JSE 5.0) and later to leverage the features of the Qt framework. Along with industry-tested cross-platform support, Qt Jambi provides support for GUI development basics such as 2D and 3D graphics, drag-and-drop interobject event messaging, internationalization, SQL database access, XML and TCP/IP network protocols. A Qt Jambi Eclipse plug-in also gives you the option to access the features of Qt Jambi using Eclipse.

Download Qt Jambi
Qt Jambi is offered under a dual open source/commercial license. You can download either version from the Trolltech homepage. The open-source version is provided under the GNU General Public License and comes without support or warranty. Commercial projects are required to use the commercial version of Qt Jambi.

Qt Jambi builds on Qt’s cross-platform C++ GUI foundation to ease the task of developing native look-and-feel user interfaces for most desktop platforms using Java code. As such, it is an interesting alternative to Swing and Java 2D for Java desktop development. In this hands-on introduction to Qt Jambi you will learn about Qt Jambi’s development API and integrated development tools. You will be introduced to Qt Jambi’s widget-based GUI framework and learn how to create a project using the Qt Designer. You will also compare Qt Jambi’s signals-and-slots event handling mechanism with the Java event listener framework, get a first look at Qt Jambi’s graphics rendering system, and see for yourself how it integrates with Eclipse via the Qt Jambi Eclipse plug-in.

Download Qt Jambi now if you would like to follow along with the examples.

Overview of Qt Jambi

Qt Jambi is a Java-based framework that integrates Trolltech’s cross-platform C++ framework for GUI development with the Java programming language. The following features are available when developing Java desktop applications with Qt Jambi:

  • Excellent cross-platform support for 2D and 3D graphics
  • A unique interobject event communication mechanism
  • Internationalization
  • SQL database access
  • Support for XML
  • Support for TCP/IP network protocols

You can use Qt Jambi with most JVMs (Java virtual machines) subsequent to Java Standard Edition (JSE) 5.0 and Java Enterprise Edition (JEE) 5.0. Qt Jambi’s tight OS integration enables you to produce Java desktop applications that mimic the native OS look and feel.

Qt meets the Java programming language

The Qt Jambi API provides a thin layer of Java code that interacts with Trolltech’s natively compiled C++ libraries. This layer of Java code runs in any Java Runtime Environment (JRE) and uses the Java Native Interface (JNI) framework to communicate with the native C++ libraries.

Essentially, Qt Jambi places a Java abstraction layer over Qt’s C++ libraries. To address the similarity and overlap between Qt’s C++ classes and Java’s core classes, Qt Jambi maps the overlapping C++ classes to corresponding Java classes. As a result, you may use standard Java constructs and operators with Qt’s native abstractions. You also can intermingle Qt Jambi APIs with non-Qt Java APIs.

Table 1 shows the Qt classes that are mapped to equivalent Java core classes.

Table 1. Qt classes mapped to equivalent Java classes

Qt classJava class(es)
QCharchar and java.lang.Character
QHashjava.util.HashMap
QListjava.util.List
QMapjava.util.SortedMap
QStringjava.lang.String
QThreadjava.lang.Thread
QVectorjava.util.List

The Qt C++ API depends on an abstract value type QVariant to encapsulate many Qt value types. Because the Java language already provides a common abstract type for all classes (the common Object base class), the Qt Jambi API relies on the Object class wherever Qt C++ relies on QVariant. Qt Jambi maps C++ enums to Java enums to enforce type safety for Qt-Java enums and bit flags.

Qt Jambi includes a widget-based GUI framework that takes advantage of platform-specific extensions such as ActiveX, OpenGL, and ClearType. This framework provides native look-and-feel capabilities for Windows, Mac OS X and Linux themes.

Widgets in Qt Jambi follow a style-sheet syntax similar to HTML Cascading Style Sheets (CSS), so you can perform minor or widespread customizations by tweaking CSS-style tags and properties. You can also subclass Qt’s widgets for even more look-and-feel customization and control.

Qt Jambi’s GUI framework includes layout management classes that are presented in the Qt Designer tool to simplify layout when building desktop components and applications. Figure 1 shows the Layouts window in the Qt Designer tool.

Qt Jambi’s GUI framework provides APIs for building applications with dockable components. Among other things, you can use this feature to create desktop applications with native look-and-feel docking toolbars.

Event-driven programming with signals and slots

Qt Jambi and Swing
Qt Jambi’s rendering system operates alongside Swing/AWT, so it is possible to use the two frameworks in tandem. Qt Jambi’s signals-and-slots mechanism (discussed below) presents a new paradigm for Swing/AWT developers accustomed to Swing’s event-listener framework, however. As a result you may encounter subtle hitches when integrating Swing/AWT with Qt Jambi, in particular with regard to focus handling, tabbing, and window activation and deactivation.

Qt Jambi simplifies interobject event-driven communication using a signals-and-slots mechanism. Unlike the Java platform event-listener framework, which requires that you implement several classes and interfaces to send and receive each specific category of events, Qt Jambi’s signal-to-slot mechanism connects signal-emitting widgets with slot-enabled objects, much reducing the code overhead usually associated with event processing.

Within Qt Jambi’s signals-and-slots framework, signal-emitting widgets emit signals whenever a given event happens, such as when a button is clicked. Signal-emitting widgets can be connected to one or more slot-enabled objects, which will be notified when a signal is emitted. Let’s compare the mechanisms in Swing to those in Qt Jambi for connecting a button click with an action that displays a dialog box.

Event handling in Swing versus Qt Jambi

With Swing, connecting a button click with an action that displays a dialog box requires something like what you see in Listing 1.

Listing 1. Event handling in Swing

 button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e)
    {
      showDialog();  // code to execute when button is pressed
    }
});

With Qt Jambi, connecting the button click and the showDialog action requires only one line of code, as shown in Listing 2.

Listing 2. The same event handled in Qt Jambi

 button.clicked.connect(this, "showDialog()");

Custom widgets and UI components

Qt Jambi widgets provide signals for most conceptual events that occur within UI components. You can also create custom widgets or components to define and emit signals for events that make the most semantic sense for the actions performed by the widget or component.

To illustrate, consider a component designed to track stock prices. Whenever the price of the stock changes, the component should emit a priceChanged() signal. In Qt Jambi, you could declare a price-changed Signal object for the stock-tracking component as shown in Listing 3.

Listing 3. Declaring a Signal for a stock-tracking component

 public Signal1<Integer> priceChanged;

Signals are defined in Qt Jambi using generics to ensure type safety. Notice the number 1 appended to the signal declaration. This indicates that the signal has one argument, in this case the new price.

In the setPrice() method of the stock-tracking component, the priceChanged() signal is emitted using the emit() method of the Signal object, as shown in Listing 4.

Listing 4. The emit() method of the Signal object

 void setPrice(int newPrice) {
    if (price != newPrice)
    {
      price = newPrice;
      priceChanged.emit(price);
    }
}

Connecting slots to signals

Qt Jambi provides a mechanism to connect to slots by name. You can also use this mechanism to connect to a component’s signals by following the naming convention of “on_widgetName_signal“. For example, to connect to the clicked() signal of a button widget named testButton, you would simply implement the slot as shown in Listing 5.

Listing 5. Connecting to testButton’s clicked() signal

 public void on_testButton_clicked() { ... }

Once the slot is defined, the slot-defining class must call the QtJavaUtils.connectSlotsByName() method, which connects testButton‘s clicked() signal to the on_testButton_clicked() slot automatically. Thereafter, the slot will be called whenever testButton is clicked.

Resource management in Qt Jambi

Qt Jambi provides a unified URI syntax for specifying the path to any given resource residing on the Java classpath. This syntax, which simplifies resource management on Qt Jambi uses a prefix of “classpath:” to refer to a resource residing in a filesystem or in a jar file using the same relative path. For example, Listing 6 is a reference to a QPixmap object.

Listing 6. A reference to a QPixmap object

 QPixmap myImage = new QPixmap("classpath:images/myimage.jpg");

In this example, myimage.jpg can reside in a filesystem directory or JAR as long as either one is specified in the VM’s classpath. You can utilize the resource-path feature in Qt Jambi APIs wherever a file path is used.

UI building with the Qt graphics system

Qt Jambi and Linux
Trolltech currently offers a Qt Jambi Open Source Edition (32-bit and 64-bit) for Linux, as well as the Qt Jambi Eclipse Integration package. Both Swing and Qt Jambi support GTK — a C-based, GUI toolkit for creating a GNOME-based GUI applications — making Qt Jambi an appealing option for Linux desktop development. See the Resources section to learn more about using Qt Jambi for Linux desktop development.

Qt Jambi’s graphics system is based on Qt’s C++ rendering system, taking full advantage of its native text-handling and font support. As a result, you can access native OS platform such features as ClearType antialiasing on Windows and Mac OS X systems, and subpixel antialiasing on X11.

The Qt graphics system uses the same API to render a UI to the screen or printer. This system simplifies rendering by providing common abstractions on paintable components. With Qt Jambi, the tasks involved in preparing components to be printed are greatly simplified. For example, to print a rectangle using the standard Java APIs, you would have to provide one block/method to prepare and display the print dialog and another method conforming to the java.awt.print.Printable interface to do the actual printing, as shown in Listing 7.

Listing 7. Printing a rectangle using Swing/AWT

 void doPrinting() {
    PrinterJob printJob = PrinterJob.getPrinterJob();
    printJob.setPrintable(this); // "this" must implement the Printable interface
    if (printJob.printDialog())
    {
      try
      {
        printJob.print();
      }
      catch (Exception ex)
      {
        ex.printStackTrace();
      }
    }
}

public int print(Graphics graphics, PageFormat pageFormat, int
pageIndex)
    throws PrinterException
{
    if (pageIndex != 0)
    {
      return Printable.NO_SUCH_PAGE;
    }

    Graphics2D graphics2D = (Graphics2D)graphics;
    Rectangle2D.Double rectangle = new Rectangle2D.Double();
    rectangle.setRect(pageFormat.getImageableX() + 1,
                      pageFormat.getImageableY() + 1,
                      144,
                      144);
    graphics2D.draw (rectangle);

    return Printable.PAGE_EXISTS;
}

Using Qt Jambi, you can accomplish the prep dialog and printing at once, using the QPrinter, QPainter, and QPrintDialog classes as shown in Listing 8.

Listing 8. Graphics rendering in Qt Jambi

 void doPrinting () {
    QPrinter printer = new QPrinter();
    QPainter painter = new QPainter();
    QPrintDialog printDialog = new QPrintDialog(printer, this);
    if (printDialog.exec())
    {
      painter.begin(printer);

      Graphics2D graphics2D = (Graphics2D) graphics;
      Rectangle2D.Double rectangle = new Rectangle2D.Double();
      rectangle.setRect(pageFormat.getImageableX() + 1,
                        pageFormat.getImageableY() + 1,
                        144,
                        144);
      graphics2D.draw (rectangle);

      painter.end();
    }
}

Qt Jambi’s graphics system rendering operates in unison with Java 2D rendering. One way to leverage this would be to use Java 2D for advanced imaging and Qt Jambi’s graphics system for widget rendering.

Drag-and-drop development with Qt Designer

Qt Jambi provides a graphical designer application, Qt Designer, for creating Java applications using a drag-and-drop UI. Qt Designer supports Qt Jambi’s signal-and-slot event framework with a nice click-and-drag mechanism for connecting signals and slots.

Figure 2 is a screenshot of the Qt Designer main window.

Figure 3 is a screenshot of the Qt Designer main window with a tree view inside of a tabbed form.

Note that Qt Designer stores UI projects as .jui files. JUI is an XML-based file format that can be converted to Java source code by Qt Jambi’s Java User Interface Compiler (JUIC) command-line utility.

Converting a Qt project file to a Java source file

The Java User Interface Compiler (JUIC) command-line utility converts Qt Designer project files (.jui) to Java source files. Listing 9 shows the call to access the JUIC utility.

Listing 9. The syntax for using the JUIC utility

 C:qtjambiinstalldirbinjuic.exe [options] <.jui file>

Listing 10 shows an example of the JUIC utility as it would be used to produce the Java source code for a Qt project file named MainProject.jui.

Listing 10. The JUIC utility being used to produce Java source code for MainProject.jui

 C:QtJambiProjects>C:qtjambiinstalldirbinjuic.exe -d ./output MainProject.jui

After executing the preceding JUIC command, the displayed output should be as shown in Listing 11.

Listing 11. Output of MainProject.jui

 updated: Ui_MainWindow

In Listing 10, the output from the preceding JUIC command was placed in a directory named “output” one level beneath the directory from which the JUIC command was executed. In the output directory you should find a file named Ui_MainWindow.java. To enable this file to be executed, you must add a main method as shown below in Listing 12.

Listing 12. Adding a main method

 public static void main(String args[]) {
    QApplication.initialize(args);

    QMainWindow mainWindow = new QMainWindow();
    Ui_MainWindow mainUIWindow = new Ui_MainWindow();
    mainUIWindow.setupUi(mainWindow);
    mainWindow.show();

    QApplication.exec();
}

Now you can compile the Ui_MainWindow.java file and execute it. You must include the Qt Jambi JAR file in the classpath when you run the compile, as shown in Listing 13.

Listing 13. Compile and execute the Java file

 C:QtJambiProjectsoutput>C:JavaVMsjdk1.5.0_11binjavac
-classpath .;C:qtjambiinstalldirqtjambi.jar Ui_MainWindow.java

The compiled Ui_MainWindow.class file can now be executed. Again, you must include the Qt Jambi jar file in the classpath when you execute the class, as shown in Listing 14.

Listing 14. Executing UI_MainWindow

 C:QtJambiProjectsoutput>C:JavaVMsjdk1.5.0_11binjava -classpath .;C:qtjambiinstalldirqtjambi.jar Ui_MainWindow

The drag-and-drop mechanism makes Qt Designer a very convenient tool for building user interfaces. This convenience is expanded upon by Qt Designer to provide a very powerful drag-and-connect environment for connecting event producers (signals) with event receivers (slots).

Connecting signals and slots with Qt Designer

Qt Designer’s signals-and-slots editing mode includes a drag-and-connect mechanism that allows you to connect widgets using Qt’s signals and slots. Layout objects can also be connected using this drag-and-connect approach.

To connect widgets in Qt Designer, switch to the signals-and-slots editing mode by opening the Edit menu and selecting the Edit Signals/Slots menu item. Once in this mode you can connect widgets and layout objects by clicking a signal-emitting widget and connecting it with a slot-enabled widget. To see this for yourself, try dragging your cursor over a signal-emitting widget such as a Push Button.

You will notice that the widget becomes highlighted. To complete the connection, press the left mouse button and drag the mouse toward a slot-enabled widget, such as a Check Box. As you drag the mouse, a line will extend from the signal-emitting object to the cursor. When you are over the slot-enabled widget, release the left mouse button. A “Configure Connection” dialog will appear allowing you to choose the exact signal and slot to connect. Choose the desired signal and slot and click the OK button. The widgets are now connected.

Figure 4 illustrates a signal-emitting QPushButton object connected to a QTextEdit object that can be connected as a slot via its paste method.

You can modify signal and slot connections after you have created them using Qt Designer’s signal and slot editor. The signal and slot editor allows you to add, modify and delete connections.

Integrating Qt Jambi with Eclipse

Qt Jambi provides a plug-in for the Eclipse platform (3.2 or higher) that enables you to build Qt Jambi applications and to design and edit GUI forms using the Qt Designer from within the Eclipse IDE. The Qt Jambi Eclipse plug-in also provides access to Qt Jambi documentation within the Eclipse IDE.

To install the Qt Jambi Eclipse plug-in, you must download the Qt Jambi Eclipse Integration package. This is provided as a .zip file and as a .tar.gz file, depending upon your development platform. The Qt Jambi Eclipse Integration package contains the Eclipse plug-ins and the native libraries needed to run Qt Designer. Expand the downloaded package to the root folder of your Eclipse installation. If you are installing to a Windows environment, you must now run the register_eclipse_integration.bat file to complete the installation.

You will have to specify the location of your Qt Jambi installation before using the Qt Jambi Eclipse plug-in. To specify the location, open the Eclipse preferences from the Eclipse Window menu and navigate to the Qt Jambi section. Then, select the Browse button and locate the directory containing the Qt Jambi installation. Once you have located the Qt Jambi directory, and optionally validated the Qt Jambi install by selecting the Validate button, select the OK button to exit the dialog. Now, restart Eclipse.

Figure 5 illustrates the Qt Jambi Preference page in Eclipse.

Creating a Qt Jambi project in Eclipse

To create a Qt Jambi project in Eclipse, navigate to the File->New->Project menu. When the “Select a wizard” dialog appears, select “Qt Jambi Project (Using Designer Form)” from the Qt Jambi node, then click the Next button. This will take you to a page where you can specify the location of the new project. Select the desired location and click the Next button again. You will now be directed to set options for the source folders path, referenced projects, additional libraries, etc. Set any additional options and click Next. The next page will allow you to set the package name for your project, the name of your main widget class and the UI template of the main widget class.

Figure 6 illustrates the UI template page.

Now, click the Finish button and the new project will be added to your Eclipse workspace.

Figure 7 shows the Navigator view in Eclipse. You can see the new Qt Jambi project along with the classes and ancillary files generated by the Qt Jambi plug-in.

When you select Finish, the Qt Jambi Eclipse plug-in generates Java classes for the main widget along with other ancillary files, such as the .jui file containing the instructions needed by Qt Designer.

To edit the main widget class for your Qt Jambi project using the Qt Designer plug-in for Eclipse, right-click on the .jui file in Eclipse’s Navigator view and select the Open With->Qt Jambi Designer menu item. Qt Designer will open in the Eclipse Editor pane.

While you edit your main widget in the Qt Designer pane, the Qt Jambi plug-in will run the JUIC tool to keep your Java classes up-to-date, reflecting the changes you’ve made to the .jui file. The .jui compile is executed before any automatic Eclipse build processes.

Deploying Qt Jambi applications

You can deploy a Qt Jambi project as a Web Start application, an executable .jar file, or a standard Java application. Since Qt Jambi is wrapped around Qt’s C++ libraries, native platform-dependent code must be distributed along with the portable Java bytecode. Qt Jambi addresses this issue in a very clean manner by searching the classpath using the Qt Jambi resource-locating mechanism based on a consistent URI syntax. This technology allows the com.trolltech.qt.Utilities.loadLibrary() method to locate native libraries placed on the classpath in any form, such as the filesystem or in a .jar file.

To deploy native libraries along with Java bytecode, you only need to include the native libraries in your executable .jar file or place them in the filesystem and point the classpath to the directory/folder. You can also place any ancillary resources, such as images, data files, and configuration files on the classpath in a similar manner, and access them using the same resource-locating mechanism.

In conclusion

Qt Jambi is one answer to the recent clamor for new alternatives for Java-based desktop-application development. As this article has shown, Qt Jambi leverages the C++-based Qt framework’s support for cross-platform GUI development while integrating with the Java programming language. Qt Jambi enables you to use familiar Java constructs to develop desktop applications that mimic the native look and feel expected by users of Windows, Mac, and Linux desktops. It also introduces new programming constructs such as the signals-and-slots mechanism, which replaces the event listener interfaces familiar to Swing/AWT developers.

Qt Jambi definitely has a place in the Java development landscape, offering a power and sophistication to be reckoned with. At the same time, it introduces a reliance on Trolltech’s C++ libraries that could be said to work at cross-purposes with the goals of the Java platform. Java developers making the leap to Qt Jambi will be well served by at least a cursory understanding of how C++ code integrates with Java code, and vice versa. Understanding the Java Native Interface makes for a smoother adoption of Qt Jambi.

Qt Jambi integrates with any standard Java virtual machine (JVM) running Java Standard Edition 5.0 and later. APIs and tools such as Qt Designer facilitate rapid drag-and-drop development of sophisticated and powerful desktop applications. Application GUI components developed using Qt Jambi’s rendering system can augment or replace those based on Java’s Swing/Java 2D framework, and vice verse. It is also possible to develop Qt Jambi applications and design and edit Qt Jambi GUI forms using the Eclipse IDE. See the Resources section to learn more about Qt Jambi.

Jeff Hanson has more than 20 years experience as a software engineer, including working as senior engineer for the Windows OpenDoc project and as chief architect for the Zareus SOA platform. The author of numerous articles and books, he is the chief architect for Max Software Inc., where he leads design and implementation teams building desktop and server applications for the content-control industry using C++, PHP, and JEE.