p3

Next Previous Table of Contents

We already created a simple KDE application in p2, but in most cases, you'd like to use a more complex user interface than a simple button :-) , so let's see how to add a standard main widget.


#include <kapp.h>
#include "p3.h"
 
int main( int argc, char **argv )
{
    KApplication a( argc, argv, "p3");
 
    MainWindow *window=new MainWindow( "Tutorial - p3" );
    window->resize( 400, 300 );
 
    a.setMainWidget( window );
    window->show();
 
    return a.exec();
} 

main.cpp


#include <kmainwindow.h>
 
class MainWindow : public KMainWindow
{
   Q_OBJECT
  public:
 
    MainWindow ( const char * name );
 
  public slots:
    void fileOpen();
    void fileSave();
};

p3.h


#include "p3.h"
#include <kfiledialog.h>
#include <kapp.h>
#include <kmenubar.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <qtextview.h>
 
MainWindow::MainWindow ( const char * name ) : KMainWindow ( 0L, name )
{
    setCaption("KDE Tutorial - p3");

    QPopupMenu *filemenu = new QPopupMenu;
    filemenu->insertItem( i18n( "&Open" ), this, SLOT(fileOpen()) );
    filemenu->insertItem( i18n( "&Save" ), this, SLOT(fileSave()) );
    filemenu->insertItem( i18n( "&Quit" ), kapp, SLOT(quit()) );
    QString about =
            i18n("p3 1.0\n\n"
                 "(C) 1999-2002 Antonio Larrosa Jimenez\n"
                 "larrosa@kde.org\t\tantlarr@supercable.es\n"
                 "Malaga (Spain)\n\n"
                 "Simple KDE Tutorial\n"
                 "This tutorial comes with ABSOLUTELY NO WARRANTY\n"
                 "This is free software, and you are welcome to redistribute it\n"
                 "under certain conditions\n");
 
    QPopupMenu *helpmenu = helpMenu( about );
    KMenuBar *menu = menuBar();
    menu->insertItem( i18n( "&File" ), filemenu );
    menu->insertSeparator();
    menu->insertItem( i18n( "&Help" ), helpmenu );
 
    QTextView *hello=new QTextView(
       i18n("<H2>Hello World !</H2><BR>This is a simple"
	" window with <I><font size=5><B>R<font color=red"
	" size=5>ich </font><font color=blue size=5>Text"
	"</font></B></I> capabilities<BR>Try to resize"
	" this window, all this is automatic !"), "", this );
    setCentralWidget( hello );
 
} 
 
void MainWindow::fileOpen()
{
    KURL filename = KFileDialog::getOpenURL( QString::null, "*", this );
    QString msg = QString( i18n("Now this app should open the url %1 .") ).arg(filename.url());
    KMessageBox::information( 0, msg, i18n( "Information" ), 
		"fileOpenInformationDialog" );
}
 
void MainWindow::fileSave()
{
   KURL filename=KFileDialog::getSaveURL( QString::null, "*", this );
}

p3.cpp

This code seems much more complex, but it isn't that much ;), let's have a look.

The sources in main.cpp aren't quite different from p1 or p2. The only difference is that now we create a MainWindow object instead of a QPushButton one and that we don't specify a NULL pointer as parent widget because it's taken by default.

Also, we don't connect any signal here, as MainWindow doesn't have any clicked() signal.

class MainWindow : public KMainWindow
{
   Q_OBJECT

In p3.h, we define the MainWindow class, inherited from KMainWindow, which is included in the kdelibs and includes all the needed methods to create a normal KDE application with a menu, a toolbar, a status bar, etc. Note that previous versions of the tutorial inherited from KTMainWindow, but that class is now considered obsolete.

We note that we have written Q_OBJECT in the declaration of our class. This is a macro that declares the special variables and virtual members needed by the meta object to implement the signal/slot mechanism and other things, it also tells moc that this is a QObject and can have signals and/or slots,

 
  public slots:
    void fileOpen();
    void fileSave();

As we learnt in p1, slots are normal methods, so they can be public:, protected: or private: as any other. The only difference is that we must specify public slots:, "protected slots:", or "private slots:", so that moc knows that these are slots.

The preprocessor will take care to remove or substitute all these special tags for the compiler not to care about it.

Let's see now the real thing, in p3.cpp :

    QPopupMenu *filemenu = new QPopupMenu;
    filemenu->insertItem( i18n( "&Open" ), this, SLOT(fileOpen()) );
    filemenu->insertItem( i18n( "&Save" ), this, SLOT(fileSave()) );
    filemenu->insertItem( i18n( "&Quit" ), kapp, SLOT(quit()) );

filemenu is a QPopupmenu, that is, a menu that contains "items" that the user clicks on to perform a given action.

We insert these items by calling insertItem. The first parameter is the text that should be on the menu, with a & character just before the letter that we want to use to access this entry in a faster way. By using i18n, we ensure that this text will appear in the user's language.

When the user selects an entry, the menu emits a signal that can (and should !) be connected to a function to do something. For convenience, instead of using connect, we can connect this signal directly when defining the item . That's what we do in the second and third parameters. We connect the Open menu entry to the fileOpen() slot in this object, the Save entry to the fileSave() slot in this object, and the Quit entry to the quit() slot in the application as we did in p1 and p2.

We note that kapp is a convenience function that can be used to access the current KApplication instance.

    QString about =
            i18n("p3 1.0\n\n"
                 "(C) 1999-2002 Antonio Larrosa Jimenez\n"
                 "larrosa@kde.org\t\tantlarr@supercable.es\n"
                 "Malaga (Spain)\n\n"
                 "Simple KDE Tutorial\n"
                 "This tutorial comes with ABSOLUTELY NO WARRANTY\n"
                 "This is free software, and you are welcome to redistribute it\n"
                 "under certain conditions\n");
 
    QPopupMenu *helpmenu = helpMenu( about );

We first put all those strings inside a QString object. QString is a general purpose string class used to manipulate strings in any form. One of the main benefits of it is that you can use Unicode in your application automatically, without having to worry about it. It also has implicit sharing mechanism which increases the efficiency as creating a copy of a QString doesn't really copy the contents until one of them changes.

Then, we create another popup menu, but we use the KMainWindow member, helpMenu . By doing this, we get a standard help menu with entries for the documentation, an about dialog, etc. The about dialog includes all the text that we've written in the about string.

    KMenuBar *menu = menuBar();
    menu->insertItem( i18n( "&File" ), filemenu );
    menu->insertSeparator();
    menu->insertItem( i18n( "&Help" ), helpmenu );

We are prepared to finish the creation of our menu. First, we get the menubar used in our main window by using menuBar(). As there's no menubar yet, this method will create it for us and will return an empty KMenuBar object that we can fill. KMenuBar provides a standard KDE menu, with the common look across applications.

Then, we insert the two QPopupMenus that we already have, with a separator in between, and we're done creating the menu.

    QTextView *hello=new QTextView(
       i18n("<H2>Hello World !</H2><BR>This is a simple"
	" window with <I><font size=5><B>R<font color=red"
	" size=5>ich </font><font color=blue size=5>Text"
	"</font></B></I> capabilities<BR>Try to resize"
	" this window, all this is automatic !"), "", this );
    setCentralWidget( hello );

I wanted this application to do something nice and simple, so we are going to add a QTextView widget. This widget is a useful Rich Text viewer, where we can specify the type of font and colour among other styles. After creating it with the above text, we set it as the central widget for this window.

But, what is a central widget ? A central widget is the widget that the user can see under the menu (or toolbar, if any). That is, the document that the user has opened, or anything that is not standard part of KMainWindow (that is, the menu, toolbars and status bar)

    KURL filename = KFileDialog::getOpenURL( QString::null, "*", this );

We are now looking at the fileOpen slot implementation. This code opens a open file dialog that allows the user to open an existing file.

The tree parameters indicate the directory to start the browse on (we don't care what it is, so it takes the current one as default), the filters (to browse only through files with a given regular expression), and the parent object.

Now we have the url the user wants to open stored on filename.

Note that you're encouraged to allow the user to open any URL not just local files. For this, we have used a getOpenURL dialog, which lets the user select any url. Check below for an example on how to use the KIO library.

    QString msg=QString( i18n("Now this app should open the url %1 .") ).arg(filename.url());

We can manipulate strings with the QString class, in fact, we're using this to compose a message that we're going to show (as opening a file isn't inside the objetives of this tutorial). The format is similar to that of sprintf, but you don't have to care about data types, as you only need to use %1, %2, etc. and then, .arg(var1).arg(var2).arg(var3) , etc. Something nice about QString is that it doesn't have any problem when the destination string doesn't have enough room for the entire string, it just allocates the needed space, as it knows what's the length of the string.

Btw, filename.url() is a QString containing the url stored on the KURL object, that is, protocol+hostname+path+query+everything :-).

    KMessageBox::information( 0, msg, i18n( "Information" ), "fileOpenInformationDialog" );

With the KMessageBox class, and its static members, we can create a variety of message boxes. In this example, we use a information box to display which file it should have opened. The third parameter is the caption message and the fourth one is an identifier, that is used to store in a config file ( usually ~/.kde/share/config/p3rc ) if the user wants or prefer not to see this dialog anymore (thing that he can decide by using a checkbox in the dialog)

Note that we haven't written any way to get back to show the dialog again ! This is done simply by calling KMessageBox::enableAllMessages();, but I think it would be a nice exercise for the reader of this tutorial to add a menu that makes this call on top of p3 :-) .

    KURL filename=KFileDialog::getSaveURL( QString::null, "*", this );

Finally, in fileSave we call the analogue to getOpenURL, getSaveURL.

We should note that if we use getOpenURL, we should make use of the KIO library to get a remote file if that's what the user wants, this is basically done by doing something like this:

    QString tmpFile;
    if( KIO::NetAccess::download( "ftp://ftp.kde.org/myfile.tgz", tmpFile ) )
    {
      /* Do whatever you want with a _local_ file stored as tmpFile */
      
      KIO::NetAccess::removeTempFile( tmpFile );
    }

But that is out of the scope of this tutorial (altough I may increase it in the future with an example on libkio).

Well, I hope it wasn't too difficult .

Next Previous Table of Contents


© 1999-2002 Antonio Larrosa