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的操作。

先看一下程序運行的效果圖:

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: 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)
                    }
                }
            }
從上面這個動畫效果,我們可以看到以下幾個組成部分:

  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簡述

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

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: 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中,顯示了三個Text(內容一樣,吐槽),然後定義了一個NumberAnimation動畫,不斷實現向左側平移的動畫(注意,這裏改變的x是針對整個Row元素的,因此如果把窗口拉長,是會看到3個Text一起向左平移的)(平移的長度竟然是一個Text的width,而參與平移的是三個Text,吐槽)

    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 }
    }

imgtag/imgtag.qml文件

這一節非常好玩,重點演示了圖片和文本在一起的各種排版效果,先看文件的整體結構,主要包括是一個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
}

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 + 20

        Column {....}
    }
這裏的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 <b>happy</b> face<img src=\"images/face-smile.png\">"
            }
字體加粗,插入圖片,設置圖片和文本是居中對齊

            TextWithImage {
                text: "This is a <b>very<img src=\"images/face-smile-big.png\" align=\"middle\"/>happy</b> face vertically aligned in the middle."
            }
插入圖片,並設置圖片的寬高進行縮放

            TextWithImage {
                text: "This is a tiny<img src=\"images/face-smile.png\" width=\"15\" height=\"15\">happy face."
            }
插入兩個圖片,分別是頂對齊和底對齊

            TextWithImage {
                text: "This is a<img src=\"images/starfish_2.png\" width=\"50\" height=\"50\" align=\"top\">aligned to the top and a<img src=\"images/heart200.png\" width=\"50\" height=\"50\">aligned to the bottom."
            }
插入多個圖片,全部是居中對齊,設置不同的寬高進行縮放

            TextWithImage {
                text: "Qt logos<img src=\"images/qtlogo.png\" width=\"55\" height=\"60\" align=\"middle\"><img src=\"images/qtlogo.png\" width=\"37\" height=\"40\" align=\"middle\"><img src=\"images/qtlogo.png\" width=\"18\" height=\"20\" align=\"middle\">aligned in the middle with different sizes."
            }
插入多個圖片,全部是底對齊,設置不同的寬高進行縮放

            TextWithImage {
                text: "Some hearts<img src=\"images/heart200.png\" width=\"20\" height=\"20\" align=\"bottom\"><img src=\"images/heart200.png\" width=\"30\" height=\"30\" align=\"bottom\"> <img src=\"images/heart200.png\" width=\"40\" height=\"40\"><img src=\"images/heart200.png\" width=\"50\" height=\"50\" align=\"bottom\">with different sizes."
            }
插入網絡圖片,居中對齊,並設置寬高進行縮放

            TextWithImage {
                text: "Resized image<img width=\"48\" height=\"48\" align=\"middle\" src=\"http://qt-project.org/images/qt13a/Qt-logo.png\">from the internet."
            }
插入網絡圖片,居中對齊

            TextWithImage {
                text: "Image<img align=\"middle\" src=\"http://qt-project.org/images/qt13a/Qt-logo.png\">from the internet."
            }
指定高度(但是文本字體以及圖片大小均不變),並進行垂直居中(如果在該TextWithImage外面套一個Rectangle並設置好背景色,那麼顯示效果就很清晰了),顯示文本和圖片
            TextWithImage {
                height: 120
                verticalAlignment: Text.AlignVCenter
                text: "This is a <b>happy</b> face<img src=\"images/face-smile.png\"> 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標籤)以及水平上的對齊方式保持和主窗口一致
注意,此處代碼上使用了跨文件的id,對於代碼的耦合性上考慮非常不建議。

最後添加了幾個按鍵事件響應函數,即分別通過左上右來改變整個Rectangle的佈局格式:左對齊、居中對齊、右對齊。
    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文件中使用。啊,多麼操蛋的設計。

styledtext-layout.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 <a href=\"http://www.digia.com\">www.digia.com</a>.<br/>Curabitur ante est, pulvinar quis adipiscing a, iaculis id ipsum. Nunc blandit condimentum odio vel egestas.<br><ul type=\"bullet\"><li>Coffee<ol type=\"a\"><li>Espresso<li>Cappuccino<li>Latte</ol><li>Juice<ol type=\"1\"><li>Orange</li><li>Apple</li><li>Pineapple</li><li>Tomato</li></ol></li></ul><p><font color=\"#434343\"><i>Proin consectetur <b>sapien</b> 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]
    }

文本text內容詳細分析包括以下內容:

  1. 純文本:Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer at ante dui
  2. 超鏈接:<a href=\"http://www.digia.com\">www.digia.com</a>.
  3. 換行:<br/>Curabitur ante est, pulvinar quis adipiscing a, iaculis id ipsum. Nunc blandit condimentum odio vel egestas.<br>
  4. 無符號列表:<ul type=\"bullet\"><li>Coffee<ol type=\"a\"><li>Espresso<li>Cappuccino<li>Latte</ol><li>Juice<ol type=\"1\"><li>Orange</li><li>Apple</li><li>Pineapple</li><li>Tomato</li></ol></li></ul>
  5. 段落:<p>
  6. 字體顏色設置:<font color=\"#434343\">
  7. 斜體:<i>Proin consectetur <b>sapien</b> 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.

吐槽:文本標籤不配對,此處難道是測試Qt對於不標準的格式的兼容性嗎??

lineLaidOut信號和onLineLaidOut事件響應函數

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來排版到空白的右半邊,具體的效果圖如下:

注意:右半邊部分的字體是斜體,顏色和左半邊也不一樣,所以視覺效果差異很大。

總結

學到的知識點:
  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寫的比較詳細和全面,但是也有很多坑和槽點。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章