Qt Quick Controls - 文本编辑器
一个使用Qt Quick Controls 的富文本编辑器应用程序。
文本编辑器示例允许对 HTML、Markdown 或纯文本文件进行所见即所得的编辑。该应用程序有两种用户界面:一种适用于大屏幕,另一种是适用于小型触摸设备的简化用户界面。texteditor.cpp
包含main()
函数,该函数调用QFontDatabase::addApplicationFont() 添加图标字体。(FontLoader 是实现相同效果的另一种方法)。
桌面用户界面
桌面版是一个完整的文本编辑器,具有格式化文本、打开和保存 HTML、Markdown 和纯文本文件的功能。
在模型-视图-控制(MVC)设计模式中,控制层包括可执行的操作集。在Qt Quick Controls 中,Action 类型用于封装单个操作或命令。因此,我们从一组 Action 对象开始:
Action { id: openAction text: qsTr("&Open") shortcut: StandardKey.Open onTriggered: { if (textArea.textDocument.modified) discardDialog.open() else openDialog.open() } }
打开文件的Action 必须首先提示用户现有文档是否已更改,以避免丢失用户的更改。否则,它只是打开FileDialog ,该文件将在下文中进一步声明。
只有在有更改需要保存时,才会启用用于保存文件的Action :
Action { id: saveAction text: qsTr("&Save…") shortcut: StandardKey.Save enabled: textArea.textDocument.modified onTriggered: textArea.textDocument.save() }
只有选中某些文本时,才会启用用于复制选中文本的Action :
Action { id: copyAction text: qsTr("&Copy") shortcut: StandardKey.Copy enabled: textArea.selectedText onTriggered: textArea.copy() }
每个用于更改文本格式(如粗体、斜体和对齐方式)的操作都是checkable ,其布尔checked
状态与selected text 中的相关属性同步。由于声明式双向同步比较困难,我们使用onTriggered
脚本在操作激活时更改属性。cursorSelection 属性是 Qt XML 6.7 中的新特性,它使这一操作比原来简单得多。
Action { id: boldAction text: qsTr("&Bold") shortcut: StandardKey.Bold checkable: true checked: textArea.cursorSelection.font.bold onTriggered: textArea.cursorSelection.font = Qt.font({ bold: checked }) } Action { id: alignCenterAction text: qsTr("&Center") shortcut: "Ctrl+|" checkable: true checked: textArea.cursorSelection.alignment === Qt.AlignCenter onTriggered: textArea.cursorSelection.alignment = Qt.AlignCenter }
我们有一个包含Menus 和 MenuItems 层次结构的MenuBar 。每个MenuItem 只需绑定相关的action ,它封装了 UI 表示和实现。
menuBar: MenuBar { Menu { title: qsTr("&File") MenuItem { action: openAction } MenuItem { action: saveAction } MenuItem { action: saveAsAction } MenuItem { action: quitAction } } Menu { title: qsTr("&Edit") MenuItem { action: copyAction } ...
ToolBar 中重复使用了相同的Action 对象;但在这里,我们覆盖了每个 Action 的text 属性,以便从我们的图标字体中选择一个文本图标:
header: ToolBar { leftPadding: 8 Flow { id: flow width: parent.width Row { id: fileRow ToolButton { id: openButton text: "\uF115" // icon-folder-open-empty font.family: "fontello" action: openAction focusPolicy: Qt.TabFocus } ToolButton { id: saveButton text: "\uE80A" // icon-floppy-disk font.family: "fontello" action: saveAction focusPolicy: Qt.TabFocus } ToolSeparator { contentItem.visible: fileRow.y === editRow.y } } Row { id: editRow ToolButton { id: copyButton text: "\uF0C5" // icon-docs font.family: "fontello" focusPolicy: Qt.TabFocus action: copyAction } ...
文本编辑器的主要部分是TextArea ,位于Flickable 内:
Flickable { id: flickable flickableDirection: Flickable.VerticalFlick anchors.fill: parent ScrollBar.vertical: ScrollBar {} TextArea.flickable: TextArea { id: textArea textFormat: Qt.AutoText wrapMode: TextArea.Wrap focus: true selectByMouse: true persistentSelection: true ...
纵轴上有一个ScrollBar 。由于通过wrapMode 启用了单词包,因此我们不需要水平ScrollBar 。
使用TextArea.flickable 附加属性,当文本光标移出视口时(例如通过方向键或输入大量文本),TextArea 会滚动Flickable 以保持光标可见。
有一个上下文菜单;我们使用TapHandler 来检测右键单击并打开它:
TapHandler { acceptedButtons: Qt.RightButton onTapped: contextMenu.popup() }
上下文Menu 包含MenuItems ,它重复使用与主MenuBar 和ToolBar 相同的Action 对象。和以前一样,只需将action 与表示要执行操作的可重用操作绑定即可。不过,我们会覆盖每个菜单项的text ,以省略上下文菜单中的下划线助记符。
我们一直使用qsTr() 函数来翻译用户界面文本,这样无论最终用户的母语是什么,应用程序都能理解。
我们使用多种dialogs :
FileDialog { id: openDialog fileMode: FileDialog.OpenFile selectedNameFilter.index: 1 nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)", "Markdown files (*.md *.markdown)"] currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation) onAccepted: { textArea.textDocument.modified = false // we asked earlier, if necessary textArea.textDocument.source = selectedFile } } FileDialog { id: saveDialog fileMode: FileDialog.SaveFile nameFilters: openDialog.nameFilters currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation) onAccepted: textArea.textDocument.saveAs(selectedFile) } FontDialog { id: fontDialog onAccepted: textArea.cursorSelection.font = selectedFont } ColorDialog { id: colorDialog selectedColor: "black" onAccepted: textArea.cursorSelection.color = selectedColor } MessageDialog { title: qsTr("Error") id: errorDialog } MessageDialog { id : quitDialog title: qsTr("Quit?") text: qsTr("The file has been modified. Quit anyway?") buttons: MessageDialog.Yes | MessageDialog.No onButtonClicked: function (button, role) { if (role === MessageDialog.YesRole) { textArea.textDocument.modified = false Qt.quit() } } } MessageDialog { id : discardDialog title: qsTr("Discard changes?") text: qsTr("The file has been modified. Open a new file anyway?") buttons: MessageDialog.Yes | MessageDialog.No onButtonClicked: function (button, role) { if (role === MessageDialog.YesRole) openDialog.open() } }
一般来说,为每种用途声明单独的实例会更容易。我们有两个FileDialog 实例,分别用于打开和保存文件。在 Qt 6.7 中,通过TextDocument 中的新功能,这变得更加容易。
FontDialog 和ColorDialog 允许更改文本格式。(在 Markdown 格式中,没有表示特定字体和颜色选择的语法;但粗体、斜体和monospace 等字体特征会被保存)。在 HTML 格式中,所有格式都会被保存)。
我们有一个用于显示错误信息的MessageDialog ,还有两个用于在文件被修改时提示用户如何操作。
触摸用户界面
触摸用户界面是文本编辑器的简化版。它适用于屏幕尺寸有限的触摸设备。该示例使用文件选择器自动加载相应的用户界面。
运行示例
要从 Qt Creator,打开Welcome 模式,然后从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.