学习了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的操作。
先看一下程序运行的效果图:
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。
hello.qml源码结构比较简单,只有一个Item:
Rectangle {
id: screen
width: 320; height: 480
color: "black"
Item {....}
}
Item中只有一个Text字段,坐标在父元素的居中位置:
Item {
id: container
x: screen.width / 2; y: screen.height / 2
Text {....}
}
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)
}
}
}
从上面这个动画效果,我们可以看到以下几个组成部分:
整体的动画效果就是:
SequentialAnimation on opacity {
loops: Animation.Infinite;
NumberAnimation { from: 1; to: 0; duration: 2600 }
PauseAnimation { duration: 400 }
}
通过上面对字间距动画的效果分析,我们可以知道本例中的透明度动画效果是:
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 {....}
}
The FontLoader type is used to load fonts by name or URL.
一个FontLoader共有3个属性,分别是:
name : string This property holds the name of the font family.
source : url The url of the font to load
status : enumeration This 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.width
wrapMode: Text.WordWrap
availableFonts文件中就是一个ListView,用来显示所有的Font格式
Rectangle {
width: 320; height: 480; color: "steelblue"
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]
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/font.qml相反的是,这一个qml中主要定义了一个Row:
Rectangle {
id: screen
property int pixelSize: screen.height * 1.25
property color textColor: "lightsteelblue"
property string text: "Hello world! "
width: 320; height: 480
color: "steelblue"
Row {....}
}
开始的3个成员变量分别定义了字体大小、字体颜色以及文本内容。
Row {
y: -screen.height / 4.5
NumberAnimation 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 }
}
这一节非常好玩,重点演示了图片和文本在一起的各种排版效果,先看文件的整体结构,主要包括是一个Flikable,以及三个键盘事件响应函数
Rectangle {
id: main
width: 320; height: 480
focus: true
color: "#dedede"
property var hAlign: Text.AlignLeft
Flickable {...}
Keys.onUpPressed: main.hAlign = Text.AlignHCenter
Keys.onLeftPressed: main.hAlign = Text.AlignLeft
Keys.onRightPressed: main.hAlign = Text.AlignRight
}
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 + 20
Column {....}
}
这里的Flickable只有一个Column子元素,使用contentWidth和contentHeight描述可以拖动的范围。因为contentWidth等于parent.width,则在左右方向上不可拖动;contentHeight等于子元素col的高度+20,表示可以拖动子元素col离开底面20个像素(此处如果我们改变20为200,经测试可以拖动到更高的位置)。
具体的Column数据如下:
字体加粗,插入图片,图片和文本的排版方式默认底对齐
TextWithImage {
text: "This is a happy face"
}
字体加粗,插入图片,设置图片和文本是居中对齐
TextWithImage {
text: "This is a veryhappy face vertically aligned in the middle."
}
插入图片,并设置图片的宽高进行缩放
TextWithImage {
text: "This is a tinyhappy face."
}
插入两个图片,分别是顶对齐和底对齐
TextWithImage {
text: "This is aaligned to the top and aaligned to the bottom."
}
插入多个图片,全部是居中对齐,设置不同的宽高进行缩放
TextWithImage {
text: "Qt logosaligned in the middle with different sizes."
}
插入多个图片,全部是底对齐,设置不同的宽高进行缩放
TextWithImage {
text: "Some hearts with different sizes."
}
插入网络图片,居中对齐,并设置宽高进行缩放
TextWithImage {
text: "Resized imagefrom the internet."
}
插入网络图片,居中对齐
TextWithImage {
text: "Imagefrom the internet."
}
指定高度(但是文本字体以及图片大小均不变),并进行垂直居中(如果在该TextWithImage外面套一个Rectangle并设置好背景色,那么显示效果就很清晰了),显示文本和图片
TextWithImage {
height: 120
verticalAlignment: Text.AlignVCenter
text: "This is a happy face with an explicit height."
}
上面使用的TextWithImage也是一个自定义的Component,单独定义在TextWithImage.qml文件中:
Text {
width: parent.width
font.pointSize: 14
wrapMode: Text.WordWrap
textFormat: Text.StyledText
horizontalAlignment: main.hAlign
}
从代码上可以看出TextWithImage这个Component指定了宽度、字体大小、换行方式、文本格式是使用格式化的方式(即支持HTML标签)以及水平上的对齐方式保持和主窗口一致
Keys.onUpPressed: main.hAlign = Text.AlignHCenter
Keys.onLeftPressed: main.hAlign = Text.AlignLeft
Keys.onRightPressed: main.hAlign = Text.AlignRight
注意,这里的hAlign并不是内置成员变量,但是为什么改变这个属性的值就能修改文本的对齐方式呢?原因就在于,TextWithImage的horizontalAlignment属性使用main.hAlign变量的值,即在imgtag.qml文件中改变hAlign变量的值,然后在TextWithImage.qml文件中使用。啊,多么操蛋的设计。
该文件演示了如何使用文本排版中的按行进行详细排版的方法,文件的主结构只有一个Text:
Rectangle {
id: main
width: 320; height: 480
focus: true
property real offset: 0
property real margin: 8
Text {....}
}
吐槽:offset字段没有使用,留之何用。
Text字段详细定义了页边距为10、换行方式为按单词进行换行、字体是Times New Roman、字体大小是14、文本采用富文本格式、水平对齐方式为自动调整文本间的空格以满足每行两端对齐(类似报纸排版),然后创建了一个非常非常长的text,其中有各种富文本标签,以及自定义了一个onLineLaidOut用来详细的进行行排版。
Text {
id: myText
anchors.fill: parent
anchors.margins: 10
wrapMode: Text.WordWrap
font.family: "Times New Roman"
font.pixelSize: 14
textFormat: Text.StyledText
horizontalAlignment: Text.AlignJustify
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer at ante dui www.digia.com.
Curabitur ante est, pulvinar quis adipiscing a, iaculis id ipsum. Nunc blandit condimentum odio vel egestas.
- Coffee
- Espresso
- Cappuccino
- Latte
- Juice
- Orange
- Apple
- Pineapple
- Tomato
Proin consectetur sapien in ipsum lacinia sit amet mattis orci interdum. Quisque vitae accumsan lectus. Ut nisi turpis, sollicitudin ut dignissim id, fermentum ac est. Maecenas nec libero leo. Sed ac leo eget ipsum ultricies viverra sit amet eu orci. Praesent et tortor risus, viverra accumsan sapien. Sed faucibus eleifend lectus, sed euismod urna porta eu. Quisque vitae accumsan lectus. Ut nisi turpis, sollicitudin ut dignissim id, fermentum ac est. Maecenas nec libero leo. Sed ac leo eget ipsum ultricies viverra sit amet eu orci."
//! [layout]
onLineLaidOut: {....}
//! [layout]
}
吐槽:文本标签不配对,此处难道是测试Qt对于不标准的格式的兼容性吗??
This signal is emitted for each line of text that is laid out during the layout process. The specified line object provides more details about the line that is currently being laid out.
This gives the opportunity to position and resize a line as it is being laid out. It can for example be used to create columns or lay out text around objects.
The corresponding handler is onLineLaidOut.
通过官方说明,我们可以了解,lineLaidOut信号是在每行文本准备布局的时候触发,开发人员可以通过自定义onLineLaidOut事件响应函数来进行个性化的按行进行排版。
onLineLaidOut: {
line.width = width / 2 - (margin)
if (line.y + line.height >= height) {
line.y -= height - margin
line.x = width / 2 + margin
}
}
示例代码中,是将每行的宽度减半再减去一个边距值,即只显示在左半边;但是如果左半边已经超出下面的边界怎么办?通过调整line.x和line.y来排版到空白的右半边,具体的效果图如下: