您现在的位置是:首页 > 文章详情

Qt 开源作品 | 小伙子,给你的 Linux 系统写个 Launcher 吧

日期:2021-05-11点击:429

今天给大家分享一下:如何用 Qt 实现一个 launcher (程序启动器)。

运行效果:

图片

github 链接:

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

代码很少,C++ 部分大约 100行代码。

下面是实现过程。

1. 创建 QML 应用

在 Qt Creator 依次点击:

-> File -> New File or Project

-> Applications -> Qt Quick Application

图片

然后一路点击 next 直到 finish 。

2. 解析配置文件

Linux 系统里安装过的应用,

都会在 /usr/share/applications 目录下有相应的配置文件,

用于说明如何启动该应用,如下:

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

以 bcompare.desktop 为例:

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

字段含义:

  • Name 字段是应用的名称,

  • Exec 字段是应用的启动命令,

  • Icon 字段是应用的图标名称,

解析配置文件:

// 文件: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);                  // 定位到 [Desktop Entry]         desktopFile.beginGroup(DESKTOP_ENTRY_STRING);         // 提取 app 信息         AppInfo app;         app.exec = desktopFile.value("Exec").toString().remove("\"").remove(QRegExp(" %."));         app.icon = desktopFile.value("Icon").toString();         app.name = desktopFile.value("Name").toString();         // 保存 app 信息         ret.append(QStringList{app.name, app.icon, app.exec});     }     return ret; } int main(int argc, char *argv[]) {     [...]     // 将解析到的 app 信息传递给 QML 前端     engine.rootContext()->setContextProperty("apps", apps());     [...] }

核心就是遍历某个目录下的所有文件,解析配置文件的工作则由 QSettings 负责。

运行效果:

// 打印出所有的 app 启动信息 exec:  "xpad"  icon:  "xpad"  name:  "Xpad" [...]

3. 实现整体布局

我们通过 SwipeView 来实现滑动翻页的功能,参考我之前的文章:

《Qt 官方示例 | 这几个 QML 版的 HelloWorld 你学会了吗?》

至于单独一页的布局,我们可以使用 Repeater 这个控件。

Repeater 可以帮我们生成重复的内容,这里我们规定一页最多显示 24 个 app。

通过 SwipeView + Repeater 实现布局:

// 文件: 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"                         }                     }                 }             }         }     }

第一个 Repeater 用于实现生成所有的页面,

第二个 Repeater 用于生成页面里的所有 APP 的图标,这里我们先用 Qt 的 logo 来代替真实的 APP 图标。

运行效果:

图片

这时候已经支持左右滑动了,但是还没填入 APP 信息。

4. 支持显示应用图标

在 main() 里,我们设置了一个名为 apps 的属性,它包含了所有 APP 的信息:

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

我们需要在前端界面中使用 APP 的图标替换掉 Qt logo。

显示 APP 图标:

// 文件:main.qml Grid {     [...]     Repeater {         model: page !== undefined ? page.length : 0         Column {             Image {                 property var app: page[index]                 // APP 图标                 source: "image://icons/" + app[1]                 [...]             }             Label {                 property var app: page[index]                 id: label                 // APP 的名称                 text: app[0]                 [...]             }         }     } }

改动非常少。

运行效果:

图片

这时仅支持显示图标,但是仍不支持鼠标选中。

5. 支持选中应用

选中应用需要添加对鼠标 hover 事件的处理。

当鼠标移动到某个图标上时,Qt 会捕获到鼠标 hover 事件,并传递给当前焦点所在的控件上。

我们将 APP 的界面代码抽取出来,单独放在 AppEntry.qml,使其成为一个独立的控件,

然后再在其中添加对鼠标 hover 事件的处理。

图标控件:AppEntry.qml

/// 文件:AppEntry.qml  Pane {     id: root     property var app     [...]     // 当鼠标移动到该图标时,发送信号 hovered()     signal hovered()     MouseArea {         [...]         onHoveredChanged: {             if (hovered) {                 root.hovered()             }         }     }     Column {         anchors.fill: parent         Image {             source: "image://icons/" + app[1]             [...]         }         Label {             [...]         }     } }

在 main.qml 中接受到 AppEntry 控件的 hovered 信号时,

需改变其背景色以提示用户已选中图标。

// 文件:main.qml Repeater {     model: page.length     AppEntry {         app: page[index]         [...]         // selected 改变时,背景色会变化         selected: swipeView.selectedIndex === index         onHovered: {             swipeView.select(index)         }         [...]     } }

运行效果:

图片

这是已经能显示选中状态了,但是仍无法启动应用。

6. 支持启动应用

在 Qt 里,可以使用 QProcess 来创建进程。

这里我们创建一个 QProcess 的子类用于运行 APP。

QProcess 的子类:

// 文件:process.cpp void Process::start(const QString &program, const QVariantList &arguments) {     [...]     QProcess::startDetached(program); } // 文件:process.h class Process : public QProcess {     Q_OBJECT public:     Process(QObject *parent = nullptr);     Q_INVOKABLE void start(const QString &program, const QVariantList &arguments = {}); }; // 文件:main.cpp int main(int argc, char *argv[]) {     // 将 Process 的实例传递给前端     engine.rootContext()->setContextProperty("proc", new Process(&engine)); }

前端处理点击事件:

// 文件:AppEntry.qml signal clicked() MouseArea {     [...]     onClicked: {         root.clicked()     } }

当用户点击图标时,AppEntry 控件会发出 clicked() 信号。

// 文件:main.qml AppEntry {     app: page[index]     [...]     // 主窗口启动 APP     onClicked: {         exec(app[2])     }     [...] } function exec(program) {     console.debug("Exec: " + program)     proc.start(program)     Qt.quit(); }

最后调用到 Process::start(),启动 APP。

运行效果:

图片


怎么样,你们学会了吗?

—— The End ——

推荐阅读:

专辑 | Linux 驱动开发

专辑 | Linux 系统编程

专辑 | 每天一点 C

专辑 | Qt 入门

想进交流群?

后台回复【加群】,我拉你进群。

原文链接:https://blog.51cto.com/u_13065153/2766537
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章