QT Demo 之 text

学习了MouseArea,我们继续选择一个基本的组件进行学习,这次我们学习text的Demo。
text的Demo位于F:\Qt\Qt5.3.2\Examples\Qt-5.3\quick\text目录。通过text.qmlproject文件我们了解,该Demo的mainFile是text.qml。

Item {height: 480 width: 320 LauncherList { id: ll anchors.fill: parent Component.onCompleted: { addExample("Hello", "An Animated Hello World", Qt.resolvedUrl("fonts/hello.qml")); addExample("Fonts", "Using various fonts with a Text element", Qt.resolvedUrl("fonts/fonts.qml")); addExample("Available Fonts", "A list of your available fonts",Qt.resolvedUrl("fonts/availableFonts.qml")); addExample("Banner", "Large, scrolling text", Qt.resolvedUrl("fonts/banner.qml")); addExample("Img tag", "Embedding images into text", Qt.resolvedUrl("imgtag/imgtag.qml")); addExample("Text Layout", "Flowing text around items", Qt.resolvedUrl("styledtext-layout.qml")); } } }

从代码来看,该Example主界面是一个LauncherList,其中包含6个子元素,分别从6个方面演示text的操作。
先看一下程序运行的效果图:
QT Demo 之 text
文章图片

LauncherList简述 LauncherList是一个自定义的容器,具体实现是在qrc:/shared/LauncherList.qml文件中,其自身的注释说明如下:
//model is a list of {"name":"somename", "url":"file:///some/url/mainfile.qml"}
//function used to add to model A) to enforce scheme B) to allow Qt.resolveUrl in url assignments

此处我们知道LaunchList是一个可以添加name,description和url的可点击栏即可,后面有机会再详细分析LaunchList。

在使用LauncherList时,添加List元素是在Component.onCompleted:{}响应函数中添加的,针对Component,官方说明如下:
Components are reusable, encapsulated QML types with well-defined interfaces.
Components are often defined by component files - that is, .qml files.

而completed()信号的以及onCompleted响应函数的说明如下:
completed()
Emitted after the object has been instantiated. This can be used to execute script code at startup, once the full QML environment has been established.The corresponding handler is onCompleted.


因此,我们了解到在这里LauncherList.qml整体是作为一个Component的,当LauncherList实例化完成之后,就会触发onCompleted响应函数,来向LauncherList中addExample。
下面我们就开始分析每一个Example。

fonts/hello.qml文件 hello.qml源码结构比较简单,只有一个Item:

Rectangle { id: screenwidth: 320; height: 480 color: "black"Item {....} }

Item中只有一个Text字段,坐标在父元素的居中位置:

Item { id: container x: screen.width / 2; y: screen.height / 2Text {....} }

Text中描述了颜色(白色)、文本内容(Hello world!)、字体大小(32),而且还定义了两个SequentialAnimation分别表示字间距和透明度上的动画效果。

Text { id: text anchors.centerIn: parent color: "white" text: "Hello world!" font.pixelSize: 32//! [letterspacing] SequentialAnimation on font.letterSpacing {....} //! [letterspacing] SequentialAnimation on opacity {....} }


字间距动画效果

SequentialAnimation on font.letterSpacing { loops: Animation.Infinite; NumberAnimation { from: 0; to: 50; easing.type: Easing.InQuad; duration: 3000 } ScriptAction { script: { container.y = (screen.height / 4) + (Math.random() * screen.height / 2) container.x = (screen.width / 4) + (Math.random() * screen.width / 2) } } }

从上面这个动画效果,我们可以看到以下几个组成部分:

  1. 动画循环次数:这里是无线循环的显示动画。(如果要停掉动画,可以设置running属性为false,或者调用stop()函数)
  2. 定义一个具体的动画:这里使用的是一个可以根据数字变化的动画效果:从0增加到50(数值的改变作用在font.letterSpacing),设置擦除曲线为InQuad(具体效果未知),动画时长为3s
  3. 定义一个脚本动作:具体的效果是随机改变container的x,y坐标值
整体的动画效果就是:

  • 在3s内不断增大文本的字间距从0到50,然后随机改变一下文本的x,y坐标;
  • 循环进行上述操作。
透明度动画效果

SequentialAnimation on opacity { loops: Animation.Infinite; NumberAnimation { from: 1; to: 0; duration: 2600 } PauseAnimation { duration: 400 } }

通过上面对字间距动画的效果分析,我们可以知道本例中的透明度动画效果是:

  • 在2.6s内不断改变文本的透明度从1到0,然后暂停0.4s(以便和上面的3s动画保持步调一致);
  • 循环进行上述操作。
fonts/fonts.qml文件 fonts.qml文件中定义了一个成员变量myText,3个FontLoader和一个Column,Column中有6个Text使用不同的字体显示myText内容。

Rectangle { property string myText: "The quick brown fox jumps over the lazy dog."width: 320; height: 480 color: "steelblue"//! [fontloader] FontLoader { id: fixedFont; name: "Courier" } //! [fontloader] //! [fontloaderlocal] FontLoader { id: localFont; source: "content/fonts/tarzeau_ocr_a.ttf" } //! [fontloaderlocal] //! [fontloaderremote] FontLoader { id: webFont; source: "http://www.princexml.com/fonts/steffmann/Starburst.ttf" } //! [fontloaderremote]Column {....} }


FontLoader简述
【QT Demo 之 text】The FontLoader type is used to load fonts by name or URL.

一个FontLoader共有3个属性,分别是:
name : stringThis property holds the name of the font family.
source : urlThe url of the font to load
status : enumerationThis property holds the status of font loading. It can be one of:FontLoader.Null,FontLoader.Ready,FontLoader.Loading,FontLoader.Error

本例中使用了上面的两种方式加载字体,分别加载了"Courier"和"content/fonts/tarzeau_ocr_a.ttf"两种本地字体,以及"http://www.princexml.com/fonts/steffmann/Starburst.ttf"网络字体,针对Starburst.ttf网络字体,因为涉及到网络加载,使用了第三个属性status,如最后一个Text是在字体加载的不同状态下显示不同的文本:

Text { text: { if (webFont.status == FontLoader.Ready) myText else if (webFont.status == FontLoader.Loading) "Loading..." else if (webFont.status == FontLoader.Error) "Error loading font" } }


字体显示
以下6个Text的字体显示分别如下(省略了部分代码):

字体是Times,大小是20

Text { font.family: "Times" font.pixelSize: 20 }

字体是Times,对齐方式居中,大小是20,文本全部大写
Text { horizontalAlignment: Text.AlignHCenter font { family: "Times"; pixelSize: 20; capitalization: Font.AllUppercase } }

字体是上面fixedFont指定的Courier字体,对齐方式是右对齐,大小是20,加粗,文本全部小写
Text { horizontalAlignment: Text.AlignRight font { family: fixedFont.name; pixelSize: 20; weight: Font.Bold; capitalization: Font.AllLowercase } }

字体是上面fixedFont指定的Courier字体,大小是20,斜体,文本小型大写(大小跟小写字母一样,样式是大写)
Text { font { family: fixedFont.name; pixelSize: 20; italic: true; capitalization: Font.SmallCaps } }

字体是上面localFont指定的tarzeau_ocr_a.ttf字体,大小是20,文本每个单词的首字母大写
Text { font { family: localFont.name; pixelSize: 20; capitalization: Font.Capitalize } }

字体是上面webFont指定的Starburst.ttf字体,大小是20
Text { font.family: webFont.name; font.pixelSize: 20 }

上面忽略的代码如下,分别定义了text文本内容、字体颜色、文本宽度以及按照单词进行换行:
text: myText color: "lightsteelblue" width: parent.widthwrapMode: Text.WordWrap


fonts/availableFonts.qml文件 availableFonts文件中就是一个ListView,用来显示所有的Font格式

Rectangle { width: 320; height: 480; color: "steelblue"ListView {....} }


ListView简述
A ListView displays data from models created from built-in QML types like ListModel and XmlListModel, or custom model classes defined in C++ that inherit from QAbstractItemModel or QAbstractListModel.
A ListView has a model, which defines the data to be displayed, and a delegate, which defines how the data should be displayed. Items in a ListView are laid out horizontally or vertically. List views are inherently flickable because ListView inherits from Flickable.

从上面的描述,我们可以知道ListView用来显示从model中提供的数据,然后按照delegate的方式进行显示。


显示的数据来源于Qt.fontFamilies()

//! [model] model: Qt.fontFamilies() //! [model]


显示的方式是每行高40,宽和列表等宽,居中显示,颜色白色,大小20

delegate: Item { height: 40; width: ListView.view.width Text { anchors.centerIn: parent text: modelData //! [delegate] font.family: modelData //! [delegate] font.pixelSize: 20 color: "white" } }

注意,这里的字体以及文本内容都是modelData,就是说每种字体都是使用自己的字体格式来显示字体名称。

令人有点疑惑不解的是modelData这个变量是哪里来的,通过查找文档,我们了解到这是一个内置变量,用来表示model中的每一个元素
Models that do not have named roles (such as the QStringList model shown below) will have the data provided via the modelData role. The modelData role is also provided for models that have only one role. In this case the modelData role contains the same data as the named role.
吐槽:帮助文档中没有任何和modelData有关的内容,官网上又是语焉不详的,在这个知识点上,Qt的帮助做的太差了。

fonts/banner.qml文件 和fonts/font.qml相反的是,这一个qml中主要定义了一个Row:

Rectangle { id: screenproperty int pixelSize: screen.height * 1.25 property color textColor: "lightsteelblue" property string text: "Hello world! "width: 320; height: 480 color: "steelblue"Row {....} }

开始的3个成员变量分别定义了字体大小、字体颜色以及文本内容。

下面的Row中,显示了三个Text(内容一样,吐槽),然后定义了一个NumberAnimation动画,不断实现向左侧平移的动画(注意,这里改变的x是针对整个Row元素的,因此如果把窗口拉长,是会看到3个Text一起向左平移的)(平移的长度竟然是一个Text的width,而参与平移的是三个Text,吐槽)

Row { y: -screen.height / 4.5NumberAnimation on x { from: 0; to: -text.width; duration: 6000; loops: Animation.Infinite } Text { id: text; font.pixelSize: screen.pixelSize; color: screen.textColor; text: screen.text } Text { font.pixelSize: screen.pixelSize; color: screen.textColor; text: screen.text } Text { font.pixelSize: screen.pixelSize; color: screen.textColor; text: screen.text } }


imgtag/imgtag.qml文件 这一节非常好玩,重点演示了图片和文本在一起的各种排版效果,先看文件的整体结构,主要包括是一个Flikable,以及三个键盘事件响应函数

Rectangle { id: main width: 320; height: 480 focus: true color: "#dedede"property var hAlign: Text.AlignLeftFlickable {...}Keys.onUpPressed: main.hAlign = Text.AlignHCenter Keys.onLeftPressed: main.hAlign = Text.AlignLeft Keys.onRightPressed: main.hAlign = Text.AlignRight }


Flickable简述

The Flickable item places its children on a surface that can be dragged and flicked, causing the view onto the child items to scroll. This behavior forms the basis of Items that are designed to show large numbers of child items, such as ListView and GridView.
简单立即,Flikable就是在一个较小的窗口下显示一个较大的内容,然后这个内容是可以拖动的。

Flickable { anchors.fill: parent contentWidth: parent.width contentHeight: col.height + 20Column {....} }

这里的Flickable只有一个Column子元素,使用contentWidth和contentHeight描述可以拖动的范围。因为contentWidth等于parent.width,则在左右方向上不可拖动;contentHeight等于子元素col的高度+20,表示可以拖动子元素col离开底面20个像素(此处如果我们改变20为200,经测试可以拖动到更高的位置)。
但是,这里有一个疑问的地方,父元素竟然可以访问子元素的属性,更何况col此时还没有创建(位于下面几行)?我的理解,QML中的所有元素都是全局的,但是有结构上从属关系,从访问的角度上是可以通过id直接访问的;而且这里设置contentHeight也可以放到Column后面,毕竟这个是针对Flickable的一个属性设置。

具体的Column数据如下:
字体加粗,插入图片,图片和文本的排版方式默认底对齐

TextWithImage { text: "This is a happy faceQT Demo 之 text
文章图片

注意:右半边部分的字体是斜体,颜色和左半边也不一样,所以视觉效果差异很大。
总结 学到的知识点:
  1. Component元素以及onCompleted事件响应函数
  2. 一个Qt动画的基本构成
  3. 了解了SequentialAnimation动画、NumberAnimation动画、PauseAnimation动画以及ScriptAction动作
  4. 如何加载字体(使用name和source,包括本地和网络)
  5. 如何设置文本的样式(字体、大小、颜色、加粗、斜体、对齐、大小写设置)
  6. 学习了怎么使用ListView
  7. 学习了Column和Row的使用方式
  8. 学习了使用property定义成员变量
  9. 学习了怎么使用Flickable
  10. 学习了富文本格式下,如何进行排版(文本和图片交叉、设置图片大小、设置文本和图片的对齐方式)
  11. 学习了Text元素内如何针对每行进行排版
经过大概3天的时间,将Text下的6个Example进行了仔细学习,基本覆盖了Text的方方面面。Example写的比较详细和全面,但是也有很多坑和槽点。

    推荐阅读