Development/Tutorials/Writing kontact plugins

    From KDE TechBase
    Revision as of 19:42, 29 June 2011 by Neverendingo (talk | contribs) (Text replace - "</code>" to "</syntaxhighlight>")

    Template:I18n/Language Navigation Bar (zh CN)

    Introduction

    Kontact is a KDE PIM (personal information management) framework that allows embedding several pim applications like kmail and korganizer as plugins into one window. Kontact plugins use KParts that get loaded at runtime. So, this is a good time to learn how to write a kPart. The "hello world" kontact plugin we will be writing here will look like this:

    Writing a KPart

    We will write a "hello world"-KPart here. And you will learn how to hold it in a MainWindow. A KPart also gives you the flexibility to be integrated into kontact or konqueror, but we will do this later. So, the "hello world"-KPart only consists of a label "hello world". We chose a label because it is the easiest viewable thing to create. It does not trigger any action. The following is what you do

    • write a subclass mypart of KParts::Part
      • because setWidget is protected
      • use a Widget in it and use setWidget to get your widget into the KPart
    • write a subclass mypartmainwindow of KParts::MainWindow
      • because createGUI is protected (and you may want createGUI later)
    • write main.cpp
      • use new mypart(mw) to get your part into your main window

    We will use 4 files to do this:

    • CMakeLists.txt to ease building work
    • mypart.h to declare our classes
    • mypart.cpp to implement our classes

    Create a directory hello in a fresh checkout of kdepim. In it, add the following files:

    CMakeLists.txt

    project(hello)
    find_package (KDE4 REQUIRED)
    INCLUDE_DIRECTORIES( ${KDE4_INCLUDES} . )
    
    include_directories( ${KDE4_INCLUDE_DIR}/kcal ${CMAKE_SOURCE_DIR}/kresources/remote  )
    
    set(hello_shared_SRCS 
    	hello_part.cpp
     )
    
    
    set(hellopart_PART_SRCS hello_part.cpp)
    
    kde4_add_plugin(hellopart ${hellopart_PART_SRCS})
    
    target_link_libraries(hellopart ${KDE4_KPARTS_LIBS} ${KDE4_KCAL_LIBS}  ${KDE4_KIO_LIBS} kdepim kcal_resourceremote)
    if(X11_Xscreensaver_LIB)
    	target_link_libraries(hellopart  ${X11_Xscreensaver_LIB} )
    endif(X11_Xscreensaver_LIB)
    
    install(TARGETS hellopart  DESTINATION ${PLUGIN_INSTALL_DIR})
    
    install( FILES hello_part.desktop  DESTINATION ${SERVICES_INSTALL_DIR})
    

    hello_part.h

    #ifndef _HELLOPART_H_
    #define _HELLOPART_H_
    
    #include <kparts/part.h>
    #include <kparts/factory.h>
    #include <QLabel>
    
    /**
     * This is a "Part".  It that does all the real work in a KPart
     * application.
     */
    class helloPart : public KParts::ReadWritePart
    {
      Q_OBJECT
    
      private:
        QLabel *mMainWidget;
    
    public:
        helloPart(QWidget *parentWidget, QObject *parent, const QVariantList&);
    
        QLabel* MainWidget() { return mMainWidget; };
    
        virtual ~helloPart();
        bool openFile(){};
        bool saveFile(){};
    };
    
    #endif // _HELLOPART_H_
    

    hello_part.cpp

    #include "hello_part.h"
    
    #include <kdemacros.h>
    #include <kparts/genericfactory.h>
    
    K_PLUGIN_FACTORY(helloPartFactory, registerPlugin<helloPart>();)  // produce a factory
    K_EXPORT_PLUGIN(helloPartFactory("hello","hello") )
    
    helloPart::helloPart( QWidget *parentWidget, QObject *parent, const QVariantList& )
        : KParts::ReadWritePart(parent)
    {
      KGlobal::locale()->insertCatalog("hello");
      // we need an instance
      setComponentData( helloPartFactory::componentData() );
    
      mMainWidget = new QLabel();
      mMainWidget->setText("hello");
      setWidget( mMainWidget );
    }
    
    helloPart::~helloPart()
    {
    }
    
    #include "hello_part.moc"
    

    hello_part.desktop

    [Desktop Entry]
    Name=Friendly Component
    MimeType=text/calendar;
    X-KDE-ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart
    X-KDE-Library=hellopart
    Type=Service
    

    Compiling your KPart

    To compile, link and install the code above, use

    cmake . && make && make install
    

    Now you can find your kpart in the local lib folder:

    tweedleburg:~/svn/kdepim/hello # ll lib
    total 468
    -rwxr-xr-x 1 root root 471554 May 28 22:42 hellopart.so
    

    And after compilation e.g. in /usr/local/lib/kde4/hellopart.so

    And you can find your kpart in the services folder (${SERVICES_INSTALL_DIR} from CMakeLists.txt):

    $ ll $(kde4-config --install services)hello*
    -rw-r--r-- 1 root root 161 May 28 23:33 /usr/local/share/kde4/services/hello_part.desktop
    

    Kontact integration

    To integrate your KPart into kontact, you will need to create a subclass of Kontact::Plugin

    So, go to your checkout of kdepim, create a folder kdepim/kontact/plugins/hello. Continue there. We will make a shared object file kontact_helloplugin.so and a file hello_plugin.desktop. Those files will be the kontact plugin and tell kontact at runtime which kPart to load (hello_plugin.desktop) and how (kontact_helloplugin.so).

    Files

    CMakeLists.txt

    include_directories(${CMAKE_SOURCE_DIR}/hello)
    
    set(kontact_hello_PART_SRCS hello_plugin.cpp)
    
    kde4_add_plugin(kontact_helloplugin ${kontact_hello_PART_SRCS})
    
    target_link_libraries(kontact_helloplugin ${KDE4_KDEUI_LIBS} kontactinterfaces)
    
    install(TARGETS kontact_helloplugin  DESTINATION ${PLUGIN_INSTALL_DIR})
    
    install(FILES hello_plugin.desktop  DESTINATION ${SERVICES_INSTALL_DIR}/kontact)
    

    For me kontact_helloplugin is a binary residing in /usr/local/lib64/kde4/kontact_helloplugin.so. hello_plugin.desktop is installed to /usr/local/share/kde4/services/kontact/hello_plugin.desktop.

    hello_plugin.desktop

    The file kontact/plugins/hello/hello_plugin.desktop tells the kontact plugin which kPart to load. In this case, it is hellopart.

    [Desktop Entry]
    Type=Service
    Icon=whatever
    X-KDE-ServiceTypes=Kontact/Plugin,KPluginInfo
    
    X-KDE-Library=kontact_helloplugin
    X-KDE-KontactPluginVersion=7
    X-KDE-KontactPartLibraryName=hellopart
    X-KDE-KontactPluginHasSummary=false
    
    X-KDE-PluginInfo-Name=kontact_helloplugin
    X-KDE-PluginInfo-Version=0.1
    X-KDE-PluginInfo-License=GPL
    X-KDE-PluginInfo-EnabledByDefault=true
    
    Comment=The friendly Plugin
    Name=hello
    

    hello_plugin.h

    #ifndef HELLO_PLUGIN_H
    #define HELLO_PLUGIN_H
    
    #include <kontactinterfaces/plugin.h>
    #include <kparts/part.h>
    
    class HelloPlugin : public Kontact::Plugin
    {
      Q_OBJECT
    
      public:
        HelloPlugin( Kontact::Core *core, const QVariantList & );
        ~HelloPlugin();
    
        virtual void readProperties( const KConfigGroup &config );
        virtual void saveProperties( KConfigGroup &config );
    
      private slots:
        void showPart();
    
      protected:
        KParts::ReadOnlyPart *createPart();
    };
    
    #endif
    

    hello_plugin.cpp

    #include "hello_plugin.h"
    
    #include <hello_part.h>
    
    #include <kontactinterfaces/core.h>
    #include <kontactinterfaces/plugin.h>
    
    #include <kactioncollection.h>
    #include <kgenericfactory.h>
    #include <kicon.h>
    #include <kparts/componentfactory.h>
    
    EXPORT_KONTACT_PLUGIN( HelloPlugin, hello )
    
    HelloPlugin::HelloPlugin( Kontact::Core *core, const QVariantList & )
      : Kontact::Plugin( core, core, "Hello" )
    {
      setComponentData( KontactPluginFactory::componentData() );
    }
    
    HelloPlugin::~HelloPlugin()
    {
    }
    
    KParts::ReadOnlyPart *HelloPlugin::createPart()
    {
      KParts::ReadOnlyPart *part = loadPart();
    
      connect( part, SIGNAL(showPart()), this, SLOT(showPart()) );
    
      return part;
    }
    
    void HelloPlugin::readProperties( const KConfigGroup &config )
    {
      if ( part() )
      {
        helloPart *myPart = static_cast<helloPart*>( part() );
      }
    }
    
    void HelloPlugin::saveProperties( KConfigGroup &config )
    {
      if ( part() )
      {
        helloPart *myPart = static_cast<helloPart*>( part() );
      }
    }
    
    void HelloPlugin::showPart()
    {
      core()->selectPlugin( this );
    }
    
    
    #include "hello_plugin.moc"
    

    install your plugin

    To compile, link and install your plugin, change kdepim/kontact/plugins/CMakeLists.txt. Include the subdirectory hello like this:

    echo "add_subdirectory( weather )" >> kdepim/kontact/plugins/CMakeLists.txt
    

    Then, recompile and install your kdepim

    itchy:~/kdepim # make -j4 && make install
    

    Call kontact and you should see an additional plugin.

    plugin <-> application Communication

    Somehow, your kontact plugin must be able to communicate with your application. For example, if you have a mail application, there will be a need for a button "new mail" in kontact. If this button is clicked, the plugin needs to call a function in the mail application (for corba fans: "needs to send a message to the application"). This is done via dbus. The kontact plugin gets an auto-generated dbus interface provided with the following addition to CMakeLists.txt:

    qt4_add_dbus_interfaces(kontact_hello_PART_SRCS ${CMAKE_SOURCE_DIR}/hello/org.kde.hello.hello.xml)
    

    The above example is about the "hello" application.

    Integration into kontact's settings

    If you want your plugin to be configurable from within kontact (this is needed e.g. to deselect it), have a look into svn how other made this, e.g. here

    Beyond hello world

    An application around your kpart

    To write an application that only contains your kpart, see Development/Tutorials/Using_KParts.

    Context menu in the kontact plugin

    TODO, e.g. user:Tstaerk

    See also