Action!
Author: Jim Bublitz

# Tutorial - Fourth Program

### KAction

In a graphical application, users interact with application using menus, toolbar buttons and keyboard shortcuts. The KAction class provides a convenient and efficient way.

This tutorial differs slightly from the Techbase tutorial it was derived from.

In even a small application, it's common to create a number of KActions - one for each of the different menu entries - so coding them compactly is important.

Our goal in this tutorial is to add two menu items: a Clear menu item with an associated button that appears in the toolbar, and a Quit menu item that we can link to something, possibly in our code. The Clear menu item and toolbar button will clear whatever text has been typed in the edit window of the app.

All of the necessary changes occur in the MainWindow class, which now looks like this:

    class MainWindow (KXmlGuiWindow):
        def __init__ (self):
            KXmlGuiWindow.__init__ (self)

            self.textArea =  KTextEdit ()
            self.setCentralWidget(self.textArea)

            self.setupActions()

        def setupActions (self):
            clearAction = KAction(KIcon("edit-clear"), i18n("Clear"), self)
            self.actionCollection().addAction("clear", clearAction)
            self.connect (clearAction, SIGNAL ("triggered(bool)"), self.textArea.clear)

            KStandardAction.quit (app.quit, self.actionCollection())

            self.setupGUI(QSize (600, 400), KXmlGuiWindow.Default, os.path.join (sys.path [0],"kactionui.rc"))

We've removed the setupGUI call from MainWindow.__init__ and placed it (with argeuments) at the end of a new method called setupActions

To create our Clear menu/toolbar entries, we need to create a KAction that represents this action. Creating a KAction from scratch requires 4 steps and a lot of explanation.

The first step is to create a KAction and give it three properties: an icon, a name, and an owner:

    clearAction = KAction(KIcon("edit-clear"), i18n("Clear"), self)

The icon chosen ("edit-clear") is a standard KDE icon, and KIcon will find it automatically. The name for the action ("Clear") will appear in the menu - i18n wraps it so that it can be automatically translated into different languages. Lastly, the KXmlGuiWindow (self) will be the owner of this action. We could have created an empty KAction objects and used KAction's methods to set the action's properties, but that requires 4 lines of code instead of one - a big difference if you're creating 10 or 20 actions.

If you've done actions using PyKDE3, you may notice that the owner is no longer the main window's action collection, and the action collection isn't referenced in the KAction constructor call. In PyKDE4 (or plain KDE4), it's necessary to do an explicit call to add the action to the action collection, so our second step is:

    self.actionCollection().addAction("clear", clearAction)

Notice that KXmlGuiWindow already owns an action collection that we can reference using self.actionCollection (). Notice also that we've given the action a different name (its "internal" name - "clear"), and provided a reference to the action itself (clearAction).

For our action to actually cause something to happen, we have to link the action to some code that does what we want. We accomplish this using Qt's "signals and slots" mechanism, so our third step is:

    self.connect (clearAction, SIGNAL ("triggered(bool)"), self.textArea.clear)

connect is a static method belonging to QObject, so we can call it using any QObject descendant - in this case our KXmlGuiWindow descendant (self).

Specifying a connection in PyQt/PyKDE is simpler than in C++. We just specify the object which emits the signal (our action clearAction in this case), we use the SIGNAL function from QtCore to provide the signal's C++ signature (in this case "triggered(bool)") specified as a character string, and we tell connect what slot to connect the signal to. In this case, the slot we're using already exists in the KTextEdit we're using, so we specify self.textArea.clear. The slot can be any callable Python function or method.

If you've been keeping count, we said there were four steps to creating an action, and we only competed three. The last step applies to all actions we create: We need a GUI file written in XML that tells KDE how to build the GUI using our actions:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
    <gui name="KAction" version="1.2">
      <ToolBar name="mainToolBar" >
        <text>Main Toolbar</text>
          <Action name="clear" />
          <ActionList name="dynamicActionlist" />
      </ToolBar>
      <MenuBar>
        <Menu name="file" >
            <text>&File</text>
            <Action name="clear" />
        </Menu>
      </MenuBar>
    </gui>

At the time this is being written, no KDE specification exists to guide the writing of the XML file, so I generally use an XML file stolen from another application as a guide. Note that the "internal" name of our action (clear) appears in the ToolBar and MenuBar sections of the XML file. When we run the application, we should expect a "Clear" entry to appear in the File menu, and a "Clear" button on the toolbar, and in fact that's what happens.

The second action we want to creat - Quit - is much easier. There are a number of pre-defined actions collected in KStandardAction. Using them requires only a single line of code:

    KStandardAction.quit (app.quit, self.actionCollection())

For our Quit action we can use the standard action quit. We just need to specify what slot it connects to (app.quit) and indicate the action collection to add it to.

Lastly, we need to tell KDE we're done defining things and it's time to actually build the GUI

    self.setupGUI(QSize (600, 400), KXmlGuiWindow.Default, os.path.join (sys.path [0], "kactionui.rc"))

This is more complicated than the setupGUI call in the previous tutorial. This call specifies an initial size for our application (QSize (600, 400)), a flag (which will usually be KXmlGuiWindow.Default for applications), and a location for our XML file. In this, the .rc file is located in the same directory as the program we're running, so the path to it can be found at sys.path [0], where Python inserts the path to the program being run.

Normally, KDE looks for XML files in well-known locations. If we had our XML file properly installed, we could still use setupGUI with no arguments, and KDE would find the XML file from our application name.

However, during development of an application, not all of the files are in their final locations, so we specify the file's location explicitly here.

If you run the application now, you can verify the new Clear menu entry and toolbar button, and type some text in the app and clear it using the button or menu item.

The only thing we haven't done is add a keyboard shortcut for our Clear action. This isn't difficult, but it involves some other considerations, so I've postponed that to the next tutorial - KAction2.