Qt open source works | young man, write a Launcher for your Linux system

Posted by billcoker on Thu, 10 Feb 2022 06:17:52 +0100

Today, I'd like to share with you how to implement a launcher (program launcher) with Qt.

Operation effect:

github link:

https://github.com/alamminsalo/qml-launcher

There is little code, and the C + + part is about 100 lines of code.

The following is the implementation process.

1. Create QML application

In Qt Creator, click:

-> File -> New File or Project

-> Applications -> Qt Quick Application

Then click next all the way until finish.

2. Parse the configuration file

Applications installed in Linux system,

There will be corresponding configuration files in / usr/share/applications directory,

It is used to explain how to start the application, as follows:

# ls -1X /usr/share/applications/
apport-gtk.desktop
apturl.desktop
arduino.desktop
audacity.desktop
bcompare.desktop
...

Bcompare Take desktop as an example:

[Desktop Entry]
Name=Beyond Compare
Exec=bcompare
Icon=bcompare
...

Field meaning:

  • The Name field is the Name of the application,

  • The Exec field is the start command of the application,

  • Icon field is the icon name of the application,

Resolve profile:

//File: main cpp

QVariantList apps()
{
    QVariantList ret;
    QDirIterator it(DESKTOP_FILE_SYSTEM_DIR, ...);

    while (it.hasNext()) {
        const auto filename = it.next();
        QSettings desktopFile(filename, QSettings::IniFormat);
        
        //Navigate to [Desktop Entry]
        desktopFile.beginGroup(DESKTOP_ENTRY_STRING);

        //Extract app information
        AppInfo app;
        app.exec = desktopFile.value("Exec").toString().remove("\"").remove(QRegExp(" %."));
        app.icon = desktopFile.value("Icon").toString();
        app.name = desktopFile.value("Name").toString();

        //Save app information
        ret.append(QStringList{app.name, app.icon, app.exec});
    }
    return ret;
}

int main(int argc, char *argv[])
{
    [...]
    //Pass the parsed app information to the QML front end
    engine.rootContext()->setContextProperty("apps", apps());
    [...]
}

The core is to traverse all files in a directory, and QSettings is responsible for parsing configuration files.

Operation effect:

//Print out all app startup information

exec:  "xpad" 
icon:  "xpad" 
name:  "Xpad"
[...]

3. Realize the overall layout

We use SwipeView to realize the function of sliding page turning. Refer to my previous article:

"Official example of Qt - have you learned these QML versions of HelloWorld?"

As for the layout of a single page, we can use the Repeater control.

Repeater can help us generate duplicate content. Here we specify that a page can display up to 24 app s.

Layout through SwipeView + Repeater:

//File: main qml

SwipeView {
        [...]
        property int selectedIndex: 0
        Repeater {
            id: pageRepeater
            model: appPages.length

            Item {
                property var page: appPages[index]
                Grid {
                    columns: 6
                    Repeater {
                        model: page.length

                        Image {
                            source: "qrc:/images/qtlogo.png"
                        }
                    }
                }
            }
        }
    }

The first Repeater is used to generate all pages,

The second Repeater is used to generate all APP icons in the page. Here, we first use Qt logo to replace the real APP icon.

Operation effect:

At this time, left-right sliding is supported, but the APP information has not been filled in.

4. Support the display of application icons

In main(), we set an attribute called apps, which contains the information of all apps:

engine.rootContext()->setContextProperty("apps", apps());

We need to replace Qt logo with APP icon in the front-end interface.

Display APP Icon:

//File: main qml

Grid {
    [...]
    Repeater {
        model: page !== undefined ? page.length : 0

        Column {
            Image {
                property var app: page[index]
                //APP Icon
                source: "image://icons/" + app[1]
                [...]
            }

            Label {
                property var app: page[index]
                id: label
                //APP name
                text: app[0]
                [...]
            }
        }
    }
}

Very few changes.

Operation effect:

At this time, only icons are supported, but mouse selection is still not supported.

5. Support selected applications

The selected application needs to add processing for mouse hover events.

When the mouse moves over an icon, Qt will capture the mouse hover event and pass it to the control where the current focus is located.

We extract the interface code of APP and put it separately in appentry QML, making it a separate control,

Then add the processing of mouse hover event.

Icon control: appentry qml

///File: appentry qml 

Pane {
    id: root
    property var app
    [...]

    //When the mouse moves to the icon, the signal {hovered() is sent
    signal hovered()
    MouseArea {
        [...]
        onHoveredChanged: {
            if (hovered) {
                root.hovered()
            }
        }
    }

    Column {
        anchors.fill: parent

        Image {
            source: "image://icons/" + app[1]
            [...]
        }

        Label {
            [...]
        }
    }
}

In main When the hovered signal of the AppEntry control is received in QML,

The background color needs to be changed to prompt the user that the icon has been selected.

//File: main qml

Repeater {
    model: page.length

    AppEntry {
        app: page[index]
        [...]
        //When selected , changes, the background color will change
        selected: swipeView.selectedIndex === index
        onHovered: {
            swipeView.select(index)
        }
        [...]
    }
}

Operation effect:

The selected status can be displayed, but the application cannot be started.

6. Support application startup

In Qt, you can use qpprocess to create processes.

Here we create a subclass of qpprocess to run APP.

Subclass of qpprocess:

//File: process cpp
void Process::start(const QString &program, const QVariantList &arguments)
{
    [...]
    QProcess::startDetached(program);
}

//File: process h
class Process : public QProcess
{
    Q_OBJECT

public:
    Process(QObject *parent = nullptr);

    Q_INVOKABLE void start(const QString &program, const QVariantList &arguments = {});
};

//File: main cpp
int main(int argc, char *argv[])
{
    //Pass the instance of # Process # to the front end
    engine.rootContext()->setContextProperty("proc", new Process(&engine));
}

Front end processing click event:

//File: appentry qml

signal clicked()
MouseArea {
    [...]
    onClicked: {
        root.clicked()
    }
}

When the user clicks the icon, the AppEntry control sends a clicked() signal.

//File: main qml

AppEntry {
    app: page[index]
    [...]
    //Main window start APP
    onClicked: {
        exec(app[2])
    }
    [...]
}

function exec(program) {
    console.debug("Exec: " + program)
    proc.start(program)
    Qt.quit();
}

Finally call to Process::start(), start APP.

Operation effect:

How's it going? Have you learned it?

- The End -

Recommended reading:

Album | Linux driver development

Album | Linux system programming

Album | one o'clock every day C

Album | introduction to Qt

Want to join the communication group?

Backstage reply [add group], I'll pull you into the group.

Topics: Linux Qt