介绍一个QML的UI库,国人编写,作者也耍知乎。这个UI库确实好用,但是教程基本等于无,个人在使用中顺便记录一下学习内容。这玩意儿也有Pyside6的版本,有需要的可以查看PySide6-FluentUI-QML。
FluentUI库地址github.com/zhuzichu520/FluentUI
预览程序,可以在上面查看大部分组件
安装
浏览项目的Github页,注意有如下前置需求。
有缺少的可以在Qt目录下的MaintenanceTool.exe里进行安装。
- Qt Core, Qt Quick, Qt QML, Qt ShaderTool, Qt 5 Compatibility Module. (必备)
- Qt LinguistTool (optional,for translations)
- Tips: 在 Qt安装目录\版本\编译环境类型\bin 里
- 例如:F:\Qt\6.5.2\mingw_64\bin
- Qt Svg (optional, however essential for Qt 5)
确认前置条件满足后,开始安装,具体步骤参考:
FluentUI:如何在新项目中使用?_哔哩哔哩_bilibiliwww.bilibili.com/video/BV1ek4y1N7r8/编辑
这里要注意git clone有些坑,作者的库里有引用别的库的,clone过程使用如下
cd source
git clone --recursive https://github.com/zhuzichu520/FluentUI.git
cd FluentUI
完成后的FluentUI源码目录
注意检查完成后的framelesshelper与zxing-cpp可能是空的。我写这篇文章的时候,作者的引用的framelesshelper仍然处于NotFound状态, 可能需要直接去clone再放进此目录下。
删除framelesshelper与zxing-cpp文件夹,直接clone:
git clone --recursive https://github.com/zhuzichu520/framelesshelper.git
git clone https://github.com/zhuzichu520/zxing-cpp.git
完成安装后,Qt目录下会有QML模块生成的。路径类似F:\Qt\6.5.2\mingw_64\qml\FluentUI
路径中的版本和编译环境类型来源于图中
第一个程序
- 新建一个Qt Quick Application,为了方便可以命名为Tutorial1-FirstWindow
2. 选择前面用来编译FluentUI的Qt版本
3. 如果有选择项,构建方式选cmake而不是qmake或者qbs
新建项目完成后
4. 把之前的FluentUI文件夹丢到项目根目录下
5. 修改CMakeLists.txt, 改完之后类似上面的构建视频的cmakelist
cmake_minimum_required(VERSION 3.16)# Tutorial1-FirstWindow是项目名, 修改为自己的项目名
project(Tutorial1-FirstWindow VERSION 0.1 LANGUAGES CXX)set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)set(FLUENTUI_BUILD_EXAMPLES OFF)
set(FLUENTUI_BUILD_FRAMELESSHEPLER OFF)
find_package(FluentUI)
find_package(Qt6 6.4 REQUIRED COMPONENTS Quick)qt_standard_project_setup()qt_add_executable(appTutorial1-FirstWindowmain.cpp
)qt_add_qml_module(appTutorial1-FirstWindowURI Tutorial1-FirstWindowVERSION 1.0QML_FILES Main.qml
)# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(appTutorial1-FirstWindow PROPERTIES
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appTutorial1-FirstWindowMACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}MACOSX_BUNDLE TRUEWIN32_EXECUTABLE TRUE
)target_link_libraries(appTutorial1-FirstWindowPRIVATE Qt6::Quick
)include(GNUInstallDirs)
install(TARGETS appTutorial1-FirstWindowBUNDLE DESTINATION .LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
6. 修改Main.qml代码:
import QtQuick
import QtQuick.Window
// 注意这里,如果报错的话可能是前面构建FluentUI失败。检查这种路径F:\Qt\6.5.2\mingw_64\qml\FluentUI
import FluentUIWindow {width: 640height: 480visible: truetitle: qsTr("Hello World")FluFilledButton {width: 100height: 50anchors.centerIn: parenttext: "HelloWorld"}}
FluFilledButton
使用FluApp与FluWindow
- QtCreator右键项目选择添加新文件->Qt Resource File->命名resource
- 在项目根目录下新建文件夹qml
- qml文件夹内新建文件AppMainWindow.qml
- 修改AppMainWindow.qml
import QtQuick 2.15
import FluentUIFluWindow {id: mainWindow// 避免双屏情景下的宽度溢出minimumWidth: Screen.width * 0.8minimumHeight: Screen.desktopAvailableHeight * 0.8visible: truetitle: "Helloworld"//appBar: undefined}
- 右键resource.qrc->添加现有文件->AppMainWindow.qml
创建完成后的项目
5. 右键resource.qrc下的AppMainWindow-> Copy URL "qrc:/qml/AppMainWindow.qml"
6. 修改Main.qml
import QtQuick
import QtQuick.Window
import FluentUIWindow {id: appflags: Qt.SplashScreen// 一定要是falsevisible: falseComponent.onCompleted: {// 初始化FluApp,实质是记录了app这个根组件实例用以获取QQmlEngine// 然后从engine中以initialRoute文本找到用户定义的组件.qml(在这里是AppMainWindow.qml)// 实例化AppMainWindow,在窗口实例表中对照是否已有相同窗口存在,若存在则会判断launchMode// 所以AppMainWindow要继承自FluWindow, 否则必须自行添加Property: _pageRegister 、 argument、 _route、 launchModeFluApp.init(app)// 相关枚举大部分都在Def.h里FluTheme.darkMode = FluThemeType.Light// 开启动画FluTheme.enableAnimation = true// 开启文本本地渲染FluTheme.nativeText = true// 切换主题色// FluTheme.primaryColor = FluColors.Orange// 路由表FluApp.routes = {"/": "qrc:/qml/AppMainWindow.qml"}// 初始化路径FluApp.initialRoute = "/"FluApp.run()}
}
Ctrl+R运行
FluWindow
图中的FluWindow拥有一个默认的FluAppBar,这个玩意儿是使用Loader动态载入的Component。我遇到过一个bug就是因为window的appbar还没有载入完成,我的canvas组件就依据appBar进行paint导致页面错误。所以可以自定义一个appBar。
这个appBar其实是有与FramelessHelper联动的,我的工作机上FramelessHelper时好时坏,就不无人子弟了。
刚刚AppMainWindow.qml里,取消这一行的注释:
appBar: undefined
后面再加上新的AppBar,整个文件内容:
import QtQuick 2.15
import FluentUIFluWindow {id: mainWindow// 避免双屏情景下的宽度溢出minimumWidth: Screen.width * 0.8minimumHeight: Screen.desktopAvailableHeight * 0.8visible: truetitle: "Tutorial1-FirstWindow"appBar: undefined// 窗口标题栏FluAppBar {id: title_bartitle: mainWindow.title// 可以在resource.qrc中添加ico,把url复制过来,程序左上角就有图标了// icon:"qrc:/example/res/image/favicon.ico"anchors {top: parent.topleft: parent.leftright: parent.right}showDark: truedarkText: "Dark Mode"}
}
这里点了右上角的dark mode会切换黑/白模式
关闭窗口提示
在AppMainWindow.qml中添加代码
// 退出软件确认提示框FluContentDialog {id: dialog_closetitle: "退出"message: "确定要退出程序吗?"negativeText: "最小化"buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.NeutralButton| FluContentDialogType.PositiveButtononNegativeClicked: {mainWindow.hide()}positiveText: "退出"neutralText: "取消"onPositiveClicked: {FluApp.exit()}}
onPositiveClicked这里,如果是1.6.0版本之前的FluentUI,需要写成
mainWindow.deleteWindow()FluApp.closeApp()
现在Ctrl+R运行程序,点击右上角关闭窗口,发现并没有什么不一样
我们查看FluWindow的代码里有:
Connections{target: windowfunction onClosing(event){closeListener(event)}}property var closeListener: function(event){if(closeDestory){destoryOnClose()}else{visible = falseevent.accepted = false}}
我们查看QML的Window 文档会发现
closing(CloseEvent close)
This signal is emitted when the user tries to close the window.
This signal includes a close parameter. The close.accepted property is true by default so that the window is allowed to close; but you can implement an onClosing handler and set close.accepted = false if you need to do something else before the window can be closed.
所以FluWindow的onClosing已经被监听了,会调用closeListener, 我们直接用closeListener就行。
closeListener:function(event){// 打开关闭确认 弹窗dialog_close.open()// 取消关闭先event.accepted = false}
完整代码
import QtQuick 2.15
import FluentUIFluWindow {id: mainWindow// 避免双屏情景下的宽度溢出minimumWidth: Screen.width * 0.8minimumHeight: Screen.desktopAvailableHeight * 0.8visible: truetitle: "Tutorial1-FirstWindow"appBar: undefinedcloseListener: function (event) {dialog_close.open()// 取消窗口关闭event.accepted = false}// 窗口标题栏FluAppBar {id: title_bartitle: mainWindow.title// icon:"qrc:/example/res/image/favicon.ico"anchors {top: parent.topleft: parent.leftright: parent.right}showDark: truedarkText: "Dark Mode"}// 退出软件确认提示框FluContentDialog {id: dialog_closetitle: "退出"message: "确定要退出程序吗?"negativeText: "最小化"buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.NeutralButton| FluContentDialogType.PositiveButtononNegativeClicked: {mainWindow.hide()}positiveText: "退出"neutralText: "取消"onPositiveClicked: {FluApp.exit()}}
}