Hello World!

Your first window using KDE Frameworks

Abstract

Your first program shall greet the world with a friendly “Hello World!”. For that, we will use a KMessageBox and customize one of its buttons.

The Code

Hello World

All the code we need will be in one file, main.cpp. We’ll start simple and increment our file as we go further. Create it with the code below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <QApplication>
#include <KMessageBox>

int main (int argc, char *argv[])
{
    QApplication app(argc, argv);

    KGuiItem yesButton("Hello", QString(),
                        "This is a tooltip",
                        "This is a WhatsThis help text.");

    return KMessageBox::questionYesNo(0, "Hello World", "Hello", yesButton)
        == KMessageBox::Yes? EXIT_SUCCESS: EXIT_FAILURE;
}

First we need to create a QApplication object. It needs to be created exactly once and before any other KDE Framework or Qt object, as it’s the starting point for creating your application and thus required for other components, like Internationalization.

We’re going to create a popup box but we’re going to customise one of the buttons by creating a KGuiItem object. The first argument of the KGuiItem constructor is the text that will appear on the item (in our case, a button object to be used soon). Then we have the option to set an icon for the button, but for now we don’t want one so we can just give it a QString(), which is just a null QString. We then set the tooltip (what appears when you hover over an item), and finally the “What’s This?” text (accessed through right-clicking or Shift-F1).

Now that we have our item, we can create our popup. We call the KMessageBox::questionYesNo function which, by default, creates a message box with a “Yes” and a “No” button. The first argument is the parent widget of the KMessageBox , but since we are just using a ternary operator to ask whether our message box returns yes, we do not need to specify the parent, so we can use 0, nullptr or NULL instead. The second argument is the text that will appear inside the message box and above the buttons. The third is the caption shown in the window’s titlebar, and then we set the KGuiItem for (what would normally be) the “Yes” button to the KGuiItem yesButton we created.

About and Internationalization

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <QApplication>
#include <KMessageBox>
#include <KAboutData>
#include <KLocalizedString>

int main (int argc, char *argv[])
{
    QApplication app(argc, argv);
    KLocalizedString::setApplicationDomain("tutorial1");
    
    KAboutData aboutData(
                         // The program name used internally. (componentName)
                         QStringLiteral("tutorial1"),
                         // A displayable program name string. (displayName)
                         i18n("Tutorial 1"),
                         // The program version string. (version)
                         QStringLiteral("1.0"),
                         // Short description of what the app does. (shortDescription)
                         i18n("Displays a KMessageBox popup"),
                         // The license this code is released under
                         KAboutLicense::GPL,
                         // Copyright Statement (copyrightStatement = QString())
                         i18n("(c) 2021"),
                         // Optional text shown in the About box.
                         // Can contain any information desired. (otherText)
                         i18n("Some text..."),
                         // The program homepage string. (homePageAddress = QString())
                         QStringLiteral("http://example.com/"),
                         // The bug report email address
                         // (bugsEmailAddress = QLatin1String("submit@bugs.kde.org")
                         QStringLiteral("submit@bugs.kde.org"));

    aboutData.addAuthor(i18n("Name"), i18n("Author Role"), QStringLiteral("your@email.com"),
                         QStringLiteral("http://your.website.com"), QStringLiteral("OCS Username"));

    KAboutData::setApplicationData(aboutData);
    
    KGuiItem yesButton(i18n("Hello"), QString(),
                        i18n("This is a tooltip"),
                        i18n("This is a WhatsThis help text."));

    return KMessageBox::questionYesNo(0, i18n("Hello World"), i18n("Hello"), yesButton) 
        == KMessageBox::Yes? EXIT_SUCCESS: EXIT_FAILURE;
}

For your application to be localized, it must first be internationalized, that is, you must prepare your code for it to be localized later. For that, we start with a call to KLocalizedString::setApplicationDomain is required to properly set the translation catalog and must be done before everything else (except QApplication). After that, we can just start enveloping the relevant user-visible strings with i18n(). The non-user visible strings that should be kept as-is (that is, read only) should use a QStringLiteral. We’ll use those next with KAboutData .

More information on internalization can be found in the i18n tutorial.

KAboutData is a core KDE Frameworks component that stores information about an application, which can then be reused by many other KDE Frameworks components. We instantiate a new KAboutData object with its fairly complete default constructor and add author information. After all the required information has been set, we call KAboutData::setApplicationData to initialize the properties of the QApplication object.

Command line

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <QApplication>
#include <KMessageBox>
#include <KAboutData>
#include <KLocalizedString>
#include <QCommandLineParser>

int main (int argc, char *argv[])
{
    QApplication app(argc, argv);
    KLocalizedString::setApplicationDomain("tutorial1");
    
    KAboutData aboutData(
                         // The program name used internally. (componentName)
                         QStringLiteral("tutorial1"),
                         // A displayable program name string. (displayName)
                         i18n("Tutorial 1"),
                         // The program version string. (version)
                         QStringLiteral("1.0"),
                         // Short description of what the app does. (shortDescription)
                         i18n("Displays a KMessageBox popup"),
                         // The license this code is released under
                         KAboutLicense::GPL,
                         // Copyright Statement (copyrightStatement = QString())
                         i18n("(c) 2021"),
                         // Optional text shown in the About box.
                         // Can contain any information desired. (otherText)
                         i18n("Some text..."),
                         // The program homepage string. (homePageAddress = QString())
                         QStringLiteral("http://example.com/"),
                         // The bug report email address
                         // (bugsEmailAddress = QLatin1String("submit@bugs.kde.org")
                         QStringLiteral("submit@bugs.kde.org"));

    aboutData.addAuthor(i18n("Name"), i18n("Author Role"), QStringLiteral("your@email.com"),
                         QStringLiteral("http://your.website.com"), QStringLiteral("OCS Username"));

    KAboutData::setApplicationData(aboutData);

    QCommandLineParser parser;
    aboutData.setupCommandLine(&parser);
    parser.process(app);
    aboutData.processCommandLine(&parser);
    
    KGuiItem yesButton(i18n("Hello"), QString(),
                        i18n("This is a tooltip"),
                        i18n("This is a WhatsThis help text."));

    return KMessageBox::questionYesNo(0, i18n("Hello World"), i18n("Hello"), yesButton) 
        == KMessageBox::Yes? EXIT_SUCCESS: EXIT_FAILURE;
}

Then we come to QCommandLineParser . This is the class one would use to specify command line flags to, for example, open the program with a specific file. However, in this tutorial, we simply initialize it with the KAboutData object we created before so we can use the --version or --author flags that are provided by default by Qt.

We’re all done as far as the code is concerned. Now to build it and try it out.

Build

You want to use cmake for your build environment. You provide a file CMakeLists.txt, CMake uses this file to generate all Makefiles out of it. Learn more about why KDE uses CMake from Alexander Neundorf.

CMakeLists.txt

Create a file named CMakeLists.txt in the same directory as main.cpp with this content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
cmake_minimum_required(VERSION 3.0)

project (helloworld)

set(QT_MIN_VERSION "5.3.0")
set(KF5_MIN_VERSION "5.2.0")

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

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

# Find Qt modules
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
    Core    # QCommandLineParser, QStringLiteral
    Widgets # QApplication
)

# Find KDE modules
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
    CoreAddons      # KAboutData
    I18n            # KLocalizedString
    WidgetsAddons   # KMessageBox
)

feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

set(helloworld_SRCS main.cpp)
add_executable(helloworld ${helloworld_SRCS})
target_link_libraries(helloworld
    Qt5::Widgets
    KF5::CoreAddons
    KF5::I18n
    KF5::WidgetsAddons
)

install(TARGETS helloworld ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

The find_package() function locates the package that you ask it for (in this case ECM, Qt5, or KF5) and sets some variables describing the location of the package’s headers and libraries. ECM, or Extra CMake Modules, is required to import special CMake files and functions for building KDE applications.

Here we try to find the modules for Qt 5 and KDE Frameworks 5 required to build our tutorial. The necessary files are included by CMake so that the compiler can see them at build time. Minimum version numbers are set at the very top of CMakeLists.txt file for easier reference.

Next we create a variable called helloworld_SRCS using the set() function. In this case we simply set it to the name of our only source file.

Then we use add_executable() to create an executable called helloworld from the source files listed in our helloworld_SRCS variable. Afterwards, we link our executable to the necessary libraries using target_link_libraries() function. The line starting with install writes a default “install” target into the Makefile.

Make And Run

To compile, link and install your program, you must have several software installed, e.g. cmake, make and gcc-c++, and the Qt 5 and KDE Frameworks development files. To be sure you have everything, best follow this install guide.

While you can run CMake directly inside the source code directory itself, it is a best practice, and actually enforced in some KDE software, to use a separate build directory and run CMake from there:

mkdir build && cd build

You can invoke CMake and make manually:

cmake .. && make

And launch it with:

./helloworld