KDE Tutorial - p5

p5

Next Previous Table of Contents

p5 is our first application that will communicate with other applications (p6, in this case). These are the sources :


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

main.cpp


#include <kmainwindow.h>
#include <kurl.h>
#include <kparts/browserextension.h>
 
class QLineEdit;
class KHTMLPart;
class QSplitter;
class QPushButton;
 
class MainWindow : public KMainWindow
{
 Q_OBJECT
public:
 
  MainWindow ( const char * name );
 
public slots:
  void changeLocation();
  void openURLRequest(const KURL &url, const KParts::URLArgs & );
  void bookLocation();
 
private:
 
  QLineEdit *location;
  KHTMLPart *browser;
  QPushButton *bookmark;
  QSplitter *split;
};

p5.h


#include "p5.h"
#include <qvbox.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include <qsplitter.h>
#include <dcopclient.h>
#include <kapp.h>
#include <kmenubar.h>
#include <klocale.h>
#include <khtml_part.h>
#include <kdebug.h>
 
MainWindow::MainWindow ( const char * name ) : KMainWindow ( 0L, name )
{
    setCaption("KDE Tutorial - p5");

    QPopupMenu * filemenu = new QPopupMenu;
    filemenu->insertItem( i18n( "&Quit" ), kapp, SLOT( quit() ) );
    QString about =
            i18n("p5 1.0\n\n"
                 "(C) 1999-2001 Antonio Larrosa Jimenez\n"
                 "larrosa@kde.org\t\tlarrosa@suse.de\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);
 
    QVBox * vbox = new QVBox ( this );
 
    location = new QLineEdit ( vbox );
    location->setText( "http://localhost" );
 
    connect( location , SIGNAL( returnPressed() ),
                this, SLOT( changeLocation() ) );
 
    split = new QSplitter ( vbox );
    split->setOpaqueResize();
 
    bookmark = new QPushButton ( i18n("Add to Bookmarks"), split );
 
    connect( bookmark, SIGNAL( clicked() ), this, SLOT( bookLocation() ) );
 
    browser=new KHTMLPart( split );
    browser->openURL( location->text() );
 
    connect( browser->browserExtension(),
	SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ),
	this, SLOT( openURLRequest(const KURL &, const KParts::URLArgs & ) ) );           	     
    setCentralWidget( vbox );
 
    DCOPClient *client = kapp->dcopClient();
    client->attach();
}
 
 
void MainWindow::changeLocation()
{
    browser->openURL( location->text() );
}

void MainWindow::openURLRequest(const KURL &url, const KParts::URLArgs & )
{
    location->setText( url.url() );
    changeLocation();
}
 
void MainWindow::bookLocation()
{
    DCOPClient *client=kapp->dcopClient();
    QByteArray params;
    QDataStream stream(params, IO_WriteOnly);
    stream << location->text();
    if (!client->send("p6-*", "bookmarkList", "add(QString)", params))
       kdDebug() << "Error with DCOP\n";
} 

p5.cpp

This application and p6 are a simple example of the new technology for desktop communication, DCOP . DCOP is an ICE based IPC/RPC mechanism that uses unix domain sockets or TCP/IP sockets transparently.

p6 will be an application that stores the user bookmarks that p5 tells it to store . Let's see how easy we can do it :

public slots:
  void changeLocation();
  void openURLRequest(const KURL &url, const KParts::URLArgs & );
  void bookLocation();
 
private:
 
  QLineEdit *location;
  KHTMLPart *browser;
  QPushButton *bookmark;
  QSplitter *split;

We are going to use a new slot that will be called when the user wants to store a location into his bookmark application. There are also a couple of new widgets we will need.

    split = new QSplitter ( vbox );
    split->setOpaqueResize();

We want to display a big button at the side of the html browser widget . Instead of making them a fixed size, we will let the user to change the size of the button, this is done with a QSplitter widget, that is a widget that distributes its size among its children.

We set split to do opaque resizing for the children to be painted while the user changes the size instead of just when the user finishes moving it.

    bookmark = new QPushButton ( i18n("Add to Bookmarks"), split );
 
    connect( bookmark, SIGNAL( clicked() ), this, SLOT( bookLocation() ) );

Now we can create a new QPushButton with localized text.

The clicked() signal is connected to our bookLocation() slot.

    browser=new KHTMLWidget( split );

Along with the bookmark button, browser is now a child of split . This is done this way to let split manage the sizes of both widgets as told earlier.

    DCOPClient *client = kapp->dcopClient();
    client->attach();

We already know the kapp macro, which is used to reference the KApplication current instance, this way, we can get the DCOPClient object, and tell it to attach this application to the DCOP server that should be running on the system (dcopserver is a fundamental part of the desktop so it will be always running)

    DCOPClient *client=kapp->dcopClient();
    QByteArray params;
    QDataStream stream(params, IO_WriteOnly);

When the user clicks on the button to add the current url to the bookmark list this slot is called. We first get the DCOPClient object of this application and then initialize a QDataStream object .

QDataStream is a class that is used to serialize data from any type . It features complete independency from the host machine (byte order, CPU, etc.). So you can make a stream in a big-endian system and read it on a little-endian computer without any problem.

    stream << location->text();

As the stream can contain any data type, we will use it to store the parameters to the function we will call in p6. We store the location text in the stream.

    if (!client->send("p6-*", "bookmarkList", "add(QString)", params))
       kdDebug() << "Error with DCOP\n";

Finally, we send a DCOP call to p6.

The first parameter is the name of the application that we want to connect to. The second one is the name of the object in the application that we want to use, and the third parameter is the name of the member of that object that we want to call. params is the stream that contains the parameters that we want to pass to that method.

In other words, p6 is an application which has an object called bookmarkList, and this object has a method called add that has a QString parameter, so this simple line calls the given method on p6 with the given parameters. As we don't need a return value from p6, it will return inmediately and continue running p5 while p6 adds the url to the bookmark list . Isn't it lovely ? ;-)

Note that we've appened a -* to the app name, this is because the p6 application will get a name in the dcopserver in the form "p6-<PID>" (i.e. "p6-12532") . By using an asterisk as the final character, we broadcast the signal to every application that matches the initial characters. If the application you're developing needs to send a signal to just one application, then you surely already know the PID of the app with which you want to communicate, so you can easily send a single signal by using "appname-PID".

We are checking the return value just in case the dcopserver couldn't complete the call. In this case, we use the KDE debug routines to let the application show us there's something wrong. As this is a message intended only for developers, we don't need to use i18n here.

kdDebug() returns a stream which you can use as output for your debug strings. There are several levels of logs : kdDebug, kdWarning, kdError and kdFatal With these you indicate the seriousness of the message. There are other debug functions that use an area number for each message, which specifies where the problem happened. With the area mechanism you can add or remove areas to debug specific parts of an application.

And that's all in p5. You should run p5 and p6 at the same time to let them work in team and see how they communicate.

Next Previous Table of Contents


© 1999,2000,2001 Antonio Larrosa