主窗口是应用程序的基础界面容器,负责承载应用的主要内容和功能。以下是 QML 中实现主窗口的完整方案。
1. 基础主窗口实现
使用 ApplicationWindow (推荐)
qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
ApplicationWindow {
id: mainWindow
title: "我的应用"
width: 1024
height: 768
visible: true
color: "#f5f5f5"
// 菜单栏
menuBar: MenuBar {
Menu {
title: "文件"
MenuItem { text: "新建"; onTriggered: createNew() }
MenuItem { text: "打开"; onTriggered: openFile() }
MenuSeparator {}
MenuItem { text: "退出"; onTriggered: Qt.quit() }
}
Menu {
title: "帮助"
MenuItem { text: "关于"; onTriggered: aboutDialog.open() }
}
}
// 状态栏
footer: Label {
text: "就绪"
padding: 5
}
// 主内容区域
StackView {
id: contentStack
anchors.fill: parent
initialItem: HomePage {}
}
// 关于对话框
AboutDialog { id: aboutDialog }
}
2. 主窗口功能扩展
窗口状态管理
qml
ApplicationWindow {
// ...其他属性...
// 窗口状态持久化
property var windowSettings: Settings {
category: "Window"
property alias x: mainWindow.x
property alias y: mainWindow.y
property alias width: mainWindow.width
property alias height: mainWindow.height
property alias visibility: mainWindow.visibility
}
// 最小化到系统托盘
onVisibilityChanged: {
if (visibility === Window.Minimized && systemTray.supported) {
hide()
}
}
SystemTrayIcon {
id: systemTray
visible: true
icon.source: "qrc:/icons/app.png"
onActivated: {
mainWindow.show()
mainWindow.raise()
mainWindow.requestActivate()
}
}
}
多文档界面(MDI)实现
qml
ApplicationWindow {
// ...其他属性...
// MDI区域
MdiArea {
id: mdiArea
anchors.fill: parent
}
// 新建文档
function createDocument() {
var doc = Qt.createQmlObject(`
import QtQuick 2.15
MdiWindow {
title: "文档 ${mdiArea.windows.length + 1}"
width: 400; height: 300
}`, mdiArea)
mdiArea.addWindow(doc)
}
}
窗口
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: mainWindow
width: 800
height: 600
visible: true
title: "主窗口"
Button {
text: "打开子窗口"
onClicked: subWindow.show()
}
}
3. 主窗口样式定制
自定义标题栏
qml
ApplicationWindow {
flags: Qt.Window | Qt.FramelessWindowHint
// 自定义标题栏
Rectangle {
id: titleBar
width: parent.width
height: 40
color: "#3498db"
DragHandler {
target: null
onActiveChanged: if (active) mainWindow.startSystemMove()
}
Text {
text: mainWindow.title
color: "white"
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
}
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 5
Button {
width: 40; height: 40
text: "━"
flat: true
onClicked: mainWindow.visibility = Window.Minimized
}
Button {
width: 40; height: 40
text: "□"
flat: true
onClicked: mainWindow.visibility === Window.Maximized ?
mainWindow.showNormal() : mainWindow.showMaximized()
}
Button {
width: 40; height: 40
text: "✕"
flat: true
onClicked: mainWindow.close()
}
}
}
// 内容区域
Item {
anchors.top: titleBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
// 主内容...
}
}
4. 主窗口与子窗口通信
使用信号/槽机制
qml
// MainWindow.qml
ApplicationWindow {
signal openDocumentRequested(string filePath)
function handleDocumentOpened(content) {
// 处理打开的文件内容
}
}
// DocumentWindow.qml
Window {
signal documentSaved(string content)
Button {
text: "保存到主窗口"
onClicked: documentSaved("文档内容...")
}
}
// 在主窗口创建文档窗口时连接信号
function createDocumentWindow() {
var docWin = Qt.createComponent("DocumentWindow.qml").createObject(mainWindow)
docWin.documentSaved.connect(mainWindow.handleDocumentOpened)
}
使用单例对象
qml
// AppManager.qml
pragma Singleton
import QtQuick 2.15
QtObject {
property var mainWindow
property var currentDocument
function showMessage(text) {
if (mainWindow) mainWindow.statusBar.showMessage(text)
}
}
// 在主窗口初始化时注册
ApplicationWindow {
Component.onCompleted: AppManager.mainWindow = this
}
5. 主窗口最佳实践
-
布局结构:
-
使用
ApplicationWindow
作为根元素 -
合理组织菜单栏、工具栏、状态栏和内容区域
-
考虑使用
StackView
或SwipeView
管理多页面
-
-
性能优化:
-
延迟加载不常用的界面部分
-
使用
Loader
动态加载资源密集型组件 -
避免在窗口组件中直接包含大量元素
-
-
用户体验:
-
实现窗口状态持久化(位置、大小)
-
处理多显示器环境
-
支持高DPI缩放
-
-
跨平台考虑:
-
适配不同操作系统的窗口管理习惯
-
处理平台特定的菜单和快捷键
-
考虑移动端和桌面端的差异
-
7. 完整主窗口示例
qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import Qt.labs.settings 1.0
ApplicationWindow {
id: appWindow
title: "文本编辑器"
minimumWidth: 800
minimumHeight: 600
visible: true
// 持久化窗口状态
Settings {
category: "Window"
property alias x: appWindow.x
property alias y: appWindow.y
property alias width: appWindow.width
property alias height: appWindow.height
property alias visibility: appWindow.visibility
}
// 菜单栏
menuBar: AppMenuBar {}
// 工具栏
header: ToolBar {
contentHeight: 40
Row {
spacing: 5
anchors.verticalCenter: parent.verticalCenter
ToolButton { action: newFileAction }
ToolButton { action: openFileAction }
ToolSeparator {}
ToolButton { action: saveAction }
}
}
// 状态栏
footer: StatusBar {
Label { id: statusLabel; text: "就绪" }
}
// 主内容区域
SplitView {
anchors.fill: parent
// 导航面板
NavigationPanel {
SplitView.preferredWidth: 200
}
// 编辑区域
EditorArea {
id: editor
SplitView.fillWidth: true
}
}
// 动作定义
Action {
id: newFileAction
text: "新建"
shortcut: StandardKey.New
onTriggered: editor.newFile()
}
// 其他动作...
// 初始化
Component.onCompleted: {
if (Qt.platform.os === "windows") {
appWindow.color = "#f0f0f0"
}
}
}
8、窗口的静态创建与动态创建
在 QML 中,窗口可以通过静态和动态两种方式创建,各有其适用场景和优缺点。以下是两种方式的详细对比和实现方法。
1). 静态创建窗口
静态创建是指在 QML 文件中直接声明窗口组件。
// MainWindow.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: mainWindow
width: 800
height: 600
visible: true
title: "主窗口"
Button {
text: "打开子窗口"
onClicked: subWindow.show()
}
}
// 静态创建的子窗口
Window {
id: subWindow
width: 400
height: 300
title: "静态子窗口"
}
2). 动态创建窗口
动态创建是指在运行时根据需要创建窗口实例。
// MainWindow.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: mainWindow
width: 800
height: 600
visible: true
title: "主窗口"
Button {
text: "动态创建窗口"
onClicked: {
// 方法1:使用createObject
var component = Qt.createComponent("DynamicWindow.qml")
if (component.status === Component.Ready) {
var window = component.createObject(mainWindow, {
title: "动态窗口"
})
window.show()
}
// 方法2:使用createWindow (Qt 5.15+)
// Qt.createQmlObject('import QtQuick.Window 2.15; Window {}',
// mainWindow, "dynamicWindow")
}
}
}
// DynamicWindow.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 400
height: 300
color: "lightblue"
Component.onCompleted: console.log("动态窗口已创建")
onClosing: destroy() // 关闭时销毁窗口
}