Using Qyoto (Qt bindings for Mono/.Net)

Section: Programming

Background

Although, day-to-day, I use a Gnome desktop (at work and at home) because of its appearance and design choices, my attempts to use it in a Mono application have been hugely delayed by its insistence on using an old-school C convention for its API instead of an up-to-date object-oriented approach that fits with the object-oriented language environment. Apparently it is an attempt to be familiar to C coders, but I'd have thought that you'd get more Object Oriented coders would be using Mono and potentially pick up GTK# than the number of C developers you'd get moving to C#.

Qt3 apps have always looked out of place on a Gnome desktop, as they've never quite had the right style. As of Qt4, however (mainly Qt4.5, which merged the extra module in to the main source, but also earlier versions), Qt apps can look 100% native on a GTK/Gnome desktop. Qt is also originally written in C++ rather than C, meaning that it starts with an object-oriented base. With that in mind, Qyoto looks like quite a good candidate for Linux-based UIs that look native on either of the main desktops and which are easy to program with. Qt is also theoretically available on Windows and could look native there as well, but Linux users are more likely to have access to the dependencies.

Getting the required dependencies

Most Gnome desktops (except the purist FOSS ones that object to Mono on principle) will probably include GTK# packages already. If you run Gnome and want Qt support for Mono then you need to install your distro's "Mono Qt" package - openSUSE calls it mono-qt and Fedora calls if qyoto (although Qyoto is just a sub-section of a wider set of Qt libraries). If you want built-in GTK theming support then you'll also need to make sure that you either have the qt-gtkstyle package for Qt 4.4 or less, or have Qt4.5 or higher with GTK theming support built in (which isn't the case with the default openSUSE 11.2 builds).

Installation of packages should follow your normal package manager method for your distro. The Mono Qt bindings can have a lot of dependencies (including some core KDE libs for the dependencies of the Smoke libraries that Qyoto uses), so make sure you pull everything.

Differences with Qt/Qyoto

Qt and Qyoto use some strange conventions compared to other toolkits. Although signals and slots might not be ideal, Qyoto apparently supports delegates for slots even if it doesn't support standard .Net events as signals. That's a fairly simple process to get used to, though, and you just need to make sure that you don't refactor too often as the signal is a string representation of the method name.

Although signals and slots may be a bit strange at first, it is apparently flexible. The real oddity compared to most toolkits and editors comes when you use the Qt Designer and the uics to build a Qt UI component (either a window or a widget) and then compile it to C# code. When you compile a .ui file the compiler generates a pair of classes - Ui.NameOfWidget and UiNameOfWidget - one of which inherits from the other, but both providing a way of setting up a widget with the desired controls. This provides the developer with the choice of using the namespaced or un-namespaced version.

The strange parts of this uics compiled code are two-fold: 1) despite having compiled a dialog, the class doesn't extend from dialog (and being an auto-generated class, editing it just leads to lost code) and 2) all of the widgets are in public fields (which is normally a very bad practice in Object Oriented code). While this is strange at first, the following is my simple method for using the compiled code in a sensible object-oriented method with the minimum of lost code.

Using Qyoto compiled .ui files

The following code snippet assumes that a dialog has been created called "MainWindow" and that it was a "QMainWindow" in the Qt Designer. Rather than use the default "MainWindow" name, I've renamed it to "...Layout" to avoid naming conflicts and to keep a convention. The code below is commented more than I'd normally do just to explain the example.

// The file and the library/program it is in are licensed and distributed, without warranty, // under the GNU LGPL license, either version 3 of the License or (at your option) any later version. using System; using Qyoto; //Imports Qyoto/Qt classes namespace IBBoard.QtSampleApp { // Main window for sample app extends the Qyoto QMainWindow and nothing else public class MainWindow : QMainWindow { // Private reference to an instance of the compiled UI class private UiMainWindowLayout layout; public MainWindow () { //Constructor creates the layout so that the dialog has a copy layout = new UiMainWindowLayout (); //Pass the window to the layout's setup method so that the window // is correctly set up with all of its widgets layout.SetupUi(this); //Do any other setup here ... } //Additional methods can reference widgets on the form by doing layout.widgetName, // which references the public field of the window's private layout field ... } }

Although the behaviour of making the widgets in to public fields seemed strange at first, the fact that the layout is used as a private field in the example above means that they are still never visible outside the form, maintaining encapsulation. References to widgets on dialog or within control are then made via public fields of private instance of layout, which is a layer of indirection but a simple and manageable one.

Conclusion

Hopefully this rambling about Qt/Qyoto in a Mono environment has been helpful for those who are new to Qt/Qyoto, as I was. Qyoto shows a lot of promise, even if it doesn't have a lot of coverage on websites, and the C++-based API should be much easier to work with than the archaic horrors of iterrators and C-like methods in the GTK# API!

Navigation