Pages
Developer Documentation
On this page

Using actions

Introduction

This step introduces actions: a single way of offering the user something to do in your program.

For example, to let the user of our main window clear the text area with a toolbar button, a File menu entry or a keyboard shortcut, one QAction does it all.

QActions

A QAction holds everything about an action: its text, icon and shortcuts. Through signals and slots, a function elsewhere in your program runs whenever the action is triggered (for example when its menu entry is clicked).

QActions are mostly used in QMenus in a QMenuBar, in a QToolBar, or in a context menu.

The code

main.cpp

We will install the user interface's .rc file for the component texteditor, so change the names in main.cpp accordingly. The .rc file is explained at the end of this step.

    // ...
    KLocalizedString::setApplicationDomain("texteditor");

    KAboutData aboutData(
            u"texteditor"_s,
    // ...

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <KXmlGuiWindow>

class KTextEdit;

class MainWindow : public KXmlGuiWindow
{
public:
    explicit MainWindow(QWidget *parent = nullptr);

private:
    KTextEdit *textArea;
    void setupActions();
};

#endif // MAINWINDOW_H

Only a function void setupActions() was added; it sets up the actions.

mainwindow.cpp

#include <QApplication>
#include <QAction>
#include <KTextEdit>
#include <KLocalizedString>
#include <KActionCollection>
#include <KStandardAction>
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) : KXmlGuiWindow(parent)
{
    textArea = new KTextEdit();
    setCentralWidget(textArea);
    setupActions();
}

void MainWindow::setupActions()
{
    using namespace Qt::Literals::StringLiterals;

    QAction *clearAction = new QAction(this);
    clearAction->setText(i18n("&Clear"));
    clearAction->setIcon(QIcon::fromTheme(u"document-new-symbolic"_s));
    actionCollection()->addAction(u"clear"_s, clearAction);
    actionCollection()->setDefaultShortcut(clearAction, Qt::CTRL | Qt::Key_L);
    connect(clearAction, &QAction::triggered, textArea, &KTextEdit::clear);

    KStandardAction::quit(qApp, &QCoreApplication::quit, actionCollection());

    setupGUI(Default, u"texteditorui.rc"_s);
}

Explanation

This builds on the KXmlGuiWindow code of the main window. Most changes are in mainwindow.cpp. An important one is that the constructor now calls setupActions() instead of setupGUI(): setupActions() creates the actions and then calls setupGUI() itself.

Creating the QAction

First include the QAction header and create one:

#include <QAction>
...
QAction *clearAction = new QAction(this);

Setting its properties

Now set the action's properties. QAction::setText() sets the text shown in the menu, and with the icon in the toolbar, depending on the widget style (beside or below the icon) and settings (whether to show text at all).

clearAction->setText(i18n("&Clear"));

The text goes through i18n(), so it can be translated, as described in Hello World (see also KI18n).

The ampersand (&) marks the letter that is the action's keyboard accelerator. When the user opens a menu and presses Alt, that letter is underlined: here the user presses Alt+C to clear the text area while the File menu is open.

The ampersand also helps translation: in languages that don't use the Latin alphabet, such as Japanese (where "copy" is コピー), it tells translators whether to add the Latin letter in parentheses, so users keep the same accelerator key whatever the translation.

Icon

An action shown in a toolbar should have an icon, which may also appear next to it in menus, depending on the widget style. QIcon::fromTheme() gets the icon document-new-symbolic from the user's icon theme (SonicDE's Silver theme has it), and QAction::setIcon() gives it to clearAction:

clearAction->setIcon(QIcon::fromTheme(u"document-new-symbolic"_s));

Adding it to the collection

For KXmlGui (explained below) to use the action, it must be in the application's action collection.

Our KXmlGuiWindow implements the KXMLGUIClient interface, so KXMLGUIClient::actionCollection() returns its actions as a KActionCollection, with convenience functions such as KActionCollection::addAction():

actionCollection()->addAction(u"clear"_s, clearAction);

This adds clearAction to the collection under the name clear. KXmlGui uses the name to refer to the action, so it is internal and never translated.

Keyboard shortcuts

KActionCollection::setDefaultShortcut() gives the action a default shortcut, Ctrl+L:

actionCollection()->setDefaultShortcut(clearAction, Qt::CTRL | Qt::Key_L);

The keys are listed in Qt's Qt::Key enum. Combine modifier and key with |.

Connecting the action

The action is set up; now connect it to something useful. Here it clears the text area, so we connect it to the clear() slot of the KTextEdit:

connect(clearAction, &QAction::triggered, textArea, &KTextEdit::clear);

QObject::connect() is Qt's most useful magic. The first argument is the object that sends the signal, clearAction; the second is the signal, triggered(); the third is the object that receives it, textArea; and the last is the slot that is called, clear(). Read it as "when this signal fires, run that slot".

Qt's documentation on signals and slots explains more. Signals and slots are essential in Qt applications; understanding them is highly recommended.

KStandardAction

For actions that appear in almost every application, such as Quit, Save and Open, KStandardAction (from sonic-frameworks-settings-ui, the ConfigWidgets component) provides ready-made actions.

They are very simple to use. Include <KStandardAction>, then give it what the action should do and the action collection to add it to:

KStandardAction::quit(qApp, &QCoreApplication::quit, actionCollection());

This calls QCoreApplication::quit() when the action is triggered. qApp is a pointer to the application object created in main().

The result is an action with the right icon, text and shortcut, which is even added to the File menu automatically.

Adding the action to menus and toolbars

The "Clear" action exists now, but isn't in any menu or toolbar yet. We use KXmlGui for that, which also makes toolbars movable for you.

Your own help menu

The Help menu is standardized to make life easier for developers and users. To make your own, see "Using your own about-application dialog box" in the KHelpMenu documentation.

XMLGUI

setupGUI() of KXmlGuiWindow builds the user interface with the KXmlGui system, from an XML description of the interface.

The XML file is named appnameui.rc, where appname is the component name set in KAboutData, here texteditor. So our file is texteditorui.rc, in the same folder as the other files. Where it is installed is up to CMake.

appnameui.rc files

The interface is described in XML, so the file must follow strict rules. This tutorial doesn't go into depth.

texteditorui.rc

<?xml version="1.0" encoding="UTF-8"?>
<gui name="texteditor"
     version="1"
     xmlns="https://www.kde.org/standards/kxmlgui/1.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="https://www.kde.org/standards/kxmlgui/1.0
                         https://www.kde.org/standards/kxmlgui/1.0/kxmlgui.xsd">
  <MenuBar>
    <Menu name="file" >
      <Action name="clear" />
    </Menu>
    <Menu >
      <text>A&amp;nother Menu</text>
      <Action name="clear" />
    </Menu>
  </MenuBar>

  <ToolBar name="mainToolBar" >
    <text>Main Toolbar</text>
    <Action name="clear" />
  </ToolBar>
</gui>

The <ToolBar> tag describes a toolbar, the bar of icons usually at the top of the window. This one has the unique name mainToolBar and, with <text>, the name "Main Toolbar" shown to users. The <Action> tag adds the clear action; its name is the name given to the action collection with KActionCollection::addAction() in mainwindow.cpp.

The action can also be in the menu bar: here it is added to the File menu of the MenuBar the same way, and to a second menu.

If you change the .rc file after installing it, increase the version attribute of the <gui> tag, so the user's saved copy is updated. It must be an integer: a decimal value doesn't work, and you get no warning.

Warning: The version attribute must always be an integer.

How the code and the .rc file work together: menus are created automatically and should have a <text> child unless they are standard menus. Actions must be created in code and added to actionCollection() with the same name as in the .rc file. Actions can be hidden or disabled; menus can't.

CMake

Finally, texteditorui.rc must be installed where it can be found; it can't stay in the source folder. So the project now has to be installed, unlike in the previous steps.

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)

project(texteditor)

set(QT_MIN_VERSION "6.6.0")
set(KF_MIN_VERSION "6.0.0")

find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)

include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(FeatureSummary)

find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
    Core    # QCommandLineParser, QStringLiteral
    Widgets # QApplication, QAction
)

find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS
    CoreAddons      # KAboutData
    I18n            # KLocalizedString
    XmlGui          # KXmlGuiWindow, KActionCollection
    TextWidgets     # KTextEdit
    ConfigWidgets   # KStandardActions
)

add_executable(texteditor)

target_sources(texteditor
    PRIVATE
    main.cpp
    mainwindow.cpp
)

target_link_libraries(texteditor
    Qt6::Widgets
    KF6::CoreAddons
    KF6::I18n
    KF6::XmlGui
    KF6::TextWidgets
    KF6::ConfigWidgets
)

install(TARGETS texteditor ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES texteditorui.rc DESTINATION ${KDE_INSTALL_KXMLGUIDIR}/texteditor)

This is almost the same as the previous file, plus the ConfigWidgets framework for KStandardAction and two lines at the end that say where to install: the texteditor program goes where programs go (${KDE_INSTALL_TARGETS_DEFAULT_ARGS}), and texteditorui.rc into the component's folder in ${KDE_INSTALL_KXMLGUIDIR}.

Running the application

Build and install it as in Hello World, then run:

texteditor

In Hello World we installed with --install-prefix:

cmake -B build/ --install-prefix ~/.local
cmake --build build/ --parallel
cmake --install build/

That creates a /usr-like layout in ~/.local: the program goes to ~/.local/bin/texteditor and the .rc file to ~/.local/share/kxmlgui5/texteditor/texteditorui.rc (KDE_INSTALL_KXMLGUIDIR is the kxmlgui5 data folder).

Next: Saving and loading.

Adapted for SonicDE from develop.kde.org/docs/getting-started/kxmlgui/using_actions/ by Alexander Lohnau, Andrew Shark, Arpan Patelia, Bharadwaj Raju, Carl Schwan, Claudio Cambra, Frederik Schwarzer, Mikhail Zolotukhin, Mladen Mirilović, Mouad Benali, Nicolas Fella, Snehit Sah, Thiago Sueto, Tobias Fella, licensed under CC BY-SA 4.0.

Edit this page on GitHub

We use cookies to personalize content and to analyze our traffic. Please decide if you are willing to accept cookies from our website.