As you saw in Chapter 4, "Core Library Classes," Delphi has two visual class libraries: the cross-platform library (CLX) alongside the traditional Windows library (VCL). There are certainly many differences, even in the use of the RTL and code library classes, between developing programs specifically for Windows or with a cross-platform attitude, but the differences are most striking in the user interface portion.
The visual portion of VCL is a wrapper of the Window API. It includes wrappers of the native Windows controls (like buttons and edit boxes), the common controls (like TreeViews and ListViews), plus a bunch of native Delphi controls bound to the Windows concept of a window. In addition, a TCanvas class wraps the basic graphic calls, so you can easily paint on the surface of a window.
VisualCLX, the visual portion of CLX, is a wrapper of the Qt (pronounced "cute") library. It includes wrappers of the native Qt widgets, which range from basic to advanced controls, very similar to Windows' standard and common controls. It also includes painting support using another, similar, TCanvas class. Qt is a C++ class library developed by Trolltech (www.trolltech.com), a Norwegian company with a strong relationship with Borland.
On Linux, Qt is one of the de facto standard user-interface libraries and is the basis of the KDE desktop environment. On Windows, Qt provides an alternative to the use of the native APIs. Unlike VCL, which provides a wrapper to the native controls, Qt provides an alternate implementation to those controls. Even if each of them is based on Windows's window, a QT button isn't a Windows BUTTON class control (you can see this by running WinSight32). This allows programs to be truly portable, because no hidden differences are created by the operating system (or introduced by the operating system vendor behind the scenes). It also allows you to avoid an extra layer; CLX on top of Qt on top of Windows native controls suggests three layers, but in fact there are two layers in each solution (CLX controls on top of Qt, VCL controls on top of Windows).
Note |
Distributing Qt applications on Windows implies the distribution of the Qt library itself. On the Linux platform, you can generally take the presence of the Qt library for granted, but you still have the interface library to deploy. Also, Linux Borland's CLX is tied to a specific version of Qt (which in Kylix 3 has been specifically patched by Borland), so you'll probably have to distribute it anyway. Distributing the Qt libraries with a professional application (as opposed to an open source project) generally implies paying a license to Trolltech. If you use Delphi or Kylix to build Qt applications, however, Borland has already paid the license to Trolltech for you. You must use at least one CLX class wrapping Qt: If you use the Qt classes exclusively (no CLX at all), you still owe the license to Qt, even when using Delphi or Kylix. |
Technically, huge differences exist behind the scenes between a native Windows application built with VCL and a portable Qt program developed with VisualCLX. Suffice to say that at the low level, Windows uses API function calls and messages to communicate with controls, whereas Qt uses class methods and direct method callbacks and has no internal messages. Technically, the Qt classes offer a high-level object-oriented architecture, but the Windows API is still bound to its C legacy and a message-based system dated 1985 (when Windows was released). VCL offers an object-oriented abstraction on top of a low-level API, whereas VisualCLX remaps an already high-level interface into a more familiar class library. (For more on the Qt architecture, see the following sidebar "From Qt to CLX.")
Note |
Microsoft has reached the point of starting to abandon the traditional low-level Windows API for a native high-level class library, part of the .NET architecture. You can read more about this topic in Part IV of this book. |
If the underlying architectures of the Windows API on one side and Qt on the other side are relevant, the two class libraries built by Borland (VCL and CLX) flatten out most differences, making the code of Delphi and Kylix applications extremely similar. Using VisualCLX on Linux offers Delphi programmers the advantage of having a familiar class library on top of a totally new platform. From the outside, a button is an object of the TButton class for both libraries, and it has more or less the same set of methods, properties, and events. In many cases, you can recompile your existing programs for the new class library in a matter of minutes, if they don't use low-level API calls, platform-dependent features (like ADO or COM), or legacy features (like the BDE).
Delphi has full support for both libraries at design time and at run time. As you begin developing a new application, you can use the File ® New Application command to create a new VCL-based program or File ® New CLX Application for a new CLX-based program. After you give one of these commands, Delphi's IDE will create a VCL or CLX design-time form and update the Component Palette so that it displays only the visual components compatible with the type of application you've selected (see Figure 5.1 for a comparison). You cannot place a VCL button into a CLX form, and you cannot even mix forms of the libraries within a single executable file. In other words, the user interface of every application must be built using one of the two libraries exclusively, which (aside from the technical implications) makes a lot of sense to me.
If you haven't already done so, I suggest you to try experimenting with the creation of a CLX application, looking at the available controls and trying to use them. You'll find few differences in the use of the components, and if you have been using Delphi for some time, you'll probably be immediately adept with CLX.
One of the cornerstones of the source-code compatibility between CLX and VCL is the fact that similar classes in the two libraries have the same class name. For example, each library has a class called TButton representing a push button; the methods and properties are so similar that this code will work with both libraries:
with TButton.Create (Self) do begin SetBounds (20, 20, 80, 35); Caption := 'New'; Parent := Self; end;
The two TButton classes can have the same name because they are saved in two different units, called StdCtrls and QStdCtrls. Of course, you cannot have the two components available at design time in the palette, because the Delphi IDE can register only components with unique names. The entire VisualCLX library is defined by units corresponding to the VCL units, but with the letter Q as a prefix—so there is a QForms unit, a QDialogs unit, a QGraphics unit, and so on. A few peculiar units, such as QStyle, have no corresponding unit in VCL because they map to features of Qt with no correspondence in the Windows API.
Notice that there are no compile settings or other hidden techniques to distinguish between the two libraries; what matters is the set of units referenced in the code. Remember that these references must be consistent—you cannot mix visual controls of the two libraries in a single form or even in a single program.
As you create a form at design time, it is saved to a form definition file. Traditional VCL applications use the DFM extension, which stands for Delphi form module. CLX applications use the XFM extension, which stands for cross-platform (X) form module. A form module is the result of streaming the form and its components: The two libraries share the streaming code, so they produce a similar effect. The format of DFM and XFM files, which can be based on a textual or binary representation, is identical.
So, the reason for having two different extensions doesn't lie in internal compiler tricks or incompatible formats. It is merely an indication to programmers and to the IDE of the type of components you should expect to find within that definition (this indication is not included in the file).
If you want to convert a DFM file into an XFM file, you can simply rename the file. However, expect to find some differences in the properties, events, and available components— reopening the form definition for a different library will probably cause quite a few warnings.
Tip |
Delphi's IDE chooses the active library by looking at the extension of the form module, ignoring the references in the uses statements. For this reason, you should change the extension if you plan to use CLX. On Kylix, a different extension is useless, because any form is opened in the IDE as a CLX form regardless of the extension. On Linux, there is only the Qt-based CLX library, which is both the cross-platform and the native library. |
As an example, I've built two identical applications, LibComp and QLibComp, with only a few components and a single event handler. Listing 5.1 presents the textual form definitions for two applications, built using the same steps in the Delphi IDE, after choosing a CLX or VCL application. I've marked differences in bold; as you can see, there are very few, most relating to the form and its font. OldCreateOrder is a legacy property used for compatibility with Delphi 3 and older code; standard colors have different names; and CLX saves the scroll bars' ranges.
object Form1: TForm1 object Form1: TForm1 Left = 192 Left = 192 Top = 107 Top = 107 Width = 350 Width = 350 Height = 210 Height = 210 Caption = 'QLibComp' Caption = 'LibComp' Color = clBackground Color = clBtnFace VertScrollBar.Range = 161 Font.Charset = DEFAULT_CHARSET HorzScrollBar.Range = 297 Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] TextHeight = 13 TextHeight = 13 TextWidth = 6 OldCreateOrder = False PixelsPerInch = 96 PixelsPerInch = 96 object Button1: TButton object Button1: TButton Left = 56 Left = 56 Top = 64 Top = 64 Width = 75 Width = 75 Height = 25 Height = 25 Caption = 'Add' Caption = 'Add' TabOrder = 0 TabOrder = 0 OnClick = Button1Click OnClick = Button1Click end end object Edit1: TEdit object Edit1: TEdit Left = 40 Left = 40 Top = 32 Top = 32 Width = 105 Width = 105 Height = 21 Height = 21 TabOrder = 1 TabOrder = 1 Text = 'my name' Text = 'my name' end end object ListBox1: TListBox object ListBox1: TListBox Left = 176 Left = 176 Top = 32 Top = 32 Width = 121 Width = 121 Height = 129 Height = 129 Rows = 3 ItemHeight = 13 Items.Strings = ( Items.Strings = ( 'marco' 'marco' 'john' 'john' 'helen') 'helen') TabOrder = 2 TabOrder = 2 end end end end
If you look at the source code of a VCL or CLX application, the only relevant difference relates to the uses statements. The form of the CLX application has the following initial code:
unit QLibCompForm; interface uses SysUtils, Types, Classes, QGraphics, QControls, QForms, QDialogs, QStdCtrls;
The form of the VCL program has the traditional uses statement:
unit LibCompForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
The code of the class and of the only event handler is absolutely identical. Of course, the compiler directive {$R *.dfm} is replaced by {$R *.xfm} in the CLX version of a standard program.
When you press the F1 key in the editor to ask for help on a routine, class, or method of the Delphi library, you'll usually get a choice between the VCL and CLX declarations of the same feature. You'll need to make a choice to proceed to the related help page, which can be quite annoying after a while (especially because the two pages are often identical).
If you don't care about CLX and are planning to use only VCL (or vice versa), you can disable this alternative by choosing the Help ® Customize command, removing everything with CLX in the name from Contents, Index, and Link, and saving the project. Then restart the Delphi IDE, and the Help engine won't bother asking you about CLX any more. Of course, don't forget to add those help files again if you decide to begin using CLX. Similarly, you can reduce the memory occupation and load time of the Delphi IDE by uninstalling all the CLX-related packages.
Because two different user interface libraries are available in Delphi, you'll have to choose one for each visual application. You must evaluate multiple criteria to come to the proper decision, which isn't always easy.
The first criterion is portability. If running your program on Windows and on Linux, with the same user interface, is a major concern to you, then using CLX will make your life simpler and let you keep a single source code file with limited IFDEFs. The same applies if you consider Linux to be (or think it possibly will become) your key platform. On the other hand, if most of your users are on Windows and you just want to extend your offering with a Linux version, you might want to keep a dual VCL/CLX system. Doing so means you'll probably need two different sets of source code files, or you may have too many IFDEFs.
Another criterion is the native look-and-feel. Is you use CLX on Windows, some controls will behave slightly differently than users expect—at least expert users. For a simple user interface (edits, buttons, grids), this probably won't matter much; but if you have many tree view and list view controls, the differences will be clear. On the other hand, with CLX, you'll be able to let your users choose a look-and-feel that's different from the basic Windows look, and use it consistently across platforms. This means that a Motif fan will be able to choose this style even when forced to use the Windows platform. While this flexibility is common on Linux, you'll seldom use a non-native look-and-feel on Windows.
Using native controls also implies that as soon as you get a new version of the Windows operating system, your application will (probably) adapt to it. This is good for the user, but might cause you a lot of headaches in case of incompatibilities. Differences in the Microsoft common controls library over the last few years have been a major source of frustration for Windows programmers in general, including Delphi programmers.
Another criterion is deployment: If you use CLX, you'll have to ship your Windows and Linux program with the Qt libraries.
I've done a little testing, and the speed of VCL and CLX applications is similar. I've tried creating 1,000 components and showing them on screen, and the speed differences are few; the VCL-based solution offered a 30 percent advantage (for what this limited benchmark is worth). You can try my tests with the LibSpeed and QLibSpeed examples for this chapter.
Another important criterion for deciding to use CLX instead of VCL is a need for Unicode support. CLX has Unicode support in controls by default (even on Win9x platforms where it isn't supported by Microsoft). VCL, however, has very little Unicode support even on versions of Windows that provide it, making it difficult to build VCL apps for countries where the local character is managed more easily when it is Unicode based.
The real issue of choosing the library resolves to the importance of Linux or Unicode for you and your users. It's important to notice that if you create a CLX application, you'll be able to recompile it unchanged (with the exact source code) with Kylix, producing a native Linux application, unless you've done any Windows API programming at all, in which case conditional compilation will be essential.
As an example, I've recompiled the QLibComp example introduced earlier. Figure 5.2 shows it running; you can also see the Kylix IDE in action on KDE.
If you want to keep a single source code file but compile with VCL on Windows and CXL on Linux, you can use platform-specific symbols (such as $IFDEF LINUX) to distinguish the two situations in case of conditional compilation. But what if you want to be able to compile a portion of code for both libraries on Windows?
You can either define a symbol of your own and use conditional compilation, or (at times) test for the presence of identifiers that exist only in VCL or CLX:
{$IF Declared(QForms)} ...CLX-specific code {$IFEND}
Besides starting with new CLX applications, you might want to convert some of your existing VCL applications to the new class library. You must perform a series of operations without any specific help from the Delphi IDE:
Rename the DFM file using the XFM extension and update all the {$R *.DFM} statements as {$R *.XFM}.
Update all the uses statements in your program (in the units and project files) to refer to the CLX units instead of the VCL units. If you miss even a few, you'll bump into trouble when running your application.
Tip |
To prevent a CLX application from compiling if it contains references to VCL units, you can move the VCL units to a different directory under lib and avoid including this folder in your search path. This way, leftover references to VCL units will cause a "Unit not found" error. |
Table 5.1 compares the names of the visual VCL and CLX units, excluding the database portion and some rarely referenced units.
VCL |
CLX |
---|---|
ActnList |
QActnList |
Buttons |
QButtons |
Clipbrd |
QClipbrd |
ComCtrls |
QComCtrls |
Consts |
QConsts |
Controls |
QControls |
Dialogs |
QDialogs |
ExtCtrls |
QExtCtrls |
Forms |
QForms |
Graphics |
QGraphics |
Grids |
QGrids |
ImgList |
QImgList |
Menus |
QMenus |
Printers |
QPrinters |
Search |
QSearch |
StdCtrls |
QStdCtrls |
You might also convert references to Windows and Messages into references to the Qt unit. Some Windows data structures are now also available in the Types unit (see Chapter 3, "The Run-Time Library," for details), so you might have to add it to your CLX programs. Notice, however, that the QTypes unit is not the CLX version of VCL's Types unit; these two units are totally unrelated.
Warning |
Watch out for your uses statements! If you compile a project that includes a CLX form, but you fail to update the project source code, leaving a reference to the VCL Forms unit there, your program will run but stop immediately. The reason is that no VCL form was created, so the program terminated right away. In other cases, trying to create a CLX form within a VCL application will cause run-time errors. Finally, the Delphi IDE might inappropriately add references to uses statements of the wrong library; in this case you end up with a single uses statement that refers to the same unit for both libraries, but only the second of the two will be effective. This situation rarely prevents the program from compiling, but you won't be able to run it. |
As a helper in converting some of my own programs, I've written a simple unit-replacement tool called VclToClx; it's available in the Tools section of the source code that accompanies the book. You can find more information about this program in Appendix A, "Extra Delphi Tools by the Author."