Writing KDE Applications

来源:互联网 发布:mac上的mysql客户端 编辑:程序博客网 时间:2024/05/29 19:40

March 2002
Volume 20 Number 3


Writing KDE Applications

Jason Mott

The Linux desktop is maturing on schedule. Here’s a look at developing event-driven GUI apps in KDE.


If you need to write a desktop application in the Linux environment and your audience is the average user, look no further than KDE [1]. KDE is one of the most popular desktop environments on Linux. It is certainly the most mature and user friendly. The KDE API stands above its competitors for speed to market, quick learning curve, and overall great software design.

At the core of KDE is Qt, an API written by Trolltech (<www.trolltech.com>). KDE’s API is written on top of Qt. This article does not focus on Qt itself. (Qt is practically its own high-level language with built-in tools and libraries.) Instead, this article focuses on Qt’s unique approach to handling events and how a programmer can manage Qt events through the KDE interface. It is possible to develop applications using only Qt, but if Linux is your destination, I recommend using the KDE layer. Using KDE gives your application better integration with the Linux desktop. Some advantages of using KDE over Qt alone include giving your application drag-and-drop support with other KDE applications (and even some Gnome [2] applications), interprocess communication through KDE’s DCOP (Desktop Communication Protocol), easy configuration saving and reading, and a consistent look-and-feel with other KDE applications.

To compile the examples in this article, you need to be running KDE with its included IDE, KDevelop. The examples in this article were developed and tested under KDE 2.2.1 using Qt 2.3.1 and KDevelop 2.0. I won’t discuss the details of installing KDE and KDevelop. Please refer to <www.kde.org> and <www.kdevelop.org> for more information. Every popular Linux distribution comes with KDE and KDevelop, so you may also find information on installing KDE and KDevelop with your distribution’s install disks. KDevelop includes a full reference for Qt and KDE classes. See KDevelop’s online reference manual for more details on the APIs mentioned in this article.

Background

The KDE project began in October of 1996 when its founder, Matthias Ettrich, envisioned making a Unix desktop environment out of the Qt API. He developed KDE under the GPL (GNU Public License). The KDE team even eventually convinced Trolltech to release a GPL version of Qt. KDE runs on a number of different flavors of Unix and other operating systems, but Linux is its native home.

KDevelop

The example I’ll be outlining will use KDevelop. KDevelop is a mature application that assists with developing KDE applications. It has a project tree, a syntax-highlighting text editor, and a full suite of wrappers for Linux’s GNU compiling tools (gcc, make, automake, autoconf, etc.). In fact, when KDevelop creates a project, the project never needs KDevelop for building. It sets up everything to be buildable on the command line using the standard GNU tools mentioned above. When KDevelop starts, it runs through some setup and then provides a wizard for creating a project. If you don’t get the wizard on startup, select “Project -> New” from the menu bar. The first page of the wizard gives you several choices of applications. The main categories are KDE, Gnome, Qt, Terminal, and Others. As you can see, almost any C/C++ project can be developed with KDevelop. Under the KDE option, several sub-options exist. Each one is explained if you highlight the option. The first three (Mini, Normal, and MDI) are options that create a skeleton stand-alone KDE application. The Mini option is just a plain-Jane window to which you must add code to get anything. It doesn’t come with anything unless you make additions. The Normal option is what you’d usually use; it sets up a lot of stock code that gives you a menu bar, toolbar, and status bar. It also sets up some classes that use the Document-View design model. The MDI (Multiple Document Interface) option is for applications that will have inner windows. KDevelop itself is an example of MDI; you can have many text-editing windows open within the application’s main window.

Once you’re in KDevelop, you’ll see that it has lots of build options, documentation browsing, and debugging options. The interface is pretty familiar if you’ve used IDEs before. If you are new (and even if you’re not), take the KDevelop tutorial (which will be a nice complement to this article). To take the tutorial, select “Help -> KDE Application Tutorials” from the menu bar.

Hello World!

Enough set-up talk, it’s code time! Assuming you’re at your Linux desktop running KDE with all the aforementioned versions, fire up KDevelop. Select “Project” and choose “New...”. You’ll notice the wizard has lots of great choices, and I encourage you to play with all of them. For the sake of simplicity, and to get going quickly with our first example, choose “KDE 2 Mini” for the type of application you want to build. I chose the Mini option because it has the least amount of skeleton code, and that makes it easier to focus on the “slots and signals” example I’ll show in this article. If you want to keep consistent with this example, call the application “HelloWorld” and put it in /usr/local/src/cuj. This will give you a basic frame that will demonstrate the key features explained in this article (when the examples are completed).

When the wizard completes, you’ll have a directory full of files and subdirectories. Fear not, you only need to worry about a few files in this example. The files that start with “Makefile” or “conf” are used by the GNU tools make, automake, and autoconf. Within the context of KDevelop, you never need to touch these files. The files named with all caps (INSTALL, TODO, AUTHORS, and COPYING) are files you can edit to give users of your source code information. The “admin” directory is where KDevelop stores all its internal-use files. The “doc” directory is where the application’s documentation is stored. KDevelop offers some nice documentation creation tools (like kdoc) — check out “Project -> Make API-Doc” and “Project -> Make User-Manual” in the menu bar. There will also be a directory named after the name of the application (“helloworld” in this example). This is the directory that holds the source code and is where most of the editing takes place. The files edited in this example (main.cpp and helloworld.cpp) are in this directory.

Listing 1 shows the main function from main.cpp. There isn’t much to it, but it sets up a very basic KDE application. (Note: there is a KMainWindow class that does even more for you before you get started. I’ve omitted the KMainWindow class here for simplicity’s sake. As a next step after you read this, I encourage you to browse the KDE documentation to learn more about KMainWindow and its many great features.) Lines 04-09 in Listing 1 set up a KAboutData object and register that application’s arguments into a static method of the KCmdLineArgs class. After these calls, the arguments and about data can be accessed anytime in the application through static methods in KCmdLineArgs.

The KApplication class comes into play in line 11 of Listing 1. KApplication is the granddaddy core of all KDE applications. It handles all the events from X (the Unix core GUI components, on which most Unix GUI’s are built, including Qt’s Linux version) and gives you access to fonts, theme options, and session management. KApplication does a lot of work for you behind the scenes. There isn’t much you need to do with KApplication in this example except to tell it what its main widget is going to be. The main widget will display the interface of the application. KDevelop creates a skeleton main widget class called HelloWorld (because that is what I defined as the application’s name). which is just a derived QTWidget. Once the HelloWorld class is created, it is up to the creator to make the class do anything. Later, I will add additional code to the HelloWorld class. I warn you, though; the example isn’t pretty. It’s only to demonstrate functionality.

Slots and Signals

Most GUI libraries handle events with callbacks. Callbacks are pointers to methods (or sometimes objects) that are registered with an event handler and called upon when some event happens. Events come in the form of user input (i.e., a mouse click or a keyboard press) or some other component wanting to “announce” something (i.e., a timer that has run out or state that has changed).

To deal with events, you create signals (the announcing of an event) and slots (methods that handle events), and you can connect any signal to any slot with the connect method present in all QObjects (of which your class must be a derivative to use slots and signals). One thing this example will not cover is signals and slots with arguments. When using arguments, you must make sure that the argument prototype of the signal is the same as the argument prototype of the slot.

To show you how to create signals and slots and how to connect them, I have made the HelloWorld application with a button called “Count Button” that increments a number below it and a button called “Reset Button” that resets the number to zero. When the counter reaches its maximum, the “Count Button” is disabled until the “Reset Button” is clicked.

As you will learn later in this article, Qt uses a pre-compiler, moc (Meta Object Compiler), that comes with Qt to manage callbacks for signals and slots. Until moc is explained, some of the code in this article will seem like incorrect C++ syntax, but don’t worry. The moc pre-compiler standardizes the syntax by using macros and adding methods to your classes.

In the application you’ve started in KDevelop, open up the file helloworld.h. Listing 2 is the HelloWorld class definition from helloworld.h. Add lines 03 and 10-23 to your code. The Q_OBJECT macro call is crucial to enabling slots and signals. Slots are just methods, but you tell moc they are slots by listing them under public slots:. As I will explain later, the “incorrect syntax” is corrected using the Standard C++ preprocessor. Listing 2 declares three slots that will be defined in helloworld.cpp. Signals are declared the same way, but under the label signals:, which is turned into protected: by the Standard C++ preprocessor.

For slots and signals to work, a QMetaObject must be created with moc. Without the benefit of KDevelop, you’d need to run Qt’s included pre-compiler moc on every header file that defines a QObject. moc creates an additional .cpp file that defines some extra methods in your class (which are declared in the Q_OBJECT macro as well as under the signals: label). The new file is usually named after the header file, but called .moc.cpp (e.g., helloworld.h produces a helloworld.moc.cpp). The actual name of the new file is defined with command-line options to moc, but in KDevelop this is all handled for you using the abovementioned naming convention. moc does not alter your header files; it just uses them to figure out what to put into the new .moc.cpp file. There should be one .moc.cpp file for each .h file that defines a class that uses slots and signals. moc also does not compile the generated .moc.cpp file. That can be done normally. KDevelop calls moc on your header files and compiles and links the new .moc.cpp files for you [3].

Next, you have to define the slots and connect them to signals. You don’t have to worry about defining signals, as moc defines those for you. All you have to do is “emit” them when you want to evoke an event. To see how to do this, open up helloworld.cpp and see Listing 3, which shows the code you should add to define the slots. Remember these slots are really just regular methods and can be called as such if the need arises in your applications. As slots, though, they can be connected to signals via the connect method.

The buttons in this example are brought to life with the code in Listing 4, which should be added to the HelloWorld::HelloWorld(parent, name) constructor in helloworld.cpp. Many slots and signals come built-in with KDE/Qt’s classes. Buttons, for example, have a clicked signal that can be connected with any slot that doesn’t take arguments. (Remember, slots can only be attached to signals whose prototype matches.) The clicked signal of buttons is what this example uses. See Table 1 for an overview of the QPushButton’s stock slots and signals.

As seen in Listing 4, there are three widgets added to the HelloWorld main widget: two buttons and one label. Notice these are pointers, and the objects are created on the heap. It is important to do it this way, as Qt cleans up for you. (No deletes are necessary in the destructor for Qt widgets [4].) The first button (Listing 4, line 04) is the “Count Button.” The label (Listing 4, line 08) is used to display the counter number. Finally, the second button (Listing 4, line 12) is used to reset the counter to zero.

The three calls to connect on lines 16-18 in Listing 4 glue this application together. Line 16 says to call the intCount slot when countButton emits the clicked signal. intCount (Listing 3, lines 01-12) increments the counter by one and checks to see if it hits the MAX_COUNT constant (Listing 2, line 20). If it is below the MAX_COUNT, it will update the label with the new number. If it is the MAX_COUNT or greater [5], it will set the count to MAX_COUNT, emit the maxCountReached signal, and print “Max count reached!” plus the max count to the label.

Line 17 in Listing 4 says to call the disableCountButton slot when the maxCountReached signal is emitted. This signal is the only signal defined by this application and is emitted in the intCount slot when the counter reaches its limit. Yes, you can emit signals within slots, but be careful of infinite loops! As the name implies, the disableCountButton slot makes the “Count Button” unusable, thus preventing the user from making the count go over the MAX_COUNT amount.

Finally, line 18 in Listing 4 says to call the resetCount slot when the resetButton is clicked. The resetCount slot puts the count at zero, enables the “Count Button,” and sets the label to the new count. And that’s our “Hello World” application that demonstrates slots and signals. If you haven’t done so already, in KDevelop go to “Build”, select “Execute”, and enjoy your new useless application. Hey, it never said “Hello World!” I’ve left that to you.

What moc Does

When KDevelop runs moc on your header files (or when you run moc on your header files manually) it creates a file that adds className, tr, initMetaObject, and staticMetaObject methods to the class, as well as methods for all your declared signals. The first four mentioned are declared in the Q_OBJECT macro (which is why it’s required). In addition, moc adds a member property metaObj, which is a singleton QMetaObject instance created in staticMetaObject. When the staticMetaObject creates the metaObj, it takes all the slots and signals, puts them into two structure arrays (one QMetaData structure array for slots and one for signals), and uses them in the constructor of the QMetaObject instance (metaObj) for this class. In the HelloWorld example, open up helloworld.moc.cpp and examine it for more details. (If you built the application yourself rather than downloading it, the file won’t be there until your first compile.)

When you compile your code with the C++ compiler, all instances of the words emit, slots, and signals in QObjects are either removed or replaced because of #defines in the Qt header file qobjectdefs.h. (This is how KDE/Qt gets away with the non-standard syntax.) Note that the emit keyword is only syntactic sugar. It is designed to remind the programmer (and tell the maintainer) that this is a signal (and thus its definition is in the file created by moc for that class). The code would work just as well without emit in front of the call to the signal. Don’t ever omit emit, however, because you never know what the future holds for this keyword.

Conclusion

As far as GUI development goes, KDE is a very easy programming environment once you’ve conquered the slots and signals. I’ve concentrated on slots and signals in this article. However, many concepts were sacrificed in this article to emphasize slots and signals. Your next step should be to read up on KMainWindow and how KDE implements the Document-View model and the MDI. Also, you’ll be interested in the KAction class. KDevelop comes complete with online tutorials on these and many more subjects. I recommend two good books, KDE 2.0 Development, by David Sweet et. al (Sams Publishing, 2000), and KDE Programming Bible, by Arthur Griffith (IDG Books, 2000)

Notes

[1] KDE stands for “K Desktop Environment” and the “K” is meaningless.

[2] Gnome is another desktop environment for Linux (<www.gnome.org>).

[3] If you were compiling “by hand,” .moc.cpp files can be compiled and linked with your program in one of two ways: either compile the .moc.cpp file in the same manner as other .cpp files and link a resulting .moc.o file at the end, or just put an #include directive in your .cpp file (knowing the naming convention) that is going to use it.

[4] Qt cleans up its widgets for you. Anytime you create a widget with a parent argument or register a widget with another widget, Qt’s stock code will clean up for you. This is why all child widgets should be created on the heap (a pointer using new) and not on the stack. The only class created on the stack should be the KApplication class (see Listing 1).

[5] It would only be greater in a race condition, as the example code shown here is not thread safe.

Jason Mott is a lead developer at Auragen Communications, Inc. (<www.auragen.com>), a top web development firm located in Rochester, NY, where he develops web applications in Java, Perl, and sometimes VBScript. He also serves there as a Linux systems consultant. Jason is a Linux freak and C/C++ enthusiast. In his spare time he codes KDE applications. He recently took over the Konverse project (a KDE Jabber client) and helps out with Komba (a KDE Network Neighborhood browser). He can be reached at jmott@users.sourceforge.net.

 
原创粉丝点击