Qt 开源作品 | 小伙子,给你的 Linux 系统写个 Launcher 吧
今天给大家分享一下:如何用 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 为例: [DesktopEntry] Name=BeyondCompare Exec=bcompare Icon=bcompare ... 字段含义: Name 字段是应用的名称, Exec 字段是应用的启动命令, Icon 字段是应用的图标名称, 解析配置文件: //文件:main.cpp QVariantListapps() { QVariantListret; QDirIteratorit(DESKTOP_FILE_SYSTEM_DIR,...); while(it.hasNext()){ constautofilename=it.next(); QSettingsdesktopFile(filename,QSettings::IniFormat); //定位到[DesktopEntry] desktopFile.beginGroup(DESKTOP_ENTRY_STRING); //提取app信息 AppInfoapp; 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}); } returnret; } intmain(intargc,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{ [...] propertyintselectedIndex:0 Repeater{ id:pageRepeater model:appPages.length Item{ propertyvarpage: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{ propertyvarapp:page[index] //APP图标 source:"image://icons/"+app[1] [...] } Label{ propertyvarapp:page[index] id:label //APP的名称 text:app[0] [...] } } } } 改动非常少。 运行效果: 这时仅支持显示图标,但是仍不支持鼠标选中。 5. 支持选中应用 选中应用需要添加对鼠标 hover 事件的处理。 当鼠标移动到某个图标上时,Qt 会捕获到鼠标 hover 事件,并传递给当前焦点所在的控件上。 我们将 APP 的界面代码抽取出来,单独放在 AppEntry.qml,使其成为一个独立的控件, 然后再在其中添加对鼠标 hover 事件的处理。 图标控件:AppEntry.qml ///文件:AppEntry.qml Pane{ id:root propertyvarapp [...] //当鼠标移动到该图标时,发送信号hovered() signalhovered() 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 voidProcess::start(constQString&program,constQVariantList&arguments) { [...] QProcess::startDetached(program); } //文件:process.h classProcess:publicQProcess { Q_OBJECT public: Process(QObject*parent=nullptr); Q_INVOKABLEvoidstart(constQString&program,constQVariantList&arguments={}); }; //文件:main.cpp intmain(intargc,char*argv[]) { //将Process的实例传递给前端 engine.rootContext()->setContextProperty("proc",newProcess(&engine)); } 前端处理点击事件: //文件:AppEntry.qml signalclicked() MouseArea{ [...] onClicked:{ root.clicked() } } 当用户点击图标时,AppEntry 控件会发出 clicked() 信号。 //文件:main.qml AppEntry{ app:page[index] [...] //主窗口启动APP onClicked:{ exec(app[2]) } [...] } functionexec(program){ console.debug("Exec:"+program) proc.start(program) Qt.quit(); } 最后调用到 Process::start(),启动 APP。 运行效果: 怎么样,你们学会了吗? —— The End —— 推荐阅读: 专辑 | Linux 驱动开发 专辑 | Linux 系统编程 专辑 | 每天一点 C 专辑 | Qt 入门 想进交流群? 后台回复【加群】,我拉你进群。