├── .gitignore ├── fluid_elements ├── advanced_techniques.md ├── README.md └── states_and_transitions.md ├── quick_starter ├── advanced_techniques.md ├── README.md ├── layout_items.md ├── simple_transformations.md ├── compontents.md ├── positioning_element.md ├── input_element.md ├── qmlqml_syntax.md └── basic_elements.md ├── scripts ├── s2t ├── s2tinplace ├── gen-terms-transfer.sh ├── terms-transfer.txt └── terms-transfer.sh ├── process_images ├── 1.PNG ├── 10.PNG ├── 11.PNG ├── 12.PNG ├── 13.PNG ├── 2.PNG ├── 3.PNG ├── 4.PNG ├── 5.PNG ├── 6.PNG ├── 7.PNG ├── 8.PNG └── 9.PNG ├── networking ├── summary.md ├── engine_io.md ├── README.md ├── authentication_using_oauth.md ├── templating.md ├── local_files.md ├── httpuiserving_ui_via_http.md ├── httphttp_requests.md ├── web_sockets.md └── restrest_api.md ├── multimedia ├── summary.md ├── README.md ├── video_streams.md ├── sounds_effects.md ├── advanced_techniques.md ├── capturing_images.md └── playing_media.md ├── qt_creator_ide ├── debugging.md ├── using_the_editor.md ├── locator.md ├── README.md ├── qtregistering_your_qt_kit.md ├── shortcuts.md ├── managing_projects.md └── the_user_interface.md ├── get_start ├── README.md ├── summary.md ├── qt5installing_qt_5_sdk.md └── hello_world.md ├── particle_simulations ├── summary.md ├── README.md ├── concept.md ├── particle_painter.md ├── particle_parameters.md ├── simple_simulation.md ├── affecting_particles.md ├── directed_particle.md └── particle_group.md ├── meet_qt_5 ├── README.md ├── qtqt_project.md ├── preface.md ├── qtqt_building_blocks.md └── qt5qt5_introduction.md ├── model-view-delegate ├── README.md ├── summary.md ├── concept.md ├── basic_model.md ├── advanced_techniques.md ├── dynamic_views.md └── delegate.md ├── shader_effect ├── openglopengl_shader.md ├── README.md ├── wave_effect.md ├── qtqt_graphicseffect_library.md ├── curtain_effect.md ├── shader_elements.md ├── fragement_shader.md └── vertex_shader.md ├── README.md ├── canvas_element ├── gradients.md ├── convenient_api.md ├── shadows.md ├── transformation.md ├── images.md ├── pixels_buffer.md ├── composition_mode.md ├── canvas_paint.md ├── README.md └── html5porting_from_html5_canvas.md └── SUMMARY.md /.gitignore: -------------------------------------------------------------------------------- 1 | /_book/ 2 | -------------------------------------------------------------------------------- /fluid_elements/advanced_techniques.md: -------------------------------------------------------------------------------- 1 | # 高級用法(Advanced Techniques) 2 | 3 | 後續添加。 4 | -------------------------------------------------------------------------------- /quick_starter/advanced_techniques.md: -------------------------------------------------------------------------------- 1 | # 高級用法(Advanced Techniques) 2 | 3 | 後續添加。 4 | -------------------------------------------------------------------------------- /scripts/s2t: -------------------------------------------------------------------------------- 1 | iconv -f utf8 -t gb2312 | iconv -f gb2312 -t big5 | iconv -f big5 -t utf8 2 | -------------------------------------------------------------------------------- /process_images/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/1.PNG -------------------------------------------------------------------------------- /process_images/10.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/10.PNG -------------------------------------------------------------------------------- /process_images/11.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/11.PNG -------------------------------------------------------------------------------- /process_images/12.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/12.PNG -------------------------------------------------------------------------------- /process_images/13.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/13.PNG -------------------------------------------------------------------------------- /process_images/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/2.PNG -------------------------------------------------------------------------------- /process_images/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/3.PNG -------------------------------------------------------------------------------- /process_images/4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/4.PNG -------------------------------------------------------------------------------- /process_images/5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/5.PNG -------------------------------------------------------------------------------- /process_images/6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/6.PNG -------------------------------------------------------------------------------- /process_images/7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/7.PNG -------------------------------------------------------------------------------- /process_images/8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/8.PNG -------------------------------------------------------------------------------- /process_images/9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ycheng/qt5_cadaques-zh_tw/HEAD/process_images/9.PNG -------------------------------------------------------------------------------- /networking/summary.md: -------------------------------------------------------------------------------- 1 | # 總結(Summary) 2 | 3 | 這章我們討論了關于QML的網絡應用。請記住Qt已在本地端提供了豐富的網絡接口可以在QML中使用。但是這一章的我們是想推動QML的網絡運用和如何與雲服務集成。 4 | -------------------------------------------------------------------------------- /scripts/s2tinplace: -------------------------------------------------------------------------------- 1 | mv $1 $1.orig 2 | iconv -f utf8 -t gb2312 $1.orig | iconv -f gb2312 -t big5 | iconv -f big5 -t utf8 > $1 3 | 4 | -------------------------------------------------------------------------------- /scripts/gen-terms-transfer.sh: -------------------------------------------------------------------------------- 1 | awk '{print "sed -e s/" $1 "/" $2 "/g | \\"} END {print "cat"}' terms-transfer.txt > terms-transfer.sh 2 | -------------------------------------------------------------------------------- /multimedia/summary.md: -------------------------------------------------------------------------------- 1 | # 總結(Summary) 2 | 3 | Qt的媒體應用程序接口提供了播放和捕捉視頻和音頻的機制。通過VideoOutput元素,視頻源能夠在我們的用戶界面上顯示。通過MediaPlayer元素,可以操作大多數的播放,SoundEffect被用于低延遲的聲音。Camera元素被用來截圖或者顯示一個實時的視頻流。 4 | -------------------------------------------------------------------------------- /networking/engine_io.md: -------------------------------------------------------------------------------- 1 | # Engine IO 2 | 3 | Engine IO是DIGIA運行的一個web服務。它允許Qt/QML應用程序訪問來自Engin.IO的NoSQL存儲。這是一個基于雲存儲對象的Qt/QML接口和一個管理平台。如果你想存儲一個QML應用程序的數據到雲存儲中,它可以提供非常方便的QML/JS的接口。 4 | 5 | 查看[EnginIO](http://engin.io/)的文檔獲得更多的幫助。 6 | -------------------------------------------------------------------------------- /qt_creator_ide/debugging.md: -------------------------------------------------------------------------------- 1 | # 調試(Debugging) 2 | 3 | Qt Creator支持C++與QML代碼調試。 4 | 5 | **注意** 6 | 7 | **嗯,我才意識到我還沒有使用過調試。這是一個好的現象。我需要有人對此提出問題,查看[Qt Creator documentation](http://qt-project.org/doc/qtcreator-2.8/)來獲得更多的幫助吧。** 8 | -------------------------------------------------------------------------------- /get_start/README.md: -------------------------------------------------------------------------------- 1 | # Get Start 2 | 3 | 這一章介紹了如何使用Qt5進行開發。我們將告訴你如何安裝Qt軟件開發工具包(Qt SDK)和如何使用Qt Creator集成開發環境(Qt Creator IDE)創建並運行一個簡單的hello word應用程序。 4 | 5 | **注意** 6 | 7 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 8 | -------------------------------------------------------------------------------- /scripts/terms-transfer.txt: -------------------------------------------------------------------------------- 1 | 窗口類 窗口類別 2 | 基礎類 基礎類別 3 | 信息 資訊 4 | 數據庫 資料庫 5 | 基礎模組 核心模組 6 | 于 於 7 | 文檔 檔案 8 | 展示 範例 9 | 導出 輸出 10 | 模塊 模組 11 | 庫 程式庫 12 | 音頻 聲音 13 | 視頻 影片 14 | 攝像頭 照相 15 | 網絡 網路 16 | 訪問 存取 17 | 接口 介面 18 | 反饋 回饋 19 | 質量 品質 20 | 聯系人 聯絡人 21 | -------------------------------------------------------------------------------- /get_start/summary.md: -------------------------------------------------------------------------------- 1 | # 總結( Summary) 2 | 3 | 我們已經知道了如何安裝Qt軟件開發工具包,並且知道如何創建我們的應用。我們向你展示和概述了使用Qt開發不同類型的應用程序。展示Qt可以給你的應用程序開發提供的一些功能。我希望你對Qt留下一個好的印象,Qt是一個非常好的用戶界面開發工具並且盡可能的提供了一個應用開發者期望的東西。當前你也不必一直鎖定使用Qt,你也可以使用其它的庫或者自己來擴展Qt。Qt對于不同類型的應用程序開發支持非常豐富:包括控制台程序,經典的桌面用戶界面程序和觸摸式用戶界面程序。 4 | -------------------------------------------------------------------------------- /particle_simulations/summary.md: -------------------------------------------------------------------------------- 1 | # 總結(Summary) 2 | 3 | 粒子是一個非常強大且有趣的方法,用來表達圖像現象的一種方式,比如煙, 火花,隨機可視元素。Qt5的擴展API非常強大,我們僅僅只使用了一些淺顯的。有一些元素我們還沒有使用過,比如精靈(spirites),尺寸表(size tables),顏色表(color tables)。粒子看起來非常有趣,它在界面上創建引人注目的東西是非常有潛力的。在一個用戶界面中使用非常多的粒子效果將會導致用戶對它產生這是一個遊戲的印象。粒子的真正力量也是用來創建遊戲。 4 | -------------------------------------------------------------------------------- /meet_qt_5/README.md: -------------------------------------------------------------------------------- 1 | # Meet Qt 5 2 | 3 | 這本書展示了通過Qt 5.x 版本開發應用的各方面內容。主要介紹Qt Quick的技術,但也提供了如何設計C++後端和擴充Qt Quick的方法。 4 | 5 | 這一章是在一個較高的層次對Qt5的一個概述,它展示了開發者可以使用的不同開發模式,並通過一個Qt5範例說明本書將要介紹的內容。本章也提供了對Qt5的廣泛介紹與如何與Qt開發者聯系的方法。 6 | 7 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 8 | -------------------------------------------------------------------------------- /multimedia/README.md: -------------------------------------------------------------------------------- 1 | # Multimedia 2 | 3 | 在QtMultimedia模塊中的multimedia元素可以播放和記錄媒體資源,例如聲音,視頻,或者圖片。解碼和編碼的操作由特定的後台完成。例如在Linux上的gstreamer框架,Windows上的DirectShow,和OS X上的QuickTime。 4 | multimedia元素不是QtQuick核心的接口。它的接口通過導入QtMultimedia 5.0來加入,如下所示: 5 | 6 | ``` 7 | import QtMultimedia 5.0 8 | ``` 9 | -------------------------------------------------------------------------------- /qt_creator_ide/using_the_editor.md: -------------------------------------------------------------------------------- 1 | # 使用編輯器(Using the Editor) 2 | 3 | 當你打開一個項目或者創建一個新的項目後,Qt Creator將會轉換到編輯模式下。你應該可以在左邊看到你的項目文件,在中央區域看到代碼編輯器。左邊選中的文件將會被編輯器打開。編輯器提供了語法高亮,代碼補全和智能糾錯的功能。也提供幾種代碼重構的命令。當你使用這個編輯器工作時你會覺得它的響應非常的迅速。這感謝與Qt Creaotor的開發者將這個工具做的如此傑出。 4 | 5 | ![](http://qmlbook.org/_images/creator-editor.png) 6 | -------------------------------------------------------------------------------- /fluid_elements/README.md: -------------------------------------------------------------------------------- 1 | # Fluid Elements 2 | 3 | **注意** 4 | 5 | **最後一次構建:2014年1月20日下午18:00。** 6 | 7 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 8 | 9 | 到目前為止,我們已經介紹了簡單的圖形元素和怎樣布局,怎樣操作它們。這一章介紹如何控制屬性值的變化,通過動畫的方式在一段時間內來改變屬性值。這項技術是建立一個現代化的平滑界面的基礎,通過使用狀態和過渡來擴展你的用戶界面。每一種狀態定義了屬性的改變,與動畫聯系起來的狀態改變稱作過渡。 10 | -------------------------------------------------------------------------------- /model-view-delegate/README.md: -------------------------------------------------------------------------------- 1 | # Model-View-Delegate 2 | 3 | **注意** 4 | 5 | **最後一次構建:2014年1月20日下午18:00。** 6 | 7 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 8 | 9 | 在QtQuick中,數據通過model-view(模型-視圖)分離。對于每個view(視圖),每個數據元素的可視化都分給一個代理(delegate)。QtQuick附帶了一組預定義的模型與視圖。想要使用這個系統,必須理解這些類,並且知道如何創建合適的代理來獲得正確的顯示和交互。 10 | -------------------------------------------------------------------------------- /quick_starter/README.md: -------------------------------------------------------------------------------- 1 | # Quick Starter 2 | Quick Starter 3 | 4 | **注意** 5 | 6 | **最後一次構建:2014年1月20日下午18:00。** 7 | 8 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 9 | 10 | 這章概述了QML語言,Qt5中大量使用了這種聲明用戶界面的語言。我們將會討論QML語言,一個樹形結構的元素,跟著是一些最基本的元素概述。然後我們會簡短的介紹怎樣創建我們自己的元素,這些元素被叫做組件,並如何使用屬性操作來轉換元素。最後我們會介紹如何對元素進行布局,如何向用戶提供輸入。 11 | -------------------------------------------------------------------------------- /shader_effect/openglopengl_shader.md: -------------------------------------------------------------------------------- 1 | # OpenGL著色器(OpenGL Shader) 2 | 3 | OpenGL的渲染管線分為幾個步驟。一個簡單的OpenGL渲染管線將包含一個頂點著色器和一個片段著色器。 4 | 5 | ![](http://qmlbook.org/_images/openglpipeline.png) 6 | 7 | 頂點著色器接收頂點數據,並且在程序最後賦值給gl_Position。然後,頂點將會被裁剪,轉換和柵格化後作為像素輸出。 8 | 片段(像素)進入片段著色器,進一步對片段操作並將結果的顏色賦值給gl_FragColor。頂點著色器調用多邊形每個角的點(頂點=3D中的點),負責這些點的3D處理。片段(片度=像素)著色器調用每個像素並決定這個像素的顏色。 9 | -------------------------------------------------------------------------------- /networking/README.md: -------------------------------------------------------------------------------- 1 | # Networking 2 | 3 | Qt5在C++中有豐富的網絡相關的類。例如在http協議層上使用請求回答方式的高級封裝類如QNetworkRequest,QNetworkReply,QNetworkAccessManageer。也有在TCP/IP或者UDP協議層封裝的低級類如QTcpSocket,QTcpServer和QUdpSocket。還有一些額外的類用來管理代理,網絡緩衝和系統網絡配置。 4 | 5 | 這章將不再闡述關于C++網絡方面的知識,這章是關于QtQuick與網絡的知識。我們應該怎樣連接QML/JS用戶界面與網絡服務,或者如何通過網絡服務來為我們用戶界面提供服務。已經有很好的教材和示例覆蓋了關于Qt/C++的網絡編程。然後你只需要閱讀這章相關的C++集成來滿足你的QtQuick就可以了。 6 | -------------------------------------------------------------------------------- /particle_simulations/README.md: -------------------------------------------------------------------------------- 1 | # Particle Simulations 2 | 3 | **注意** 4 | 5 | **最後一次構建:2014年1月20日下午18:00。** 6 | 7 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 8 | 9 | 粒子模擬是計算機圖形技術的可視化圖形效果。典型的效果有:落葉,火燄,爆炸,流星,雲等等。 10 | 11 | 它不同于其它圖形渲染,粒子是基于模糊來渲染。它的結果在基于像素下是不可預測的。粒子系統的參數描述了隨機模擬的邊界。傳統的渲染技術實現粒子渲染效果很困難。有一個好消息是你可以使用QML元素與粒子系統交互。同時參數也可以看做是屬性,這些參數可以使用傳統的動畫技術來實現動態效果。 12 | -------------------------------------------------------------------------------- /get_start/qt5installing_qt_5_sdk.md: -------------------------------------------------------------------------------- 1 | # 安裝Qt5軟件工具包(Installing Qt 5 SDK) 2 | 3 | Qt軟件工具包包含了編譯桌面或者嵌入式應用程序的工具。最新的版本可以從[Qt-Project](http://qt-project.org/)下載。我們將使用這種方法開始。 4 | 5 | 軟件工具包自身包含了一個維護工具允許你更新到最新版本的軟件工具包。 6 | 7 | Qt軟件工具包非常容易安裝,並且附帶了一個它自身的快速集成開發環境叫做Qt Creator。這個集成開發環境可以讓你高效的使用Qt進行開發,我們推薦給所有的讀者使用。在任何情況下Qt都可以通過命令的方式來編譯,你可以自由的選擇你的代碼編輯器。 8 | 9 | 當你安裝軟件工具包時,你最好選擇默認的選項確保Qt 5.x可以被使用。然後一切準備就緒。 10 | -------------------------------------------------------------------------------- /qt_creator_ide/locator.md: -------------------------------------------------------------------------------- 1 | # 定位器(Locator) 2 | 3 | 定位器是Qt Creaotor中心的一個組件。它可以讓開發者迅速的找到指定代碼的位置,或者獲得幫助。使用Ctrl+K來打開定位器。 4 | 5 | ![](http://qmlbook.org/_images/locator.png) 6 | 7 | 左邊底部可以顯示彈出一系列的選項。如果你只是想搜索你項目中的一個文件,你只需要給出文件第一個字母提示就可以了。定位器也接收通配符,比如*main.qml也可以查找。你也可以通過前綴搜索來搜索指定內容的類型。 8 | 9 | ![](http://qmlbook.org/_images/creator-locator.png) 10 | 11 | 試試它,例如尋找一個QML矩形框的幫助,輸入?rectangle。定位器會不停的更新它的建議直到你找到你想要的參考文檔。 12 | -------------------------------------------------------------------------------- /meet_qt_5/qtqt_project.md: -------------------------------------------------------------------------------- 1 | # Qt項目(Qt Project) 2 | 3 | 來自qt-project百科:Qt-Project是由Qt社區上對Qt感興趣的人達成共識的地方。任何人都可以在社區上分享它感興趣的東西,參與它的開發,並且向Qt的開發做出貢獻。 4 | 5 | Qt-Project是一個為Qt未來開發開源部分的組織。它基於使用者的貢獻。最大的貢獻者是DIGIA,它可以提供Qt的商業授權。 6 | Qt對於公司分為開源授權和商業授權。使用商業授權的公司不需要遵守開源協議。沒有商業授權的許可的公司不能使用Qt,並且它也不允許DIGIA向Qt項目貢獻太多的代碼。 7 | 8 | 在全球有很多公司,他們在不同的平台上使用Qt開發產品,提供咨詢。同樣也有很多開源專案和開源開發者,它們使用Qt作為它們的開發程式庫。成為這樣開發活潑的社區的一部分,並且使用這個很棒的程式庫讓人感覺很好。它能讓你成為一個更好的人嗎?也許:-)。 9 | -------------------------------------------------------------------------------- /qt_creator_ide/README.md: -------------------------------------------------------------------------------- 1 | # Qt Creator IDE 2 | 3 | Qt Creator是Qt默認的集成開發環境。它由Qt的開發者們編寫提供的。這個集成開發環境能夠在大多數的桌面開發平台上使用,例如 Windows/Mac/Linux。我們也已經看到有些用戶在嵌入式設備上使用Qt Creator。Qt Creator有著精簡的用戶界面,可以幫助開發者們高效的完成開發生產。Qt Creator 能夠啟動你的QtQuick用戶界面,也可以用來編譯c++代碼到你的主機系統或者使用交叉編譯到你的設備系統上。 4 | 5 | ![](http://qmlbook.org/_images/qtcreator-screenshots.png) 6 | 7 | **注意** 8 | 9 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 10 | -------------------------------------------------------------------------------- /multimedia/video_streams.md: -------------------------------------------------------------------------------- 1 | # 視頻流(Video Streams) 2 | 3 | VideoOutput元素不被限制與MediaPlayer元素綁定使用的。它也可以直接用來加載實時視頻資源顯示一個流媒體。應用程序使用Camera元素作為資源。來自Camera的視頻流給用戶提供了一個實時流媒體。 4 | 5 | ``` 6 | import QtQuick 2.0 7 | import QtMultimedia 5.0 8 | 9 | Item { 10 | width: 1024 11 | height: 600 12 | 13 | VideoOutput { 14 | anchors.fill: parent 15 | source: camera 16 | } 17 | 18 | Camera { 19 | id: camera 20 | } 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Qt5 Cadaques 正體中文版 2 | ======= 3 | 4 | 雖然 C++ 的複雜會讓人確步,但如果加上跨平台昨為條件,選擇還真的是很少。 5 | 6 | Qt 無疑的是在這樣的的選擇下,一個強大穩定且悠久的平台。 7 | 8 | 對岸的朋友 cwc1987 將 Qt5 Cadaques 一書翻譯為簡體中文,我在此共襄盛舉, 9 | 轉譯為正體中文,並將用詞順為台灣用語,以利學人閱讀。 10 | 11 | [正體中文版WIP](http://ycheng.gitbooks.io/qt5_cadaques-zh_tw/content/) 12 | 13 | [正體中文版於GitHub工作區](https://github.com/ycheng/qt5_cadaques-zh_tw) 14 | 15 | [GitHub程式碼](https://github.com/qmlbook/qmlbook) 16 | 17 | [簡體中文版](https://www.gitbook.io/read/book/cwc1987/qt5cadaquesinchinese) 18 | -------------------------------------------------------------------------------- /model-view-delegate/summary.md: -------------------------------------------------------------------------------- 1 | # 總結(Summary) 2 | 3 | 在這個章節中,我們學習了模型,視圖與代理。每個數據的入口是模型,視圖通過可視化代理來實現數據的可視化。將數據從顯示中分離出來。 4 | 5 | 一個模型可以是一個整數,提供給代理使用的索引值(index )。如果JavaScript數組被作為一個模型,模型數據變量(modelData)代表了數組的數據的當前索引。對于更加復雜的情況,每個數據項需要提供多個值,使用鏈表模型(ListModel)與鏈表元素(ListElement)是一個更好的解決辦法。 6 | 7 | 對于靜態模型,一個Repeater可以被用作視圖。它可以非常方便的使用行(Row),列(Column),柵格(Grid),或者流(Flow)來創建用戶界面。對于動態或者大的數據模型,使用ListView或者GridView更加適合。它們會在需要時動態的創建代理,減少在場景下一次顯示的元素的數量。 8 | 9 | 在視圖中的代理可以與數據模型中的屬性靜態綁定,或者動態綁定。使用視圖的onAdd與onRemove信號,可以動態播放的它們的顯示與消失。 10 | -------------------------------------------------------------------------------- /model-view-delegate/concept.md: -------------------------------------------------------------------------------- 1 | # 概念(Concept) 2 | 3 | 對于開發用戶界面,最重要的一方面是保持數據與可視化的分離。例如,一個電話薄可以使用一個垂直文本鏈表排列或者使用一個網格聯系人圖片排列。在這兩個案例中,數據都是相同的,但是可視化效果卻是不同的。這種方法通常被稱作model-view(模型-視圖)模式。在這種模式中,數據通常被稱作model(模型),可視化處理稱作view(視圖)。 4 | 5 | 在QML中,model(模型)與view(視圖)都通過delegate(代理)連接起來。功能劃分如下,model(模型)提供數據。對于每個數據項,可能有多個值。在上面的電話薄例子中,每個電話薄條目對應一個名字,一個圖片和一個號碼。顯示在view(視圖)中的每項數據,都是通過delegate(代理)來實現可視化。view(視圖)的任務是排列這些delegate(代理),每個delegate(代理)將model item(模型項)的值顯示給用戶。 6 | 7 | ![](http://qmlbook.org/_images/graphviz-00dabdd0a911009b7da04ffd00f7c1537eab1281.png) 8 | -------------------------------------------------------------------------------- /scripts/terms-transfer.sh: -------------------------------------------------------------------------------- 1 | sed -e s/窗口類/窗口類別/g | \ 2 | sed -e s/基礎類/基礎類別/g | \ 3 | sed -e s/信息/資訊/g | \ 4 | sed -e s/數據庫/資料庫/g | \ 5 | sed -e s/基礎模組/核心模組/g | \ 6 | sed -e s/于/於/g | \ 7 | sed -e s/文檔/檔案/g | \ 8 | sed -e s/展示/範例/g | \ 9 | sed -e s/導出/輸出/g | \ 10 | sed -e s/模塊/模組/g | \ 11 | sed -e s/庫/程式庫/g | \ 12 | sed -e s/音頻/聲音/g | \ 13 | sed -e s/視頻/影片/g | \ 14 | sed -e s/攝像頭/照相/g | \ 15 | sed -e s/網絡/網路/g | \ 16 | sed -e s/訪問/存取/g | \ 17 | sed -e s/接口/介面/g | \ 18 | sed -e s/反饋/回饋/g | \ 19 | sed -e s/質量/品質/g | \ 20 | sed -e s/聯系人/聯絡人/g | \ 21 | cat 22 | -------------------------------------------------------------------------------- /qt_creator_ide/qtregistering_your_qt_kit.md: -------------------------------------------------------------------------------- 1 | # 注冊你的Qt工具箱(Registering your Qt Kit) 2 | 3 | 最開始使用Qt Creator時最困難的部分可能是Qt Kit。一個Qt Kit由Qt的版本,編譯系統和設備等等其它設置來配置它。它使用唯一標識的工具組合來構建你的項目。一個典型的桌面kit(工具箱)可能包含一個GCC編譯程序,一個Qt版本庫(比如Qt5.1.1)和一個設備(”桌面“)。在你創建好你的項目後你需要為項目指定一個kit(工具箱)來構建項目。在你創建一個kit(工具箱)之前你需要先安裝一個編譯程序並注冊一個Qt版本。Qt版本的注冊由指定qmake的執行路徑完成。Qt Creator通過查詢qmake的信息來獲取Qt的版本標識。 4 | 5 | 添加kit(工具箱)與注冊Qt版本在Settings->Bulild & Run entry中完成,在這裡你也可以查看有哪些編譯程序已經被注冊了的。 6 | 7 | **注意** 8 | 9 | **請首先確保你的Qt Creator中已經注冊了正確的Qt版本,並且確保一個Kit(工具箱)指定了一個編譯程序與Qt版本和設備的組合。你無法離開Kit(工具箱)來構建一個項目。** 10 | -------------------------------------------------------------------------------- /networking/authentication_using_oauth.md: -------------------------------------------------------------------------------- 1 | # 使用開放授權登陸驗證(Authentication using OAuth) 2 | 3 | OAuth是一個開放協議,允許簡單的安全驗證,是來自web的典型方法,用于移動和桌面應用程序。使用OAuth對通常的web服務的客戶端進行身份驗證,例如Google,Facebook和Twitter。 4 | 5 | **注意** 6 | 7 | **對于自定義的web服務,你也可以使用典型的HTTP身份驗證,例如使用XMLHttpRequest的用戶名和密碼的獲取方法(比如xhr.open(verb,url,true,username,password))。** 8 | 9 | Auth目前不是QML/JS的接口,你需要寫一些C++代碼並且將身份驗證導入到QML/JS中。另一個問題是安全的存儲訪問密碼。 10 | 11 | 下面這些是我找到的有用的連接: 12 | 13 | * http://oauth.net 14 | 15 | * http://hueniverse.com/oauth/ 16 | 17 | * https://github.com/pipacs/o2 18 | 19 | * http://www.johanpaul.com/blog/2011/05/oauth2-explained-with-qt-quick/ 20 | -------------------------------------------------------------------------------- /qt_creator_ide/shortcuts.md: -------------------------------------------------------------------------------- 1 | # 快捷鍵(Shortcuts) 2 | 3 | 在好使用的系統中和專業系統中,快捷鍵是不同的。作為專業的開發人員,你也許會在你的應用程序上花很多時間,每一個快捷鍵都能使你的工作效率得到提高。Qt Creator的開發者也這樣想,並且在應用程序中加入了許許多多的快捷鍵。 4 | 5 | 我們列出了一些基本的快捷鍵操作: 6 | 7 | * Ctrl+B - 構建項目 8 | 9 | * Ctrl+R - 運行項目 10 | 11 | * Ctrl+Tab - 切換已打開的文檔 12 | 13 | * Ctrl+k - 打開定位器 14 | 15 | * Esc - 返回 16 | 17 | * F2 - 查找對應的符號解釋。 18 | 19 | * F4 - 在頭文件與源文件之間切換(只對c++代碼有效) 20 | 21 | 這些快捷鍵的定義來自[Qt Creator shortcuts](http://qt-project.org/doc/qtcreator-2.8/creator-keyboard-shortcuts.html)這個文檔。 22 | 23 | **注意** 24 | 25 | **你可以使用設置窗口來編輯你的快捷鍵。** 26 | 27 | ![](http://qmlbook.org/_images/creator-edit-shortcuts.png) 28 | -------------------------------------------------------------------------------- /particle_simulations/concept.md: -------------------------------------------------------------------------------- 1 | # 概念(Concept) 2 | 3 | 粒子模擬的核心是粒子系統(ParticleSystem),它控制了共享時間線。一個場景下可以有多個粒子系統,每個都有自己獨立的時間線。一個粒子使用發射器元素(Emitter)發射,使用粒子畫筆(ParticlePainter)實現可視化,它可以是一張圖片,一個QML項或者一個著色項(shader item)。一個發射器元素(Emitter)也提供向量來控制粒子方向。一個粒子被發送後就再也無法控制。粒子模型提供粒子控制器(Affector),它可以控制已發射粒子的參數。 4 | 5 | 在一個系統中,粒子可以使用粒子群元素(ParticleGroup)來共享移動時間。默認下,每個例子都屬于空("")組。 6 | 7 | ![](http://qmlbook.org/_images/particlesystem.png) 8 | 9 | * 粒子系統(ParticleSystem)- 管理發射器之間的共享時間線。 10 | 11 | * 發射器(Emitter)- 向系統中發射邏輯粒子。 12 | 13 | * 粒子畫筆(ParticlePainter)- 實現粒子可視化。 14 | 15 | * 方向(Direction)- 已發射粒子的向量空間。 16 | 17 | * 粒子組(ParticleGroup)- 每個粒子是一個粒子組的成員。 18 | 19 | * 粒子控制器(Affector)- 控制已發射粒子。 20 | -------------------------------------------------------------------------------- /qt_creator_ide/managing_projects.md: -------------------------------------------------------------------------------- 1 | # 項目管理(Managing Projects) 2 | 3 | Qt Creator在項目中管理你的源代碼。你可以使用File->New File或者Project來創建一個新項目。當你創建一個項目時,你可以選擇多種應用程序模板。Qt Creator 能夠創建桌面,手機應用程序。這些應用程序使用窗口部件(Widgets)或者QtQuick或者控制台,甚至可以是更加簡單的項目。當然也支持HTML5與python的項目。對于一個新手是很難選擇的,所以我們為你選擇了三種類型的項目。 4 | 5 | * 應用程序/QtQuick2.0用戶界面:這將會為你創建一個QML/JS的項目,不需要使用任何的C++代碼。使用這個你可以迅速的創建一個新的用戶界面或者計劃創建一個基于本地插件的現代的用戶界面應用程序。 6 | 7 | * 庫/Qt Quick2.0擴展插件:使用這個安裝引導能夠創建一個你自己的Qt Quick用戶界面插件。這個插件被用來擴展Qt Quick的本地元素。 8 | 9 | * 其它項目/空的Qt項目:只是一個項目的骨架。如果你想從頭使用C++來編寫你的應用程序,你可以使用這種方式。你需要知道你在這裡能做什麼。 10 | 11 | **注意** 12 | 13 | **在這本書的前面部分我們主要使用QtQuick 2.0用戶界面項目。在後面我們會使用空的Qt項目或者類似的項目描述一些C++方面的使用。為了使用我們自己的本地插件來擴展QtQuick,我們將會使用Qt Quick2.0擴展插件安裝引導項目。** 14 | -------------------------------------------------------------------------------- /qt_creator_ide/the_user_interface.md: -------------------------------------------------------------------------------- 1 | # 用戶界面(The User Interface) 2 | 3 | 當你啟動Qt Creator時,你可以看到一個歡迎畫面。在這裡你可以找到怎樣在Qt Creator中繼續的重要提示,或者你最近使用的項目。你可以看到一個會話列表,你可以看到是一個空的。一個會話是供你參考使用的一堆項目的集合。當你在同時擁有幾個客戶的大項目時,這個功能非常有用。 4 | 5 | 你可以在左邊看到模式選擇。模式選擇包含了你典型的工作步驟。 6 | 7 | * 歡迎模式:你目前所在的位置。 8 | 9 | * 編輯模式:專注于編碼。 10 | 11 | * 設計模式:專注于用戶界面設計。 12 | 13 | * 調試模式:獲取當前運行程序的相關信息。 14 | 15 | * 項目模式:修改你的項目編譯運行配置。 16 | 17 | * 分析模式:檢查內存洩露並剖析。 18 | 19 | * 幫助模式:閱讀Qt的幫助文檔。 20 | 21 | 在模式選擇下面你可以找到項目配置選擇與執行/調試。 22 | 23 | ![](http://qmlbook.org/_images/creator-welcome.png) 24 | 25 | 你應該大多數時間都處于編輯模式的中央面板中的代碼編輯器編輯你的代碼。當你需要配置你的項目時,你將不時的訪問項目模式。當你點擊Run(運行)。Qt Creator會先確保充分的構建你的項目後再運行它。 26 | 27 | 在最下面的輸出窗是錯誤信息,應用程序信息,編譯信息和其它的信息。 28 | -------------------------------------------------------------------------------- /canvas_element/gradients.md: -------------------------------------------------------------------------------- 1 | # 漸變(Gradients) 2 | 3 | 畫布中可以使用顏色填充也可以使用漸變或者圖像來填充。 4 | 5 | ``` 6 | onPaint: { 7 | var ctx = getContext("2d") 8 | 9 | var gradient = ctx.createLinearGradient(100,0,100,200) 10 | gradient.addColorStop(0, "blue") 11 | gradient.addColorStop(0.5, "lightsteelblue") 12 | ctx.fillStyle = gradient 13 | ctx.fillRect(50,50,100,100) 14 | } 15 | ``` 16 | 17 | 在這個例子中,漸變色定義在開始點(100,0)到結束點(100,200)。在我們畫布中是一個中間垂直的線。漸變色在停止點定義一個顏色,範圍從0.0到1.0。這裡我們使用一個藍色作為0.0(100,0),一個高亮剛藍色作為0.5(100,200)。漸變色的定義比我們想要繪制的矩形更大,所以矩形在它定義的範圍內對漸變進行了裁剪。 18 | 19 | ![](http://qmlbook.org/_images/gradient.png) 20 | 21 | **注意** 22 | 23 | **漸變色是在畫布坐標下定義的,而不是在繪制路徑相對坐標下定義的。畫布中沒有相對坐標的概念。** 24 | -------------------------------------------------------------------------------- /canvas_element/convenient_api.md: -------------------------------------------------------------------------------- 1 | # 便捷的接口(Convenient API) 2 | 3 | 在繪制矩形時,我們提供了一個便捷的接口,而不需要調用stroke或者fill來完成。 4 | 5 | ``` 6 | // convenient.qml 7 | 8 | import QtQuick 2.0 9 | 10 | Canvas { 11 | id: root 12 | width: 120; height: 120 13 | onPaint: { 14 | var ctx = getContext("2d") 15 | ctx.fillStyle = 'green' 16 | ctx.strokeStyle = "blue" 17 | ctx.lineWidth = 4 18 | 19 | // draw a filles rectangle 20 | ctx.fillRect(20, 20, 80, 80) 21 | // cut our an inner rectangle 22 | ctx.clearRect(30,30, 60, 60) 23 | // stroke a border from top-left to 24 | // inner center of the larger rectangle 25 | ctx.strokeRect(20,20, 40, 40) 26 | } 27 | } 28 | ``` 29 | 30 | ![](http://qmlbook.org/_images/convenient.png) 31 | 32 | **注意** 33 | 34 | **畫筆的繪制區域由中間向兩邊延展。一個寬度為4像素的畫筆將會在繪制路徑的裡面繪制2個像素,外面繪制2個像素。** 35 | -------------------------------------------------------------------------------- /meet_qt_5/preface.md: -------------------------------------------------------------------------------- 1 | # 序(Preface) 2 | 3 | **歷史** 4 | 5 | Qt4自2005年發布以來向成千上萬的應用程序提供了開發框架,甚至是完整的桌面與移動系統。在最近幾年計算機的使用模式發生了改變。從PC機向可攜式設備和移動電腦發展。傳統的桌面設備被越來越多的基於觸摸屏的手機設備取代。桌面用戶的體驗模式也在發生改變。在過去,Windows UI佔據了我們的世界,但現在我們會花更多的時間在其它的UI上。 6 | 7 | Qt4設計用於滿足在大多數主流平台的桌面上有一個可以使用的UI視窗。如今Qt的開發者面臨新的問題,它將提供更多的觸摸驅動的使用界面,並且適用於大多數主流桌面與移動系統。Qt4.7開始引進了QtQuick技術,允許用戶設計一個滿足客戶需求的,從簡單的元素來實現一個完整的新的用戶界面。 8 | 9 | ## 1.1.1 Qt5 焦點(Qt5 Focus) 10 | 11 | Qt5是Qt4版本完整的更新,到Qt4.8版本,Qt4已經發布了7年。是時候讓這個令人驚奇的工具更加驚奇了。 12 | 13 | Qt5主要焦點如下: 14 | 15 | * 傑出的圖形繪制:Qt Quick2是基於OpenGL(ES)場景的實現。重新設計的圖形引擎可以得到更加好的圖形效果與更加簡單的使用方法,在這一領域是之前是從未實現的。 16 | 17 | * 開發者生產率:QML和JavaScript語言是主要用於設計UI的方法。後端將有C++來完成繪制。將JavaScript與C++分開能夠快速的開發,讓前端的開發人員專注於建立漂亮的用戶界面,後端的C++開發人員專注於穩定,性能和擴展。 18 | 19 | * 跨平台移植性:基於Qt平台的統一抽象概念,現在可以更加容易和快速的將Qt移植到更多的平台上。Qt5是一個Qt核心元件和附加元件的概念,系統開發者只需要專注於必要模塊的實現,可以使程序更加效率的運行。 20 | 21 | * 開放的開發:Qt是由Qt-Porject(qt-project.org)主持,它的開發是開放的,由Qt社區驅動的。 22 | -------------------------------------------------------------------------------- /networking/templating.md: -------------------------------------------------------------------------------- 1 | # 模板(Templating) 2 | 3 | 當使用HTML項目時,通常需要使用模板驅動開發。服務器使用模板機制生成代碼在服務器端對一個HTML根進行擴展。例如一個照片列表的列表頭將使用HTML編碼,動態圖片鏈表將會使用模板機制動態生成。通常這也可以使用QML解決,但是仍然有一些問題。 4 | 5 | 首先,HTML開發者這樣做的原因是為了克服HTML後端的限制。在HTML中沒有組件模型,動態機制方面不得不使用這些機制或者在客戶端邊使用javascript編程。很多的JS框架產生(jQuery,dojo,backbone,angular,...)可以用來解決這個問題,把更多的邏輯問題放在使用網絡服務連接的客戶端瀏覽器。客戶端使用一個web服務的接口(例如JSON服務,或者XML數據服務)與服務器通信。這也適用于QML。 6 | 7 | 第二個問題是來自QML的組件緩衝。當QML訪問一個組件時,緩衝渲染樹(render-tree),並且只加載緩衝版本來渲染。磁盤上的修改版本或者遠程的修改在沒有重新啟動客戶端時不會被檢測到。為了克服這個問題,我們需要跟蹤。我們使用URL後綴來加載鏈接(例如[http://localhost:8080/main.qml#1234](http://localhost:8080/main.qml#1234)),“#1234”就是後綴標識。HTTP服務器總是為相同的文檔服務,但是QML將使用完整的鏈接來保存這個文檔,包括鏈接標識。每次我們訪問的這個鏈接的標識獲得改變,QML緩衝無法獲得這個信息。這個後綴標識可以是當前時間的毫秒或者一個隨機數。 8 | 9 | ``` 10 | Loader { 11 | source: 'http://localhost:8080/main.qml#' + new Date().getTime() 12 | } 13 | ``` 14 | 15 | 總之,模板可以實現,但是不推薦,無法完整發揮QML的長處。一個更好的方法是使用web服務提供JSON或者XML數據服務。 16 | -------------------------------------------------------------------------------- /canvas_element/shadows.md: -------------------------------------------------------------------------------- 1 | # 陰影(Shadows) 2 | 3 | **注意** 4 | 5 | **在Qt5的alpha版本中,我們使用陰影遇到了一些問題。** 6 | 7 | 2D對象的路徑可以使用陰影增強顯示效果。陰影是一個區域的輪廓線使用偏移量,顏色和模糊來實現的。所以你需要指定一個陰影顏色(shadowColor),陰影X軸偏移值(shadowOffsetX),陰影Y軸偏移值(shadowOffsetY)和陰影模糊(shadowBlur)。這些參數的定義都使用2D context來定義。2D context是唯一的繪制操作接口。 8 | 9 | 陰影也可以用來創建發光的效果。在下面的例子中我們使用白色的光創建了一個“Earth”的文本。在一個黑色的背景上可以有更加好的顯示效果。 10 | 11 | 首先我們繪制黑色背景: 12 | 13 | ``` 14 | // setup a dark background 15 | ctx.strokeStyle = "#333" 16 | ctx.fillRect(0,0,canvas.width,canvas.height); 17 | ``` 18 | 19 | 然後定義我們的陰影配置: 20 | 21 | ``` 22 | ctx.shadowColor = "blue"; 23 | ctx.shadowOffsetX = 2; 24 | ctx.shadowOffsetY = 2; 25 | // next line crashes 26 | // ctx.shadowBlur = 10; 27 | ``` 28 | 29 | 最後我們使用加粗的,80像素寬度的Ubuntu字體來繪制“Earth”文本: 30 | 31 | ``` 32 | ctx.font = 'Bold 80px Ubuntu'; 33 | ctx.fillStyle = "#33a9ff"; 34 | ctx.fillText("Earth",30,180); 35 | ``` 36 | -------------------------------------------------------------------------------- /shader_effect/README.md: -------------------------------------------------------------------------------- 1 | # Shader Effect 2 | 3 | **注意** 4 | 5 | **最後一次構建:2014年1月20日下午18:00。** 6 | 7 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 8 | 9 | * http://labs.qt.nokia.com/2012/02/02/qt-graphical-effects-in-qt-labs/ 10 | 11 | * http://labs.qt.nokia.com/2011/05/03/qml-shadereffectitem-on-qgraphicsview/ 12 | 13 | * http://qt-project.org/doc/qt-4.8/declarative-shadereffects.html 14 | 15 | * http://www.opengl.org/registry/doc/GLSLangSpec.4.20.6.clean.pdf 16 | 17 | * http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf 18 | 19 | * http://www.lighthouse3d.com/opengl/glsl/ 20 | 21 | * http://wiki.delphigl.com/index.php/Tutorial_glsl 22 | 23 | * [Qt5Doc qtquick-shaders](http://doc.qt.nokia.com/5.0-snapshot/qtquick-shaders.html) 24 | 25 | 著色器允許我們利用SceneGraph的接口直接調用在強大的GPU上運行的OpenGL來創建渲染效果。著色器使用ShaderEffect與ShaderEffectSource元素來實現。著色器本身的算法使用OpenGL Shading Language(OpenGL著色語言)來實現。 26 | 27 | 實際上這意味著你需要混合使用QML代碼與著色器代碼。執行時,會將著色器代碼發送到GPU,並在GPU上編譯執行。QML著色器元素(Shader QML Elements)允許你與OpenGL著色器程序的屬性交互。 28 | 29 | 讓我們首先來看看OpenGL著色器。 30 | -------------------------------------------------------------------------------- /canvas_element/transformation.md: -------------------------------------------------------------------------------- 1 | # 轉換(Transformation) 2 | 3 | 畫布有多種方式來轉換坐標系。這些操作非常類似于QML元素的轉換。你可以通過縮放(scale),旋轉(rotate),translate(移動)來轉換坐標系。與QML元素的轉換不同的是,轉換原點通常就是畫布原點。例如,從中心點放大一個封閉的路徑,你需要先將畫布原點移動到整個封閉的路徑的中心點上。使用這些轉換的方法你可以創建一些更加復雜的轉換。 4 | 5 | ``` 6 | // transform.qml 7 | 8 | import QtQuick 2.0 9 | 10 | Canvas { 11 | id: root 12 | width: 240; height: 120 13 | onPaint: { 14 | var ctx = getContext("2d") 15 | ctx.strokeStyle = "blue" 16 | ctx.lineWidth = 4 17 | 18 | ctx.beginPath() 19 | ctx.rect(-20, -20, 40, 40) 20 | ctx.translate(120,60) 21 | ctx.stroke() 22 | 23 | // draw path now rotated 24 | ctx.strokeStyle = "green" 25 | ctx.rotate(Math.PI/4) 26 | ctx.stroke() 27 | } 28 | } 29 | ``` 30 | 31 | ![](http://qmlbook.org/_images/transform.png) 32 | 33 | 除了移動畫布外,也可以使用scale(x,y)來縮放x,y坐標軸。旋轉使用rotate(angle),angle是角度(360度=2*Math.PI)。使用setTransform(m11,m12,m21,m22,dx,dy)來完成矩陣轉換。 34 | 35 | **警告** 36 | 37 | **QML畫布中的轉換與HTML5畫布中的機制有些不同。不確定這是不是一個Bug。** 38 | 39 | **注意** 40 | 41 | **重置矩陣你可以調用resetTransform()函數來完成,這個函數會將轉換矩陣還原為單位矩陣。** 42 | -------------------------------------------------------------------------------- /particle_simulations/particle_painter.md: -------------------------------------------------------------------------------- 1 | # 粒子畫筆(Particle Painter) 2 | 3 | 到目前為止我們只使用了基于粒子畫筆的圖像來實現粒子可視化。Qt也提供了一些其它的粒子畫筆: 4 | 5 | * 粒子項(ItemParticle):基于粒子畫筆的代理 6 | 7 | * 自定義粒子(CustomParticle):基于粒子畫筆的著色器 8 | 9 | 粒子項可以將QML元素項作為粒子發射。你需要制定自己的粒子代理。 10 | 11 | ``` 12 | ItemParticle { 13 | id: particle 14 | system: particleSystem 15 | delegate: itemDelegate 16 | } 17 | ``` 18 | 19 | 在這個例子中,我們的代理是一個隨機圖片(使用Math.random()完成),有著白色邊框和隨機大小。 20 | 21 | ``` 22 | Component { 23 | id: itemDelegate 24 | Rectangle { 25 | id: container 26 | width: 32*Math.ceil(Math.random()*3); height: width 27 | color: 'white' 28 | Image { 29 | anchors.fill: parent 30 | anchors.margins: 4 31 | source: 'assets/fruits'+Math.ceil(Math.random()*10)+'.jpg' 32 | } 33 | } 34 | } 35 | ``` 36 | 37 | 每秒發出四個粒子,每個粒子擁有4秒的生命週期。粒子自動淡入淡出。 38 | 39 | ![](http://qmlbook.org/_images/itemparticle.png) 40 | 41 | 對于更多的動態情況,也可以由你自己創建一個子項,讓粒子系統來控制它,使用take(item, priority)來完成。粒子系統控制你的粒子就像控制普通的粒子一樣。你可以使用give(item)來拿回子項的控制權。你也可以操作子項粒子,甚至可以使用freeze(item)來停止它,使用unfreeze(item)來恢復它。 42 | -------------------------------------------------------------------------------- /multimedia/sounds_effects.md: -------------------------------------------------------------------------------- 1 | # 聲音效果(Sounds Effects) 2 | 3 | 當播放聲音效果時,從請求播放到真實響應播放的響應時間非常重要。在這種情況下,SoundEffect元素將會派上用場。設置source屬性,一個簡單調用play函數會直接開始播放。 4 | 5 | 當敲擊屏幕時,可以使用它來完成音效反饋,如下所示: 6 | 7 | ``` 8 | SoundEffect { 9 | id: beep 10 | source: "beep.wav" 11 | } 12 | 13 | Rectangle { 14 | id: button 15 | 16 | anchors.centerIn: parent 17 | 18 | width: 200 19 | height: 100 20 | 21 | color: "red" 22 | 23 | MouseArea { 24 | anchors.fill: parent 25 | onClicked: beep.play() 26 | } 27 | } 28 | ``` 29 | 30 | 這個元素也可以用來完成一個配有音效的轉換。為了從轉換觸發,使用ScriptAction元素。 31 | 32 | ``` 33 | SoundEffect { 34 | id: swosh 35 | source: "swosh.wav" 36 | } 37 | 38 | transitions: [ 39 | Transition { 40 | ParallelAnimation { 41 | ScriptAction { script: swosh.play(); } 42 | PropertyAnimation { properties: "rotation"; duration: 200; } 43 | } 44 | } 45 | ] 46 | ``` 47 | 48 | 除了調用play函數,在MediaPlayer中類似屬性也可以使用。比如volume和loops。loops可以設置為SoundEffect.Infinite來提供無限重復播放。停止播放調用stop函數。 49 | 50 | **注意** 51 | 52 | **當後台使用PulseAudio時,stop將不會立即停止,但會阻止繼續循環。這是由于底層API的限制造成的。** 53 | -------------------------------------------------------------------------------- /particle_simulations/particle_parameters.md: -------------------------------------------------------------------------------- 1 | # 粒子參數(Particle Parameters) 2 | 3 | 我們已經知道通過改變發射器的行為就可以改變我們的粒子模擬。粒子畫筆被用來繪制每一個粒子。 4 | 回到我們之前的粒子中,我們更新一下我們的圖片粒子畫筆(ImageParticle)。首先我們改變粒子圖片為一個小的星形圖片: 5 | 6 | ``` 7 | ImageParticle { 8 | ... 9 | source: 'assets/star.png' 10 | } 11 | ``` 12 | 13 | 粒子使用金色來進行初始化,不同的粒子顏色變化範圍為+/- 20%。 14 | 15 | ``` 16 | color: '#FFD700' 17 | colorVariation: 0.2 18 | ``` 19 | 20 | 為了讓場景更加生動,我們需要旋轉粒子。每個粒子首先按順時針旋轉15度,不同的粒子在+/-5度之間變化。每個例子會不斷的以每秒45度旋轉。每個粒子的旋轉速度在+/-15度之間變化: 21 | 22 | ``` 23 | rotation: 15 24 | rotationVariation: 5 25 | rotationVelocity: 45 26 | rotationVelocityVariation: 15 27 | ``` 28 | 29 | 最後,我們改變粒子的入場效果。 這個效果是粒子產生時的效果,在這個例子中,我們希望使用一個縮放效果: 30 | 31 | ``` 32 | entryEffect: ImageParticle.Scale 33 | ``` 34 | 35 | 現在我們可以看到旋轉的星星出現在我們的屏幕上。 36 | 37 | ![](http://qmlbook.org/_images/particleparameters.png) 38 | 39 | 下面是我們如何改變圖片粒子畫筆的代碼段。 40 | 41 | ``` 42 | ImageParticle { 43 | source: "assets/star.png" 44 | system: particleSystem 45 | color: '#FFD700' 46 | colorVariation: 0.2 47 | rotation: 0 48 | rotationVariation: 45 49 | rotationVelocity: 15 50 | rotationVelocityVariation: 15 51 | entryEffect: ImageParticle.Scale 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /canvas_element/images.md: -------------------------------------------------------------------------------- 1 | # 圖片(Images) 2 | 3 | QML畫布支持多種資源的圖片繪制。在畫布中使用一個圖片需要先加載圖片資源。在我們的例子中我們使用Component.onCompleted操作來加載圖片。 4 | 5 | ``` 6 | onPaint: { 7 | var ctx = getContext("2d") 8 | 9 | 10 | // draw an image 11 | ctx.drawImage('assets/ball.png', 10, 10) 12 | 13 | // store current context setup 14 | ctx.save() 15 | ctx.strokeStyle = 'red' 16 | // create a triangle as clip region 17 | ctx.beginPath() 18 | ctx.moveTo(10,10) 19 | ctx.lineTo(55,10) 20 | ctx.lineTo(35,55) 21 | ctx.closePath() 22 | // translate coordinate system 23 | ctx.translate(100,0) 24 | ctx.clip() // create clip from triangle path 25 | // draw image with clip applied 26 | ctx.drawImage('assets/ball.png', 10, 10) 27 | // draw stroke around path 28 | ctx.stroke() 29 | // restore previous setup 30 | ctx.restore() 31 | 32 | } 33 | 34 | Component.onCompleted: { 35 | loadImage("assets/ball.png") 36 | } 37 | ``` 38 | 39 | 在左邊,足球圖片使用10×10的大小繪制在左上方的位置。在右邊我們對足球圖片進行了裁剪。圖片或者輪廓路徑都可以使用一個路徑來裁剪。裁剪需要定義一個裁剪路徑,然後調用clip()函數來實現裁剪。在clip()之前所有的繪制操作都會用來進行裁剪。如果還原了之前的狀態或者定義裁剪區域為整個畫布時,裁剪是無效的。 40 | 41 | ![](http://qmlbook.org/_images/canvas_image.png) 42 | -------------------------------------------------------------------------------- /networking/local_files.md: -------------------------------------------------------------------------------- 1 | # 本地文件(Local files) 2 | 3 | 使用XMLHttpRequest也可以加載本地文件(XML/JSON)。例如加載一個本地名為“colors.json”的文件可以這樣使用: 4 | 5 | ``` 6 | xhr.open("GET", "colors.json"); 7 | ``` 8 | 9 | 我們使用它讀取一個顏色表並且使用表格來顯示。從QtQuick這邊無法修改文件。為了將源數據存儲回去,我們需要一個基于HTTP服務器的REST服務支持或者一個用來訪問文件的QtQuick擴展。 10 | 11 | ``` 12 | import QtQuick 2.0 13 | 14 | Rectangle { 15 | width: 360 16 | height: 360 17 | color: '#000' 18 | 19 | GridView { 20 | id: view 21 | anchors.fill: parent 22 | cellWidth: width/4 23 | cellHeight: cellWidth 24 | delegate: Rectangle { 25 | width: view.cellWidth 26 | height: view.cellHeight 27 | color: modelData.value 28 | } 29 | } 30 | 31 | function request() { 32 | var xhr = new XMLHttpRequest(); 33 | xhr.onreadystatechange = function() { 34 | if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) { 35 | print('HEADERS_RECEIVED') 36 | } else if(xhr.readyState === XMLHttpRequest.DONE) { 37 | print('DONE'); 38 | var obj = JSON.parse(xhr.responseText.toString()); 39 | view.model = obj.colors 40 | } 41 | } 42 | xhr.open("GET", "colors.json"); 43 | xhr.send(); 44 | } 45 | 46 | Component.onCompleted: { 47 | request() 48 | } 49 | } 50 | ``` 51 | 52 | 也可以使用XmlListModel來替代XMLHttpRequest訪問本地文件。 53 | 54 | ``` 55 | import QtQuick.XmlListModel 2.0 56 | 57 | XmlListModel { 58 | source: "http://localhost:8080/colors.xml" 59 | query: "/colors" 60 | XmlRole { name: 'color'; query: 'name/string()' } 61 | XmlRole { name: 'value'; query: 'value/string()' } 62 | } 63 | ``` 64 | 65 | XmlListModel只能用來讀取XML文件,不能讀取JSON文件。 66 | -------------------------------------------------------------------------------- /canvas_element/pixels_buffer.md: -------------------------------------------------------------------------------- 1 | # 像素緩衝(Pixels Buffer) 2 | 3 | 當你使用畫布時,你可以檢索讀取畫布上的像素數據,或者操作畫布上的像素。讀取圖像數據使用createImageData(sw,sh)或者getImageData(sx,sy,sw,sh)。這兩個函數都會返回一個包含寬度(width),高度(height)和數據(data)的圖像數據(ImageData)對象。圖像數據包含了一維數組像素數據,使用RGBA格式進行檢索。每個數據的數據範圍在0到255之間。設置畫布的像素數據你可以使用putImageData(imagedata,dx,dy)函數來完成。 4 | 5 | 另一種檢索畫布內容的方法是將畫布的數據存儲進一張圖片中。可以使用畫布的函數save(path)或者toDataURL(mimeType)來完成,toDataURL(mimeType)會返回一個圖片的地址,這個鏈接可以直接用Image元素來讀取。 6 | 7 | ``` 8 | import QtQuick 2.0 9 | 10 | Rectangle { 11 | width: 240; height: 120 12 | Canvas { 13 | id: canvas 14 | x: 10; y: 10 15 | width: 100; height: 100 16 | property real hue: 0.0 17 | onPaint: { 18 | var ctx = getContext("2d") 19 | var x = 10 + Math.random(80)*80 20 | var y = 10 + Math.random(80)*80 21 | hue += Math.random()*0.1 22 | if(hue > 1.0) { hue -= 1 } 23 | ctx.globalAlpha = 0.7 24 | ctx.fillStyle = Qt.hsla(hue, 0.5, 0.5, 1.0) 25 | ctx.beginPath() 26 | ctx.moveTo(x+5,y) 27 | ctx.arc(x,y, x/10, 0, 360) 28 | ctx.closePath() 29 | ctx.fill() 30 | } 31 | MouseArea { 32 | anchors.fill: parent 33 | onClicked: { 34 | var url = canvas.toDataURL('image/png') 35 | print('image url=', url) 36 | image.source = url 37 | } 38 | } 39 | } 40 | 41 | Image { 42 | id: image 43 | x: 130; y: 10 44 | width: 100; height: 100 45 | } 46 | 47 | Timer { 48 | interval: 1000 49 | running: true 50 | triggeredOnStart: true 51 | repeat: true 52 | onTriggered: canvas.requestPaint() 53 | } 54 | } 55 | ``` 56 | 57 | 在我們這個例子中,我們每秒在左邊的畫布中繪制一個的圓形。當使用鼠標點擊畫布內容時,會將內容存儲為一個圖片鏈接。在右邊將會展示這個存儲的圖片。 58 | 59 | **注意** 60 | 61 | **在Qt5的Alpha版本中,檢索圖像數據似乎不能工作。** 62 | -------------------------------------------------------------------------------- /canvas_element/composition_mode.md: -------------------------------------------------------------------------------- 1 | # 組合模式(Composition Mode) 2 | 3 | 組合允許你繪制一個形狀然後與已有的像素點集合混合。畫布提供了多種組合模式,使用globalCompositeOperation(mode)來設置。 4 | 5 | * "source-over" 6 | 7 | * "source-in" 8 | 9 | * "source-out" 10 | 11 | * "source-atop" 12 | 13 | ``` 14 | onPaint: { 15 | var ctx = getContext("2d") 16 | ctx.globalCompositeOperation = "xor" 17 | ctx.fillStyle = "#33a9ff" 18 | 19 | for(var i=0; i<40; i++) { 20 | ctx.beginPath() 21 | ctx.arc(Math.random()*400, Math.random()*200, 20, 0, 2*Math.PI) 22 | ctx.closePath() 23 | ctx.fill() 24 | } 25 | } 26 | ``` 27 | 28 | 下面這個例子遍歷了列表中的組合模式,使用對應的組合模式生成了一個矩形與圓形的組合。 29 | 30 | ``` 31 | property var operation : [ 32 | 'source-over', 'source-in', 'source-over', 33 | 'source-atop', 'destination-over', 'destination-in', 34 | 'destination-out', 'destination-atop', 'lighter', 35 | 'copy', 'xor', 'qt-clear', 'qt-destination', 36 | 'qt-multiply', 'qt-screen', 'qt-overlay', 'qt-darken', 37 | 'qt-lighten', 'qt-color-dodge', 'qt-color-burn', 38 | 'qt-hard-light', 'qt-soft-light', 'qt-difference', 39 | 'qt-exclusion' 40 | ] 41 | 42 | onPaint: { 43 | var ctx = getContext('2d') 44 | 45 | for(var i=0; i= items.count) 42 | { 43 | index = -1; 44 | mediaPlayer.source = ""; 45 | } 46 | else 47 | mediaPlayer.source = items.get(index).source; 48 | } 49 | 50 | function next() 51 | { 52 | setIndex(index + 1); 53 | } 54 | 55 | function previous() 56 | { 57 | setIndex(index + 1); 58 | } 59 | ``` 60 | 61 | 讓播放列表自動播放下一個元素的訣竅是使用MediaPlayer的status屬性。當得到MediaPlayer.EndOfMedia狀態時,索引值增加,恢復播放,或者當列表達到最後時,停止播放。 62 | 63 | ``` 64 | Connections { 65 | target: root.mediaPlayer 66 | 67 | onStopped: { 68 | if (root.mediaPlayer.status == MediaPlayer.EndOfMedia) 69 | { 70 | root.next(); 71 | if (root.index == -1) 72 | root.mediaPlayer.stop(); 73 | else 74 | root.mediaPlayer.play(); 75 | } 76 | } 77 | } 78 | ``` 79 | -------------------------------------------------------------------------------- /shader_effect/wave_effect.md: -------------------------------------------------------------------------------- 1 | # 波浪效果(Wave Effect) 2 | 3 | 在這個更加復雜的例子中,我們使用片段著色器創建一個波浪效果。波浪的形成是基于sin曲線,並且它影響了使用的紋理坐標的顏色。 4 | 5 | ``` 6 | import QtQuick 2.0 7 | 8 | Rectangle { 9 | width: 480; height: 240 10 | color: '#1e1e1e' 11 | 12 | Row { 13 | anchors.centerIn: parent 14 | spacing: 20 15 | Image { 16 | id: sourceImage 17 | width: 160; height: width 18 | source: "assets/coastline.jpg" 19 | } 20 | ShaderEffect { 21 | width: 160; height: width 22 | property variant source: sourceImage 23 | property real frequency: 8 24 | property real amplitude: 0.1 25 | property real time: 0.0 26 | NumberAnimation on time { 27 | from: 0; to: Math.PI*2; duration: 1000; loops: Animation.Infinite 28 | } 29 | 30 | fragmentShader: " 31 | varying highp vec2 qt_TexCoord0; 32 | uniform sampler2D source; 33 | uniform lowp float qt_Opacity; 34 | uniform highp float frequency; 35 | uniform highp float amplitude; 36 | uniform highp float time; 37 | void main() { 38 | highp vec2 pulse = sin(time - frequency * qt_TexCoord0); 39 | highp vec2 coord = qt_TexCoord0 + amplitude * vec2(pulse.x, -pulse.x); 40 | gl_FragColor = texture2D(source, coord) * qt_Opacity; 41 | }" 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | 波浪的計算是基于一個脈衝與紋理坐標的操作。我們使用一個基于當前時間與使用的紋理坐標的sin波浪方程式來實現脈衝。 48 | 49 | ``` 50 | highp vec2 pulse = sin(time - frequency * qt_TexCoord0); 51 | ``` 52 | 53 | 離開了時間的因素,我們僅僅只有扭曲,而不是像波浪一樣運動的扭曲。 54 | 55 | 我們使用不同的紋理坐標作為顏色。 56 | 57 | ``` 58 | highp vec2 coord = qt_TexCoord0 + amplitude * vec2(pulse.x, -pulse.x); 59 | ``` 60 | 61 | 紋理坐標受我們的x脈衝值影響,結果就像一個移動的波浪。 62 | 63 | ![](http://qmlbook.org/_images/wave.png) 64 | 65 | 如果我們沒有在片段著色器中使用像素的移動,這個效果可以首先考慮使用頂點著色器來完成。 66 | -------------------------------------------------------------------------------- /get_start/hello_world.md: -------------------------------------------------------------------------------- 1 | # 你好世界(Hello World) 2 | 3 | 為了測試你的安裝,我們創建一個簡單的應用程序hello world.打開Qt Creator並且創建一個Qt Quick UI Project(File->New File 或者 Project-> Qt Quick Project -> Qt Quick UI)並且給項目取名 HelloWorld。 4 | 5 | **注意** 6 | 7 | **Qt Creator集成開發環境允許你創建不同類型的應用程序。如果沒有另外說明,我們都創建Qt Quick UI Project。** 8 | 9 | **提示** 10 | 11 | **一個典型的Qt Quick應用程序在運行時解釋,與本地插件或者本地代碼在運行時解釋代碼一樣。對于才開始的我們不需要關注本地端的解釋開發,只需要把注意力集中在Qt5運行時的方面。** 12 | 13 | Qt Creator將會為我們創建幾個文件。HellWorld.qmlproject文件是項目文件,保存了項目的配置信息。這個文件由Qt Creator管理,我們不需要編輯它。 14 | 15 | 另一個文件HelloWorld.qml保存我們應用程序的代碼。打開它,並且嘗試想想這個應用程序要做什麼,然後再繼續讀下去。 16 | 17 | ``` 18 | // HelloWorld.qml 19 | 20 | import QtQuick 2.0 21 | 22 | Rectangle { 23 | width: 360 24 | height: 360 25 | Text { 26 | anchors.centerIn: parent 27 | text: "Hello World" 28 | } 29 | MouseArea { 30 | anchors.fill: parent 31 | onClicked: { 32 | Qt.quit(); 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | HelloWorld.qml使用QML語言來編寫。我們將在下一章更深入的討論QML語言,現在只需要知道它描述了一系列有層次的用戶界面。這個代碼指定了顯示一個360乘以360像素的一個矩形,矩形中間有一個“Hello World"的文本。鼠標區域覆蓋了整個矩形,當用戶點擊它時,程序就會退出。 39 | 40 | 你自己可以運行這個應用程序,點擊左邊的運行或者從菜單選擇select Bulid->Run。 41 | 42 | 如果一切順利,你將看到下面這個窗口: 43 | 44 | ![](http://qmlbook.org/_images/example.png) 45 | 46 | Qt 5似乎已經可以工作了,我們接著繼續。 47 | 48 | **建議** 49 | 50 | **如果你是一個名系統集成人員,你會想要安裝最新穩定的Qt版本,將這個Qt版本的源代碼編譯到你特定的目標機器上。** 51 | 52 | **從頭開始構建** 53 | 54 | 如果你想使用命令行的方式構建Qt5,你首先需要拷貝一個代碼庫並構建他。 55 | 56 | ``` 57 | git clone git://gitorious.org/qt/qt5.git 58 | cd qt5 59 | ./init-repository 60 | ./configure -prefix $PWD/qtbase -opensource 61 | make -j4 62 | ``` 63 | 64 | 等待兩杯咖啡的時間編譯完成後,qtbase文件夾中將會出現可以使用的Qt5。任何飲料都好,不過我喜歡喝著咖啡等待最好的結果。 65 | 66 | 如果你想測試你的編譯,只需簡單的啟動qtbase/bin/qmlscene並且選擇一個QtQuick的例子運行,或者跟著我們進入下一章。 67 | 68 | 為了測試你的安裝,我們創建了一個簡單的hello world應用程序。創建一個空的qml文件example1.qml,使用你最喜愛的文本編輯器並且粘貼一下內容: 69 | 70 | ``` 71 | // HelloWorld.qml 72 | 73 | import QtQuick 2.0 74 | 75 | Rectangle { 76 | width: 360 77 | height: 360 78 | Text { 79 | anchors.centerIn: parent 80 | text: "Greetings from Qt5" 81 | } 82 | MouseArea { 83 | anchors.fill: parent 84 | onClicked: { 85 | Qt.quit(); 86 | } 87 | } 88 | } 89 | ``` 90 | 91 | 你現在使用來自Qt5的默認運行環境來可以運行這個例子: 92 | 93 | ``` 94 | $ qtbase/bin/qmlscene 95 | ``` 96 | -------------------------------------------------------------------------------- /particle_simulations/simple_simulation.md: -------------------------------------------------------------------------------- 1 | # 簡單的模擬(Simple Simulation) 2 | 3 | 讓我們從一個簡單的模擬開始學習。Qt Quick使用簡單的粒子渲染非常簡單。下面是我們需要的: 4 | 5 | * 綁定所有元素到一個模擬的粒子系統(ParticleSystem)。 6 | 7 | * 一個向系統發射粒子的發射器(Emitter)。 8 | 9 | * 一個ParticlePainter派生元素,用來實現粒子的可視化。 10 | 11 | ``` 12 | import QtQuick 2.0 13 | import QtQuick.Particles 2.0 14 | 15 | Rectangle { 16 | id: root 17 | width: 480; height: 160 18 | color: "#1f1f1f" 19 | 20 | ParticleSystem { 21 | id: particleSystem 22 | } 23 | 24 | Emitter { 25 | id: emitter 26 | anchors.centerIn: parent 27 | width: 160; height: 80 28 | system: particleSystem 29 | emitRate: 10 30 | lifeSpan: 1000 31 | lifeSpanVariation: 500 32 | size: 16 33 | endSize: 32 34 | Tracer { color: 'green' } 35 | } 36 | 37 | ImageParticle { 38 | source: "assets/particle.png" 39 | system: particleSystem 40 | } 41 | } 42 | ``` 43 | 44 | 例子的運行結果如下所示: 45 | 46 | ![](http://qmlbook.org/_images/simpleparticles.png) 47 | 48 | 我們使用一個80x80的黑色矩形框作為我們的根元素和背景。然後我們定義一個粒子系統(ParticleSystem)。這通常是粒子系統綁定所有元素的第一步。下一個元素是發射器(Emitter),它定義了基于矩形框的發射區域和發射粒子的基礎屬性。發射器使用system屬性與粒子系統進行綁定。 49 | 50 | 在這個例子中,發射器每秒發射10個粒子(emitRate:10)到發射器的區域,每個粒子的生命週期是1000毫秒(lifeSpan:1000),一個已發射粒子的生命週期變化是500毫秒(lifeSpanVariation:500)。一個粒子開始的大小是16個像素(size:16),生命週期結束時的大小是32個像素(endSize:32)。 51 | 52 | 綠色邊框的矩形是一個跟蹤元素,用來顯示發射器的幾何形狀。這個可視化展示了粒子在發射器矩形框內發射,但是渲染效果不被限制在發射器的矩形框內。渲染位置依賴于粒子的壽命和方向。這將幫助我們更加清楚的知道如何改變粒子的方向。 53 | 54 | 發射器發射邏輯粒子。一個邏輯粒子的可視化使用粒子畫筆(ParticlePainter)來實現,在這個例子中我們使用了圖像粒子(ImageParticle),使用一個圖片鏈接作為源屬性。圖像粒子也有其它的屬性用來控制粒子的外觀。 55 | 56 | * 發射頻率(emitRate)- 每秒粒子發射數(默認為10個)。 57 | 58 | * 生命週期(lifeSpan)- 粒子持續時間(單位毫秒,默認為1000毫秒)。 59 | 60 | * 初始大小(size),結束大小(endSize)- 粒子在它的生命週期的開始和結束時的大小(默認為16像素)。 61 | 62 | 改變這些屬性將會徹底改變顯示結果: 63 | 64 | ``` 65 | Emitter { 66 | id: emitter 67 | anchors.centerIn: parent 68 | width: 20; height: 20 69 | system: particleSystem 70 | emitRate: 40 71 | lifeSpan: 2000 72 | lifeSpanVariation: 500 73 | size: 64 74 | sizeVariation: 32 75 | Tracer { color: 'green' } 76 | } 77 | ``` 78 | 79 | 增加發射頻率為40,生命週期增加到2秒,開始大小為64像素,結束大小減少到32像素。 80 | 81 | ![](http://qmlbook.org/_images/simpleparticles2.png) 82 | 83 | 增加結束大小(endSize)可能會導致白色的背景出現。請注意粒子只有發射被限制在發射器定義的區域內,而粒子渲染是不會考慮這個參數的。 84 | -------------------------------------------------------------------------------- /canvas_element/canvas_paint.md: -------------------------------------------------------------------------------- 1 | # 畫布繪制(Canvas Paint) 2 | 3 | 在這個例子中我們將使用畫布(Canvas)創建一個簡單的繪制程序。 4 | 5 | ![](http://qmlbook.org/_images/canvaspaint.png) 6 | 7 | 在我們場景的頂部我們使用行定位器排列四個方形的顏色塊。一個顏色塊是一個簡單的矩形,使用鼠標區域來檢測點擊。 8 | 9 | ``` 10 | Row { 11 | id: colorTools 12 | anchors { 13 | horizontalCenter: parent.horizontalCenter 14 | top: parent.top 15 | topMargin: 8 16 | } 17 | property variant activeSquare: red 18 | property color paintColor: "#33B5E5" 19 | spacing: 4 20 | Repeater { 21 | model: ["#33B5E5", "#99CC00", "#FFBB33", "#FF4444"] 22 | ColorSquare { 23 | id: red 24 | color: modelData 25 | active: parent.paintColor == color 26 | onClicked: { 27 | parent.paintColor = color 28 | } 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | 顏色存儲在一個數組中,作為繪制顏色使用。當用戶點擊一個矩形時,矩形內的顏色被設置為colorTools的paintColor屬性。 35 | 36 | 為了在畫布上跟蹤鼠標事件,我們使用鼠標區域(MouseArea)覆蓋畫布元素,並連接點擊和移動操作。 37 | 38 | ``` 39 | Canvas { 40 | id: canvas 41 | anchors { 42 | left: parent.left 43 | right: parent.right 44 | top: colorTools.bottom 45 | bottom: parent.bottom 46 | margins: 8 47 | } 48 | property real lastX 49 | property real lastY 50 | property color color: colorTools.paintColor 51 | 52 | onPaint: { 53 | var ctx = getContext('2d') 54 | ctx.lineWidth = 1.5 55 | ctx.strokeStyle = canvas.color 56 | ctx.beginPath() 57 | ctx.moveTo(lastX, lastY) 58 | lastX = area.mouseX 59 | lastY = area.mouseY 60 | ctx.lineTo(lastX, lastY) 61 | ctx.stroke() 62 | } 63 | MouseArea { 64 | id: area 65 | anchors.fill: parent 66 | onPressed: { 67 | canvas.lastX = mouseX 68 | canvas.lastY = mouseY 69 | } 70 | onPositionChanged: { 71 | canvas.requestPaint() 72 | } 73 | } 74 | } 75 | ``` 76 | 77 | 鼠標點擊存儲在laxstX與lastY屬性中。每次鼠標位置的改變會觸發畫布的重繪,這將會調用onPaint操作。 78 | 79 | 最後繪制用戶的筆劃,在onPaint操作中,我們繪制從最近改變的點上開始繪制一條新的路徑,然後我們從鼠標區域採集新的點,使用選擇的顏色繪制線段到新的點上。鼠標位置被存儲為新改變的位置。 80 | -------------------------------------------------------------------------------- /meet_qt_5/qtqt_building_blocks.md: -------------------------------------------------------------------------------- 1 | # Qt構建模組(Qt Building Blocks) 2 | 3 | Qt5是由大量的模組組成的。一個模組通常情況下是一個程式庫,提供給開發者使用。一些模組是強制性用來支持Qt平台的,它們分成一組叫做Qt基礎模組。許多模組是可選的,它們分成一組叫做Qt附加模組,預計大多數得到開發人員將不會使用它們,但是最好知道它們可以對一些通用的問題提供非常有價值的解決方案。 4 | 5 | ## 1.3.1 Qt模組(Qt Modules) 6 | 7 | Qt基礎模組是對Qt平台的必要支持。它們使用Qt Quick 2開發Qt 5應用程序的基礎。 8 | 9 | **核心基礎模組** 10 | 11 | 以下這些是啟動QML程序最小的模組集合。 12 | 13 | | 模組名 | 描述 | 14 | | -- | -- | 15 | | Qt Core | 核心的非圖形類,供其它模組使用。 | 16 | | Qt GUI | 圖形用戶界面(GUI)組件的基類,包括OpenGL。 | 17 | | Qt Multimedia | 聲音,影片,電台,照相的功能類。 | 18 | | Qt Network | 簡化方便的網路編程的類。 | 19 | | Qt QML | QML類與JavaScript語言的支持。 | 20 | | Qt Quick | 可高度動態構建的自定義應用程序用戶界面框架。 | 21 | | Qt SQL | 集成SQL資料程式庫類。 | 22 | | Qt Test | Qt應用程序與程式庫的單元測試類。 | 23 | | Qt WebKit | 集成WebKit2的基礎實現並且提供了新的QML應用程序介面。在附件模組中查看Qt WebKit Widgets可以獲取更多的資訊。 | 24 | | Qt WebKit Widgets | Widgets 來自Qt4中集成WebKit1的窗口基礎類別。 | 25 | | Qt Widgets | 擴展Qt GUI模組的C++窗口類別。 | 26 | 27 | ![](http://qmlbook.org/_images/graphviz-748fb340dd79a8004144bb8c91ec98bf1ed82cf3.png) 28 | 29 | **Qt附加模組** 30 | 31 | 除了必不可少的基礎模組,Qt提供了附加模組供軟件開發者使用,這部分不一定包含在發布的版本中。以下簡短的列出了一些可用的附加模組列表。 32 | * Qt 3D - 一組使3D編程更加方便的應用程序介面和聲明。 33 | 34 | * Qt Bluetooth - 在多平台上使用無線藍牙技術的C++和QML應用程序介面。 35 | 36 | * Qt Contacts - 提供存取聯系人與聯系人資料程式庫的C++和QML應用程序介面。 37 | 38 | * Qt Location - 提供了定位,地圖,導航和位置搜索的C++與QML介面。使用NMEA在後端進行定位。(NMEA縮寫,同時也是數據傳輸標準工業協會,在這裡,實際上應為NMEA 0183。它是一套定義接收機輸出的標準資訊,有幾種不同的格式,每種都是獨立相關的ASCII格式,逗點隔開數據流,數據流長度從30-100字符不等,通常以每秒間隔選擇輸出,最常用的格式為"GGA",它包含了定位時間,緯度,經度,高度,定位所用的衛星數,DOP值,差分狀態和校正時段等,其他的有速度,跟蹤,日期等。NMEA實際上已成為所有的GPS接收機和最通用的數據輸出格式,同時它也被用於與GPS接收機介面的大多數的軟件包裡。) 39 | 40 | * Qt Organizer - 提供了組織事件(任務清單,事件等等)的C++和QML應用程序介面。 41 | 42 | * Qt Publish and SubScribe - Qt發布與訂閱 43 | 44 | * Qt Sensors - 存取傳感器的QML與C++介面。 45 | 46 | * Qt Service Framework - 允許應用程序讀取,操縱和訂閱來改變通知資訊。 47 | 48 | * Qt System Info - 發布系統相關的資訊和功能。 49 | 50 | * Qt Versit - 支持電子名片與日歷數據格式(iCalendar)。(iCalendar是“日歷數據交換”的標準(RFC 2445)。 此標準有時指的是“iCal”,即蘋果公司的出品的一款同名日歷軟件,這個軟件也是此標準的一種實現方式。) 51 | 52 | * Qt Wayland - 只用於Linux系統。包含了Qt合成器應用程序介面(server),和Wayland平台插件(clients)。 53 | 54 | * Qt Feedback - 回饋用戶的觸摸和聲音操作。 55 | 56 | * Qt JSON DB - 對於Qt的一個不使用SQL的資料庫。 57 | 58 | **注意** 59 | 60 | **這些模組一部分還沒有發布,這依賴於有多少貢獻者,並且它們能夠獲得更好的測試。** 61 | 62 | ## 1.3.2 支持的平台(Supported Platforms) 63 | 64 | Qt支持各種不同的平台。大多數主流的桌面與嵌入式平台都能夠支持。通過Qt應用程序抽象,現在可以更容易的將Qt移植到你自己的平台上。在一個平台上測試Qt5是非常花費時間的。選擇測試的平台子集可以參考qt-project元件的平台。這些平台需要完全通過系統的測試才能確保最好的品質。友情提醒:任何代碼都可能會有Bug的。 65 | -------------------------------------------------------------------------------- /shader_effect/qtqt_graphicseffect_library.md: -------------------------------------------------------------------------------- 1 | # Qt圖像效果庫(Qt GraphicsEffect Library) 2 | 3 | 圖像效果庫是一個著色器效果的集合,是由Qt開發者提供制作的。它是一個很好的工具,你可以將它應用在你的程序中,它也是一個學習如何創建著色器的例子。 4 | 5 | 圖像效果庫附帶了一個手動測試平台,這個工具可以幫助你測試發現不同的效果 6 | 測試工具在$QTDIR/qtgraphicaleffects/tests/manual/testbed下。 7 | 8 | ![](http://qmlbook.org/_images/graphicseffectstestbed.png) 9 | 10 | 效果庫包含了大約20種效果,下面是效果列表和一些簡短的描述。 11 | 12 | | 種類 | 效果 | 描述 | 13 | | -- | -- | -- | 14 | | 混合(Blend)| 混合(Blend) | 使用混合模式合並兩個資源項 | 15 | | 顏色(Color) | 亮度與對比度(BrightnessContrast) | 調整亮度與對比度 | 16 | | | 著色(Colorize)| 設置HSL顏色空間顏色 | 17 | | | 顏色疊加(ColorOverlay) | 應用一個顏色層 | 18 | | | 降低飽和度(Desaturate) | 減少顏色飽和度 | 19 | | | 伽馬調整(GammaAdjust) | 調整發光度 | 20 | | | 色調飽和度(HueSaturation) | 調整HSL顏色空間顏色 | 21 | | | 色階調整(LevelAdjust) | 調整RGB顏色空間顏色 | 22 | | 漸變(Gradient) | 圓錐漸變(ConicalGradient) | 繪制一個圓錐漸變 | 23 | | | 線性漸變(LinearGradient) | 繪制一個線性漸變 | 24 | | | 射線漸變(RadialGradient) | 繪制一個射線漸變 | 25 | | 失真(Distortion) | 置換(Displace) | 按照指定的置換源移動源項的像素 | 26 | | 陰影(Drop Shadow) | 陰影 (DropShadow) | 繪制一個陰影 | 27 | | | 內陰影(InnerShadow) | 繪制一個內陰影 | 28 | | 模糊 (Blur)| 快速模糊(FastBlur) | 應用一個快速模糊效果 | 29 | | | 高斯模糊(GaussianBlur) | 應用一個高質量模糊效果 | 30 | | | 蒙版模糊(MaskedBlur)| 應用一個多種強度的模糊效果 | 31 | | | 遞歸模糊(RecursiveBlur) | 重復模糊,提供一個更強的模糊效果 | 32 | | 運動模糊(Motion Blur) | 方向模糊(DirectionalBlur) | 應用一個方向的運動模糊效果 | 33 | | | 放射模糊(RadialBlur) | 應用一個放射運動模糊效果 | 34 | | | 變焦模糊(ZoomBlur) | 應用一個變焦運動模糊效果 | 35 | | 發光(Glow)| 發光(Glow) | 繪制一個外發光效果 | 36 | | | 矩形發光(RectangularGlow) | 繪制一個矩形外發光效果 | 37 | | 蒙版(Mask)| 透明蒙版(OpacityMask) | 使用一個源項遮擋另一個源項 | 38 | | | 閾值蒙版(ThresholdMask) | 使用一個閾值,一個源項遮擋另一個源項 | 39 | 40 | 下面是一個使用快速模糊效果的例子: 41 | 42 | ``` 43 | import QtQuick 2.0 44 | import QtGraphicalEffects 1.0 45 | 46 | Rectangle { 47 | width: 480; height: 240 48 | color: '#1e1e1e' 49 | 50 | Row { 51 | anchors.centerIn: parent 52 | spacing: 16 53 | 54 | Image { 55 | id: sourceImage 56 | source: "assets/tulips.jpg" 57 | width: 200; height: width 58 | sourceSize: Qt.size(parent.width, parent.height) 59 | smooth: true 60 | } 61 | 62 | FastBlur { 63 | width: 200; height: width 64 | source: sourceImage 65 | radius: blurred?32:0 66 | property bool blurred: false 67 | 68 | Behavior on radius { 69 | NumberAnimation { duration: 1000 } 70 | } 71 | 72 | MouseArea { 73 | id: area 74 | anchors.fill: parent 75 | onClicked: parent.blurred = !parent.blurred 76 | } 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | 左邊是原圖片。點擊右邊的圖片將會觸發blurred屬性,模糊在1秒內從0到32。左邊顯示模糊後的圖片。 83 | 84 | ![](http://qmlbook.org/_images/fastblur.png) 85 | -------------------------------------------------------------------------------- /quick_starter/layout_items.md: -------------------------------------------------------------------------------- 1 | # 布局元素(Layout Items) 2 | 3 | QML使用anchors(錨)對元素進行布局。anchoring(錨定)是基礎元素對象的基本屬性,可以被所有的可視化QML元素使用。一個anchors(錨)就像一個協議,並且比幾何變化更加強大。Anchors(錨)是相對關系的表達式,你通常需要與其它元素搭配使用。 4 | 5 | ![](http://qmlbook.org/_images/anchors.png) 6 | 7 | 一個元素有6條錨定線(top頂,bottom底,left左,right右,horizontalCenter水平中,verticalCenter垂直中)。在文本元素(Text Element)中有一條文本的錨定基線(baseline)。每一條錨定線都有一個偏移(offset)值,在top(頂),bottom(底),left(左),right(右)的錨定線中它們也被稱作邊距。對于horizontalCenter(水平中)與verticalCenter(垂直中)與baseline(文本基線)中被稱作偏移值。 8 | 9 | ![](http://qmlbook.org/_images/anchorgrid.png) 10 | 11 | 1. 元素填充它的父元素。 12 | ``` 13 | GreenSquare { 14 | BlueSquare { 15 | width: 12 16 | anchors.fill: parent 17 | anchors.margins: 8 18 | text: '(1)' 19 | } 20 | } 21 | ``` 22 | 23 | 2. 元素左對齊它的父元素。 24 | ``` 25 | GreenSquare { 26 | BlueSquare { 27 | width: 48 28 | y: 8 29 | anchors.left: parent.left 30 | anchors.leftMargin: 8 31 | text: '(2)' 32 | } 33 | } 34 | ``` 35 | 36 | 3. 元素的左邊與它父元素的右邊對齊。 37 | ``` 38 | GreenSquare { 39 | BlueSquare { 40 | width: 48 41 | anchors.left: parent.right 42 | text: '(3)' 43 | } 44 | } 45 | ``` 46 | 47 | 4. 元素中間對齊。Blue1與它的父元素水平中間對齊。Blue2與Blue1中間對齊,並且它的頂部對齊Blue1的底部。 48 | ``` 49 | GreenSquare { 50 | BlueSquare { 51 | id: blue1 52 | width: 48; height: 24 53 | y: 8 54 | anchors.horizontalCenter: parent.horizontalCenter 55 | } 56 | BlueSquare { 57 | id: blue2 58 | width: 72; height: 24 59 | anchors.top: blue1.bottom 60 | anchors.topMargin: 4 61 | anchors.horizontalCenter: blue1.horizontalCenter 62 | text: '(4)' 63 | } 64 | } 65 | ``` 66 | 67 | 5. 元素在它的父元素中居中。 68 | ``` 69 | GreenSquare { 70 | BlueSquare { 71 | width: 48 72 | anchors.centerIn: parent 73 | text: '(5)' 74 | } 75 | } 76 | ``` 77 | 78 | 6. 元素水平方向居中對齊父元素並向後偏移12像素,垂直方向居中對齊。 79 | ``` 80 | GreenSquare { 81 | BlueSquare { 82 | width: 48 83 | anchors.horizontalCenter: parent.horizontalCenter 84 | anchors.horizontalCenterOffset: -12 85 | anchors.verticalCenter: parent.verticalCenter 86 | text: '(6)' 87 | } 88 | } 89 | ``` 90 | 91 | **注意** 92 | 93 | **我們的方格都打開了拖拽。試著拖放幾個方格。你可以發現第一個方格無法被拖拽因為它每個邊都被固定了,當然第一個方格的父元素能夠被拖拽是因為它的父元素沒有被固定。第二個方格能夠在垂直方向上拖拽是因為它只有左邊被固定了。類似的第三個和第四個方格也只能在垂直方向上拖拽是因為它們都使用水平居中對齊。第五個方格使用居中布局,它也無法被移動,第六個方格與第五個方格類似。拖拽一個元素意味著會改變它的x,y坐標。anchoring(錨定)比幾何變化(例如x,y坐標變化)更強大是因為錨定線(anchored lines)的限制,我們將在後面討論動畫時看到這些功能的強大。** 94 | -------------------------------------------------------------------------------- /canvas_element/README.md: -------------------------------------------------------------------------------- 1 | # Canvas Element 2 | 3 | **注意** 4 | 5 | **最後一次構建:2014年1月20日下午18:00。** 6 | 7 | **這章的源代碼能夠在[assetts folder](http://qmlbook.org/assets)找到。** 8 | 9 | ![](http://qmlbook.org/_images/glowlines.png) 10 | 11 | 在早些時候的Qt4中加入QML時,一些開發者討論如何在QtQuick中繪制一個圓形。類似圓形的問題,一些開發者也對于其它的形狀的支持進行了討論。在QtQuick中沒有圓形,只有矩形。在Qt4中,如果你需要一個除了矩形外的形狀,你需要使用圖片或者使用你自己寫的C++圓形元素。 12 | 13 | Qt5中引進了畫布元素(canvas element),允許腳本繪制。畫布元素(canvas element)提供了一個依賴于分辨率的位圖畫布,你可以使用JavaScript腳本來繪制圖形,制作遊戲或者其它的動態圖像。畫布元素(canvas element)是基于HTML5的畫布元素來完成的。 14 | 15 | 畫布元素(canvas element)的基本思想是使用一個2D對象來渲染路徑。這個2D對象包括了必要的繪圖函數,畫布元素(canvas element)充當繪制畫布。2D對象支持畫筆,填充,漸變,文本和繪制路徑創建命令。 16 | 17 | 讓我們看看一個簡單的路徑繪制的例子: 18 | 19 | ``` 20 | import QtQuick 2.0 21 | 22 | Canvas { 23 | id: root 24 | // canvas size 25 | width: 200; height: 200 26 | // handler to override for drawing 27 | onPaint: { 28 | // get context to draw with 29 | var ctx = getContext("2d") 30 | // setup the stroke 31 | ctx.lineWidth = 4 32 | ctx.strokeStyle = "blue" 33 | // setup the fill 34 | ctx.fillStyle = "steelblue" 35 | // begin a new path to draw 36 | ctx.beginPath() 37 | // top-left start point 38 | ctx.moveTo(50,50) 39 | // upper line 40 | ctx.lineTo(150,50) 41 | // right line 42 | ctx.lineTo(150,150) 43 | // bottom line 44 | ctx.lineTo(50,150) 45 | // left line through path closing 46 | ctx.closePath() 47 | // fill using fill style 48 | ctx.fill() 49 | // stroke using line width and stroke style 50 | ctx.stroke() 51 | } 52 | } 53 | ``` 54 | 55 | 這個例子產生了一個在坐標(50,50),高寬為100的填充矩形框,並且使用了畫筆來修飾邊界。 56 | 57 | ![](http://qmlbook.org/_images/rectangle.png) 58 | 59 | 畫筆的寬度被設置為4個像素,並且定義strokeStyle(畫筆樣式)為藍色。最後的形狀由設置填充樣式(fillStyle)為steelblue顏色,然後填充完成的。只有調用stroke或者fill函數,創建的路徑才會繪制,它們與其它的函數使用是相互獨立的。調用stroke或者fill將會繪制當前的路徑,創建的路徑是不可重用的,只有繪制狀態能夠被存儲和恢復。 60 | 61 | 在QML中,畫布元素(canvas element)充當了繪制的容器。2D繪制對象提供了實際繪制的方法。繪制需要在onPaint事件中完成。 62 | 63 | ``` 64 | Canvas { 65 | width: 200; height: 200 66 | onPaint: { 67 | var ctx = getContext("2d") 68 | // setup your path 69 | // fill or/and stroke 70 | } 71 | } 72 | ``` 73 | 74 | 畫布自身提供了典型的二維笛卡爾坐標系統,左上角是(0,0)坐標。Y軸坐標軸向下,X軸坐標軸向右。 75 | 76 | 典型繪制命令調用如下: 77 | 78 | 1. 裝載畫筆或者填充模式 79 | 80 | 2. 創建繪制路徑 81 | 82 | 3. 使用畫筆或者填充繪制路徑 83 | 84 | ``` 85 | onPaint: { 86 | var ctx = getContext("2d") 87 | 88 | // setup the stroke 89 | ctx.strokeStyle = "red" 90 | 91 | // create a path 92 | ctx.beginPath() 93 | ctx.moveTo(50,50) 94 | ctx.lineTo(150,50) 95 | 96 | // stroke path 97 | ctx.stroke() 98 | } 99 | ``` 100 | 101 | 102 | 這將產生一個從P1(50,50)到P2(150,50)水平線。 103 | 104 | ![](http://qmlbook.org/_images/line.png) 105 | 106 | **注意** 107 | 108 | **通常在你重置了路徑後你將會設置一個開始點,所以,在beginPath()這個操作後,你需要使用moveTo來設置開始點。** 109 | -------------------------------------------------------------------------------- /quick_starter/simple_transformations.md: -------------------------------------------------------------------------------- 1 | # 簡單的轉換(Simple Transformations) 2 | 3 | 轉換操作改變了一個對象的幾何狀態。QML元素對象通常能夠被平移,旋轉,縮放。下面我們將講解這些簡單的操作和一些更高級的用法。 4 | 我們先從一個簡單的轉換開始。用下面的場景作為我們學習的開始。 5 | 6 | 簡單的位移是通過改變x,y坐標來完成的。旋轉是改變rotation(旋轉)屬性來完成的,這個值使用角度作為單位(0~360)。縮放是通過改變scale(比例)的屬性來完成的,小于1意味著縮小,大于1意味著放大。旋轉與縮放不會改變對象的幾何形狀,對象的x,y(坐標)與width/height(寬/高)也類似。只有繪制指令是被轉換的對象。 7 | 8 | 在我們展示例子之前我想要介紹一些東西:ClickableImage元素(ClickableImage element),ClickableImage僅僅是一個包含鼠標區域的圖像元素。我們遵循一個簡單的原則,三次使用相同的代碼描述一個用戶界面最好可以抽象為一個組件。 9 | 10 | ``` 11 | // ClickableImage.qml 12 | 13 | // Simple image which can be clicked 14 | 15 | import QtQuick 2.0 16 | 17 | Image { 18 | id: root 19 | signal clicked 20 | 21 | MouseArea { 22 | anchors.fill: parent 23 | onClicked: root.clicked() 24 | } 25 | } 26 | ``` 27 | 28 | ![](http://qmlbook.org/_images/rockets.png) 29 | 30 | 我們使用我們可點擊圖片元素來顯示了三個火箭。當點擊時,每個火箭執行一種簡單的轉換。點擊背景將會重置場景。 31 | 32 | ``` 33 | // transformation.qml 34 | 35 | 36 | import QtQuick 2.0 37 | 38 | Item { 39 | // set width based on given background 40 | width: bg.width 41 | height: bg.height 42 | 43 | Image { // nice background image 44 | id: bg 45 | source: "assets/background.png" 46 | } 47 | 48 | MouseArea { 49 | id: backgroundClicker 50 | // needs to be before the images as order matters 51 | // otherwise this mousearea would be before the other elements 52 | // and consume the mouse events 53 | anchors.fill: parent 54 | onClicked: { 55 | // reset our little scene 56 | rocket1.x = 20 57 | rocket2.rotation = 0 58 | rocket3.rotation = 0 59 | rocket3.scale = 1.0 60 | } 61 | } 62 | 63 | ClickableImage { 64 | id: rocket1 65 | x: 20; y: 100 66 | source: "assets/rocket.png" 67 | onClicked: { 68 | // increase the x-position on click 69 | x += 5 70 | } 71 | } 72 | 73 | ClickableImage { 74 | id: rocket2 75 | x: 140; y: 100 76 | source: "assets/rocket.png" 77 | smooth: true // need antialising 78 | onClicked: { 79 | // increase the rotation on click 80 | rotation += 5 81 | } 82 | } 83 | 84 | ClickableImage { 85 | id: rocket3 86 | x: 240; y: 100 87 | source: "assets/rocket.png" 88 | smooth: true // need antialising 89 | onClicked: { 90 | // several transformations 91 | rotation += 5 92 | scale -= 0.05 93 | } 94 | } 95 | } 96 | ``` 97 | 98 | ![](http://qmlbook.org/_images/rockets_transformed.png) 99 | 100 | 火箭1在每次點擊後X軸坐標增加5像素,火箭2每次點擊後會旋轉。火箭3每次點擊後會縮小。對于縮放和旋轉操作我們都設置了smooth:true來增加反鋸齒,由于性能的原因通常是被關閉的(與剪裁屬性clip類似)。當你看到你的圖形中出現鋸齒時,你可能就需要打開平滑(smooth)。 101 | 102 | **注意** 103 | 104 | **為了獲得更好的顯示效果,當縮放圖片時推薦使用已縮放的圖片來替代,過量的放大可能會導致圖片模糊不清。當你在縮放圖片時你最好考慮使用smooth:true來提高圖片顯示質量。** 105 | 106 | 使用MouseArea來覆蓋整個背景,點擊背景可以初始化火箭的值。 107 | 108 | **注意** 109 | 110 | **在代碼中先出現的元素有更低的堆疊順序(叫做z順序值z-order),如果你點擊火箭1足夠多次,你會看見火箭1移動到了火箭2下面。z軸順序也可以使用元素對象的z-property來控制。** 111 | 112 | ![](http://qmlbook.org/_images/order_matters.png) 113 | 114 | **由于火箭2後出現在代碼中,火箭2將會放在火箭1上面。這同樣適用于MouseArea(鼠標區域),一個後出現在代碼中的鼠標區域將會與之前的鼠標區域重疊,後出現的鼠標區域才能捕捉到鼠標事件。** 115 | 116 | **請記住:文檔中元素的順序很重要。** 117 | -------------------------------------------------------------------------------- /quick_starter/compontents.md: -------------------------------------------------------------------------------- 1 | # 組件(Compontents) 2 | 3 | 4 | 一個組件是一個可以重復使用的元素,QML提供幾種不同的方法來創建組件。但是目前我們只對其中一種方法進行講解:一個文件就是一個基礎組件。一個以文件為基礎的組件在文件中創建了一個QML元素,並且將文件以元素類型來命名(例如Button.qml)。你可以像任何其它的QtQuick模塊中使用元素一樣來使用這個組件。在我們下面的例子中,你將會使用你的代碼作為一個Button(按鈕)來使用。 5 | 6 | 讓我們來看看這個例子,我們創建了一個包含文本和鼠標區域的矩形框。它類似于一個簡單的按鈕,我們的目標就是讓它足夠簡單。 7 | 8 | ``` 9 | Rectangle { // our inlined button ui 10 | id: button 11 | x: 12; y: 12 12 | width: 116; height: 26 13 | color: "lightsteelblue" 14 | border.color: "slategrey" 15 | Text { 16 | anchors.centerIn: parent 17 | text: "Start" 18 | } 19 | MouseArea { 20 | anchors.fill: parent 21 | onClicked: { 22 | status.text = "Button clicked!" 23 | } 24 | } 25 | } 26 | 27 | Text { // text changes when button was clicked 28 | id: status 29 | x: 12; y: 76 30 | width: 116; height: 26 31 | text: "waiting ..." 32 | horizontalAlignment: Text.AlignHCenter 33 | } 34 | ``` 35 | 36 | 用戶界面將會看起來像下面這樣。左邊是初始化的狀態,右邊是按鈕點擊後的效果。 37 | 38 | ![](http://qmlbook.org/_images/button_waiting.png) 39 | 40 | ![](http://qmlbook.org/_images/button_clicked.png) 41 | 42 | 我們的目標是提取這個按鈕作為一個可重復使用的組件。我們可以簡單的考慮一下我們的按鈕會有的哪些API(應用程序接口),你可以自己考慮一下你的按鈕應該有些什麼。下面是我考慮的結果: 43 | 44 | ``` 45 | // my ideal minimal API for a button 46 | Button { 47 | text: "Click Me" 48 | onClicked: { // do something } 49 | } 50 | ``` 51 | 52 | 我想要使用text屬性來設置文本,然後實現我們自己的點擊操作。我也期望這個按鈕有一個比較合適的初始化大小(例如width:240)。 53 | 為了完成我們的目標,我創建了一個Button.qml文件,並且將我們的代碼拷貝了進去。我們在根級添加一個屬性導出方便使用者修改它。 54 | 55 | 我們在根級導出了文本和點擊信號。通常我們命名根元素為root讓引用更加方便。我們使用了QML的alias(別名)的功能,它可以將內部嵌套的QML元素的屬性導出到外面使用。有一點很重要,只有根級目錄的屬性才能夠被其它文件的組件訪問。 56 | 57 | ``` 58 | // Button.qml 59 | 60 | import QtQuick 2.0 61 | 62 | Rectangle { 63 | id: root 64 | // export button properties 65 | property alias text: label.text 66 | signal clicked 67 | 68 | width: 116; height: 26 69 | color: "lightsteelblue" 70 | border.color: "slategrey" 71 | 72 | Text { 73 | id: label 74 | anchors.centerIn: parent 75 | text: "Start" 76 | } 77 | MouseArea { 78 | anchors.fill: parent 79 | onClicked: { 80 | root.clicked() 81 | } 82 | } 83 | } 84 | ``` 85 | 86 | 使用我們新的Button元素只需要在我們的文件中簡單的聲明一下就可以了,之前的例子將會被簡化。 87 | 88 | ``` 89 | Button { // our Button component 90 | id: button 91 | x: 12; y: 12 92 | text: "Start" 93 | onClicked: { 94 | status.text = "Button clicked!" 95 | } 96 | } 97 | 98 | Text { // text changes when button was clicked 99 | id: status 100 | x: 12; y: 76 101 | width: 116; height: 26 102 | text: "waiting ..." 103 | horizontalAlignment: Text.AlignHCenter 104 | } 105 | ``` 106 | 107 | 現在你可以在你的用戶界面代碼中隨意的使用Button{ ...}來作為按鈕了。一個真正的按鈕將更加復雜,比如提供按鍵反饋或者添加一些裝飾。 108 | 109 | **注意** 110 | 111 | **就個人而言,可以更進一步的使用基礎元素對象(Item)作為根元素。這樣可以防止用戶改變我們設計的按鈕的顏色,並且可以提供出更多相關控制的API(應用程序接口)。我們的目標是導出一個最小的API(應用程序接口)。實際上我們可以將根矩形框(Rectangle)替換為一個基礎元素(Item),然後將一個矩形框(Rectangle)嵌套在這個根元素(root item)就可以完成了。** 112 | 113 | ``` 114 | Item { 115 | id: root 116 | Rectangle { 117 | anchors.fill parent 118 | color: "lightsteelblue" 119 | border.color: "slategrey" 120 | } 121 | ... 122 | } 123 | ``` 124 | 125 | 使用這項技術可以很簡單的創建一系列可重用的組件。 126 | -------------------------------------------------------------------------------- /multimedia/capturing_images.md: -------------------------------------------------------------------------------- 1 | # 捕捉圖像(Capturing Images) 2 | 3 | Camera元素一個關鍵特性就是可以用來拍照。我們將在一個簡單的定格動畫程序中使用到它。在這章中,你將學習如何顯示一個視圖查找器,截圖和追蹤拍攝的圖片。 4 | 5 | 用戶界面如下所示。它由三部分組成,背景是一個視圖查找器,右邊有一列按鈕,底部有一連串拍攝的圖片。我們想要拍攝一系列的圖片,然後點擊Play Sequence按鈕。這將回放圖片,並創建一個簡單的定格電影。 6 | 7 | ![](http://qmlbook.org/_images/camera-ui.png) 8 | 9 | 相機的視圖查找器部分是在VideoOutput中使用一個簡單的Camera元素作為資源。這將給用戶顯示一個來自相機的流媒體視頻。 10 | 11 | ``` 12 | VideoOutput { 13 | anchors.fill: parent 14 | source: camera 15 | } 16 | 17 | Camera { 18 | id: camera 19 | } 20 | ``` 21 | 22 | 使用一個水平放置的ListView顯示來自ListModel的圖片,這個部件叫做imagePaths。在背景中使用一個半透明的Rectangle。 23 | 24 | ``` 25 | ListModel { 26 | id: imagePaths 27 | } 28 | 29 | ListView { 30 | id: listView 31 | 32 | anchors.left: parent.left 33 | anchors.right: parent.right 34 | anchors.bottom: parent.bottom 35 | anchors.bottomMargin: 10 36 | 37 | height: 100 38 | 39 | orientation: ListView.Horizontal 40 | spacing: 10 41 | 42 | model: imagePaths 43 | 44 | delegate: Image { source: path; fillMode: Image.PreserveAspectFit; height: 100; } 45 | 46 | Rectangle { 47 | anchors.fill: parent 48 | anchors.topMargin: -10 49 | 50 | color: "black" 51 | opacity: 0.5 52 | } 53 | } 54 | ``` 55 | 56 | 為了拍攝圖像,你需要知道Camera元素包含了一組子對象用來完成各種工作。使用Camera.imageCapture用來捕捉圖像。當你調用capture方法時,一張圖片就被拍攝下來了。Camera.imageCapture的結果將會發送imageCaptured信號,接著發送imageSaved信號。 57 | 58 | ``` 59 | Button { 60 | id: shotButton 61 | 62 | width: 200 63 | height: 75 64 | 65 | text: "Take Photo" 66 | onClicked: { 67 | camera.imageCapture.capture(); 68 | } 69 | } 70 | ``` 71 | 72 | 為了攔截子元素的信號,需要一個Connections元素。在這個例子中,我們不需要顯示預覽圖片,僅僅只是將結果圖片加入底部的ListView中。就如下面的例子展示的一樣,圖片保存的路徑由信號的path參數提供。 73 | 74 | ``` 75 | Connections { 76 | target: camera.imageCapture 77 | 78 | onImageSaved: { 79 | imagePaths.append({"path": path}) 80 | listView.positionViewAtEnd(); 81 | } 82 | } 83 | ``` 84 | 85 | 為了顯示預覽,連接imageCaptured信號,並且使用preview信號參數作為Image元素的source。requestId信號參數與imageCaptured和imageSaved一起發送。這個值由capture方法返回。這樣,就可以完整的跟蹤拍攝的圖片了。預覽的圖片首先被使用,然後替換為保存的圖片。然而在這個例子中我們不需要這樣做。 86 | 87 | 最後是自動回放的部分。使用Timer元素來驅動它,並且加上一些JavaScript。_imageIndex變量被用來跟蹤當前顯示的圖片。當最後一張圖片被顯示時,回放停止。在例子中,當播放序列時,root.state被用來隱藏用戶界面。 88 | 89 | ``` 90 | property int _imageIndex: -1 91 | 92 | function startPlayback() 93 | { 94 | root.state = "playing"; 95 | setImageIndex(0); 96 | playTimer.start(); 97 | } 98 | 99 | function setImageIndex(i) 100 | { 101 | _imageIndex = i; 102 | 103 | if (_imageIndex >= 0 && _imageIndex < imagePaths.count) 104 | image.source = imagePaths.get(_imageIndex).path; 105 | else 106 | image.source = ""; 107 | } 108 | 109 | Timer { 110 | id: playTimer 111 | 112 | interval: 200 113 | repeat: false 114 | 115 | onTriggered: { 116 | if (_imageIndex + 1 < imagePaths.count) 117 | { 118 | setImageIndex(_imageIndex + 1); 119 | playTimer.start(); 120 | } 121 | else 122 | { 123 | setImageIndex(-1); 124 | root.state = ""; 125 | } 126 | } 127 | } 128 | ``` 129 | -------------------------------------------------------------------------------- /multimedia/playing_media.md: -------------------------------------------------------------------------------- 1 | # 媒體播放(Playing Media) 2 | 3 | 在QML應用程序中,最基本的媒體應用是播放媒體。使用MediaPlayer元素可以完成它,如果源是一個圖片或者視頻,可以選擇結合VideoOutput元素。MediaPlayer元素有一個source屬性指向需要播放的媒體。當媒體源被綁定後,簡單的調用play函數就可以開始播放。 4 | 5 | 如果你想播放一個可視化的媒體,例如圖片或者視頻等,你需要配置一個VideoOutput元素。MediaPlayer播放通過source屬性與視頻輸出綁定。 6 | 7 | 在下面的例子中,給MediaPlayer元素一個視頻文件作為source。一個VideoOutput被創建和綁定到媒體播放器上。一旦主要部件完全初始化,例如在Component.onCompleted中,播放器的play函數被調用。 8 | 9 | ``` 10 | import QtQuick 2.0 11 | import QtMultimedia 5.0 12 | import QtSystemInfo 5.0 13 | 14 | Item { 15 | width: 1024 16 | height: 600 17 | 18 | MediaPlayer { 19 | id: player 20 | source: "trailer_400p.ogg" 21 | } 22 | 23 | VideoOutput { 24 | anchors.fill: parent 25 | source: player 26 | } 27 | 28 | Component.onCompleted: { 29 | player.play(); 30 | } 31 | 32 | ScreenSaver { 33 | screenSaverEnabled: false; 34 | } 35 | } 36 | // M1>> 37 | ``` 38 | 39 | 除了上面介紹的視頻播放,這個例子也包括了一小段代碼用于禁止屏幕保護。這將阻止視頻被中斷。通過設置ScreenSaver元素的screenSaverEnabled屬性為false來完成。通過導入QtSystemInfo 5.0可以使用ScreenSaver元素。 40 | 41 | 基礎操作例如當播放媒體時可以通過MediaPlayer元素的volume屬性來控制音量。還有一些其它有用的屬性。例如,duration與position屬性可以用來創建一個進度條。如果seekable屬性為true,當撥動進度條時可以更新position屬性。下面這個例子展示了在上面的例子基礎上如何添加基礎播放。 42 | 43 | ``` 44 | Rectangle { 45 | id: progressBar 46 | 47 | anchors.left: parent.left 48 | anchors.right: parent.right 49 | anchors.bottom: parent.bottom 50 | anchors.margins: 100 51 | 52 | height: 30 53 | 54 | color: "lightGray" 55 | 56 | Rectangle { 57 | anchors.left: parent.left 58 | anchors.top: parent.top 59 | anchors.bottom: parent.bottom 60 | 61 | width: player.duration>0?parent.width*player.position/player.duration:0 62 | 63 | color: "darkGray" 64 | } 65 | 66 | MouseArea { 67 | anchors.fill: parent 68 | 69 | onClicked: { 70 | if (player.seekable) 71 | player.position = player.duration * mouse.x/width; 72 | } 73 | } 74 | } 75 | ``` 76 | 77 | 默認情況下position屬性每秒更新一次。這意味著進度條將只會在大跨度下的時間週期下才會更新,需要媒體持續時間足夠長,進度條像素足夠寬。然而,這個可以通過mediaObject屬性的notifyInterval屬性改變。它可以設置每個position之間更新的毫秒數,增加用戶界面的平滑度。 78 | 79 | ``` 80 | Connections { 81 | target: player 82 | onMediaObjectChanged: { 83 | if (player.mediaObject) 84 | player.mediaObject.notifyInterval = 50; 85 | } 86 | } 87 | ``` 88 | 89 | 當使用MediaPlayer創建一個媒體播放器時,最好使用status屬性來監聽播放器。這個屬性是一個枚舉,它枚舉了播放器可能出現的狀態,從MediaPlayer.Buffered到MediaPlayer.InvalidMedia。下面是這些狀態值的總結: 90 | 91 | * MediaPlayer.UnknownStatus - 未知狀態 92 | 93 | * MediaPlayer.NoMedia - 播放器沒有指定媒體資源,播放停止 94 | 95 | * MediaPlayer.Loading - 播放器正在加載媒體 96 | 97 | * MediaPlayer.Loaded - 媒體已經加載完畢,播放停止 98 | 99 | * MediaPlayer.Stalled - 加載媒體已經停止 100 | 101 | * MediaPlayer.Buffering - 媒體正在緩衝 102 | 103 | * MediaPlayer.Buffered - 媒體緩衝完成 104 | 105 | * MediaPlayer.EndOfMedia - 媒體播放完畢,播放停止 106 | 107 | * MediaPlayer.InvalidMedia - 無法播放媒體,播放停止 108 | 109 | 正如上面提到的這些枚舉項,播放狀態會隨著時間變化。調用play,pause或者stop將會切換狀態,但由于媒體的原因也會影響這些狀態。例如,媒體播放完畢,它將會無效,導致播放停止。當前的播放狀態可以使用playbackState屬性跟蹤。這個值可能是MediaPlayer.PlayingState,MediaPlayer.PasuedState或者MediaPlayer.StoppedState。 110 | 111 | 使用autoPlay屬性,MediaPlayer在source屬性改變時將會嘗試進入播放狀態。類似的屬性autoLoad將會導致播放器在source屬性改變時嘗試加載媒體。默認下autoLoad是被允許的。 112 | 113 | 當然也可以讓MediaPlayer循環播放一個媒體項。loops屬性控制source將會被重復播放多少次。設置屬性為MediaPlayer.Infinite將會導致不停的重播。非常適合持續的動畫或者一個重復的背景音樂。 114 | -------------------------------------------------------------------------------- /meet_qt_5/qt5qt5_introduction.md: -------------------------------------------------------------------------------- 1 | # Qt5介紹(Qt5 Introduction) 2 | 3 | ## 1.2.1 Qt Quick 4 | 5 | Qt Quick是Qt5的使用界面技術。Qt Quick自身包含了以下幾種技術: 6 | 7 | * QML-使用於用戶界面的標示語言 8 | 9 | * JavaScript-動態腳本語言 10 | 11 | * Qt C++-具有高度可移植性的C++程式庫. 12 | 13 | ![](http://qmlbook.org/_images/qt5_overview.png) 14 | 15 | 類似HTML語言,QML是一個標識語言。它由QtQuick封裝在Item {}的元素的標識組成。它從頭設計了用戶界面的設計,並且可以讓開發人員快速,簡單的理解。用戶界面可以使用JavaScript代碼來提供和加強更多的功能。Qt Quick可以使用你自己本地已有的Qt C++輕鬆快速的擴展它的能力。簡單宣告式的UI作為前端,本地部分被稱作後端。這樣你可以將程序的計算密集部分與來自應用程序用戶界面操作部分分開。 16 | 17 | 在典型的項目中前端開發使用QML/JaveScript,後端代碼開發使用Qt C++來完成系統接口和繁重的計算工作。這樣就很自然的將設計界面的開發者和功能開發者分開了。後端開發測試使用Qt自有的單元測試框架後,輸出給前端開發者使用。 18 | 19 | ## 1.2.2 一個用戶界面(Digesting an User Interface) 20 | 21 | 讓我們來使用QtQuick來創建一個簡單的用戶界面,範例QML語言某些方面的特性。最後我們將獲得一個旋轉的風車。 22 | 23 | 我們開始創建一個空的main.qml檔案。所有的QML文件都已.qml作為後綴。作為一個標識語言(類似HTML)一個QML文檔需要並且只有一個根元素,在我們的案例中是一個基於background的圖像高度與寬度的幾何圖形元素: 24 | 25 | ``` 26 | import QtQuick 2.0 27 | 28 | Image { 29 | id: root 30 | source: "images/background.png" 31 | } 32 | ``` 33 | 34 | QML不會對最上層元素設置任何限制,我們使用一個backgournd圖像作為資源的圖像元素來作為我們的根元素。 35 | 36 | ![](http://qmlbook.org/_images/background.png) 37 | 38 | **注意** 39 | 40 | **每一個元素都有屬性,比如一個圖像有寬度,高度但是也有一些其它的屬性例如資源。圖像元素的大小能夠自動的從圖像大小上得出。否則我們應該設置寬度和高度屬性來顯示有效的像素。** 41 | 42 | **大多數典型的元素都放置在QtQuick2.0模塊中,我們首先應該在第一行作這個重要的聲明。** 43 | 44 | **id是這個特殊的屬性是可選的,包含了一個標識符,在檔案後面的地方可以直接引用。** 45 | 46 | **重要提示:一個id屬性無法在它被設置後改變,並且在程序執行期間無法被設置。使用root作為根元素id僅僅是作者的習慣,可以在比較大的QML檔案中方便的引用最頂層元素。** 47 | 48 | 風車作為前景元素使用圖像的方式放置在我們的用戶界面上。 49 | 50 | ![](http://qmlbook.org/_images/pinwheel.png) 51 | 52 | 正常情況下你的用戶界面應該有不同類型的元素構成,而不是像我們的例子一樣只有圖像元素。 53 | 54 | ``` 55 | Image { 56 | id: root 57 | ... 58 | Image { 59 | id: wheel 60 | anchors.centerIn: parent 61 | source: "images/pinwheel.png" 62 | } 63 | ... 64 | } 65 | ``` 66 | 67 | 為了把風車放在中間的位置,我們使用了一個復雜的屬性,稱之為錨。錨定允許你指定幾何對象與父對象或者同級對象之間的位置關系。比如放置我在另一個元素中間(anchors.centerIn:parent).有左邊(left),右邊(right),頂部(top),底部(bottom),中央(centerIn),填充(fill),垂直中央(verticalCenter)和水平中央(horizontalCenter)來表示元素之間的關系。確保他們能夠匹配,錨定一個對象的左側頂部的一個元素這樣的做法是沒有意義的。所以我們設置風車在父對象background的中央。 68 | 69 | **注意** 70 | 71 | **有時你需要進行一些微小的調整。使用anchors.horizontalCenterOffset或者anchors.verticalCenterOffset可以幫你實現這個功能。類似的調整屬性也可以用於其他所有的錨。查閱Qt的幫助檔案可以知道完整的錨屬性列表。** 72 | 73 | **注意** 74 | 75 | **將一個圖像作為根矩形元素的子元素放置範例了一種聲明式語言的重要概念。你描述了用戶界面的層和分組的順序,最頂部的一層(根矩形框)先繪制,然後子層按照包含它的元素局部坐標繪制在包含它的元素上。** 76 | 77 | 為了讓我們的範例更加有趣一點,我們應該讓程序有一些交互功能。當用戶點擊場景上某個位置時,讓我們的風車轉動起來。 78 | 79 | 我們使用mouseArea元素,並且讓它與我們的根元素大小一樣。 80 | 81 | ``` 82 | Image { 83 | id: root 84 | ... 85 | MouseArea { 86 | anchors.fill: parent 87 | onClicked: wheel.rotation += 90 88 | } 89 | ... 90 | } 91 | ``` 92 | 93 | 當用戶點擊覆蓋區域時,鼠標區域會發出一個信號。你可以重寫onClicked函數來鏈接這個信號。在這個案例中引用了風車的圖像並且讓他旋轉增加90度。 94 | 95 | **注意** 96 | 97 | **對於每個工作的信號,命名方式都是on + SignalName的標題。當屬性的值發生改變時也會發出一個信號。它們的命名方式是:on + PropertyName + Chagned。 98 | 如果一個寬度(width)屬性改變了,你可以使用onWidthChanged: print(width)來得到這個監控這個新的寬度值。** 99 | 100 | 現在風車將會旋轉,但是還不夠流暢。風車的旋轉角度屬性被直接改變了。我們應該怎樣讓90度的旋轉可以持續一段時間呢。現在是動畫效果發揮作用的時候了。一個動畫定義了一個屬性的在一段時間內的變化過程。為了實現這個效果,我們使用一個動畫類型叫做屬性行為。這個行為指定了一個動畫來定義屬性的每一次改變並賦值給屬性。每次屬性改變,動畫都會運行。這是QML中聲明動畫的幾種方式中的一種方式。 101 | 102 | ``` 103 | Image { 104 | id: root 105 | Image { 106 | id: wheel 107 | Behavior on rotation { 108 | NumberAnimation { 109 | duration: 250 110 | } 111 | } 112 | } 113 | } 114 | ``` 115 | 116 | 現在每當風車旋轉角度發生改變時都會使用NumberAnimation來實現250毫秒的旋轉動畫效果。每一次90度的轉變都需要花費250ms。 117 | 118 | 現在風車看起來好多了,我希望以上這些能夠讓你能夠對Qt Quick編程有一些了解。 119 | -------------------------------------------------------------------------------- /particle_simulations/affecting_particles.md: -------------------------------------------------------------------------------- 1 | # 粒子控制(Affecting Particles) 2 | 3 | 粒子由粒子發射器發出。在粒子發射出後,發射器無法再改變粒子。粒子控制器允許你控制發射後的粒子參數。 4 | 5 | 控制器的每個類型使用不同的方法來影響粒子: 6 | 7 | * 生命週期(Age)- 修改粒子的生命週期 8 | 9 | * 吸引(Attractor)- 吸引粒子朝向指定點 10 | 11 | * 摩擦(Friction)- 按當前粒子速度成正比減慢運動 12 | 13 | * 重力(Gravity)- 設置一個角度的加速度 14 | 15 | * 紊流(Turbulence)- 強制基于噪聲圖像方式的流動 16 | 17 | * 漂移(Wander)- 隨機變化的軌蹟 18 | 19 | * 組目標(GroupGoal)- 改變一組粒子群的狀態 20 | 21 | * 子粒子(SpriteGoal)- 改變一個子粒子的狀態 22 | 23 | **生命週期(Age)** 24 | 25 | 允許粒子老得更快,lifeLeft屬性指定了粒子的有多少的生命週期。 26 | 27 | ``` 28 | Age { 29 | anchors.horizontalCenter: parent.horizontalCenter 30 | width: 240; height: 120 31 | system: particleSystem 32 | advancePosition: true 33 | lifeLeft: 1200 34 | once: true 35 | Tracer {} 36 | } 37 | ``` 38 | 39 | 在這個例子中,當粒子的生命週期達到1200毫秒後,我們將會縮短上方的粒子的生命週期一次。由于我們設置了advancePosition為true,當粒子的生命週期到達1200毫秒後,我們將會再一次在這個位置看到粒子出現。 40 | 41 | ![](http://qmlbook.org/_images/age.png) 42 | 43 | **吸引(Attractor)** 44 | 45 | 吸引會將粒子朝指定的點上吸引。這個點使用pointX與pointY來指定,它是與吸引區域的幾何形狀相對的。strength指定了吸引的力度。在我們的例子中,我們讓粒子從左向右運動,吸引放在頂部,有一半運動的粒子會穿過吸引區域。控制器只會影響在它們幾何形狀內的粒子。這種分離讓我們可以同步看到正常的流動與受影響的流動。 46 | 47 | ``` 48 | Attractor { 49 | anchors.horizontalCenter: parent.horizontalCenter 50 | width: 160; height: 120 51 | system: particleSystem 52 | pointX: 0 53 | pointY: 0 54 | strength: 1.0 55 | Tracer {} 56 | } 57 | ``` 58 | 59 | 很容易看出上半部分粒子受到吸引。吸引點被設置為吸引區域的左上角(0/0點),吸引力為1.0。 60 | 61 | ![](http://qmlbook.org/_images/attractor.png) 62 | 63 | **摩擦(Friction)** 64 | 65 | 摩擦控制器使用一個參數(factor)減慢粒子運動,直到達到一個閾值。 66 | 67 | ``` 68 | Friction { 69 | anchors.horizontalCenter: parent.horizontalCenter 70 | width: 240; height: 120 71 | system: particleSystem 72 | factor : 0.8 73 | threshold: 25 74 | Tracer {} 75 | } 76 | ``` 77 | 78 | 在上部的摩擦區域,粒子被按照0.8的參數(factor)減慢,直到粒子的速度達到25像素每秒。這個閾值像一個過濾器。粒子運動速度高于閾值將會按照給定的參數來減慢它。 79 | 80 | ![](http://qmlbook.org/_images/friction.png) 81 | 82 | **重力(Gravity)** 83 | 84 | 重力控制器應用在加速度上,在我們的例子中,我們使用一個角度方向將粒子從底部發射到頂部。右邊是為控制區域,左邊使用重力控制器控制,重力方向為90度方向(垂直向下),梯度值為50。 85 | 86 | ``` 87 | Gravity { 88 | width: 240; height: 240 89 | system: particleSystem 90 | magnitude: 50 91 | angle: 90 92 | Tracer {} 93 | } 94 | ``` 95 | 96 | 左邊的粒子試圖爬上去,但是穩定向下的加速度將它們按照重力的方向拖拽下來。 97 | 98 | ![](http://qmlbook.org/_images/gravity.png) 99 | 100 | **紊流(Turbulence)** 101 | 102 | 紊流控制器,對粒子應用了一個混亂映射方向力的矢量。這個混亂映射是由一個噪聲圖像定義的。可以使用noiseSource屬性來定義噪聲圖像。strength定義了矢量對于粒子運動的影響有多大。 103 | 104 | ``` 105 | Turbulence { 106 | anchors.horizontalCenter: parent.horizontalCenter 107 | width: 240; height: 120 108 | system: particleSystem 109 | strength: 100 110 | Tracer {} 111 | } 112 | ``` 113 | 114 | 在這個例子中,上部區域被紊流影響。它們的運動看起來是不穩定的。不穩定的粒子偏差值來自原路徑定義的strength。 115 | 116 | ![](http://qmlbook.org/_images/turbulence.png) 117 | 118 | **漂移(Wander)** 119 | 120 | 漂移控制器控制了軌蹟。affectedParameter屬性可以指定哪個參數控制了漂移(速度,位置或者加速度)。pace屬性制定了每秒最多改變的屬性。yVariance指定了y組件對粒子軌蹟的影響。 121 | 122 | ``` 123 | Wander { 124 | anchors.horizontalCenter: parent.horizontalCenter 125 | width: 240; height: 120 126 | system: particleSystem 127 | affectedParameter: Wander.Position 128 | pace: 200 129 | yVariance: 240 130 | Tracer {} 131 | } 132 | ``` 133 | 134 | 在頂部漂移控制器的粒子被隨機的軌蹟改變。在這種情境下,每秒改變粒子y方向的位置200次。 135 | 136 | ![](http://qmlbook.org/_images/wander.png) 137 | -------------------------------------------------------------------------------- /shader_effect/curtain_effect.md: -------------------------------------------------------------------------------- 1 | # 劇幕效果(Curtain Effect) 2 | 3 | 在最後的自定義效果例子中,我們將帶來一個劇幕效果。這個效果是2011年5月Qt實驗室發布的著色器效果中的一部分。目前網址已經轉到blog.qt.digia.com,不知道還能不能找到。 4 | 5 | ![](http://qmlbook.org/_images/curtain.png) 6 | 7 | 當時我非常喜歡這些效果,劇幕效果是我最喜愛的一個。我喜歡劇幕打開然後遮擋後面的背景對象。 8 | 9 | 我將代碼移植適配到Qt5上,這非常簡單。同時我做了一些簡化讓它能夠更好的展示。如果你對整個例子有興趣,可以訪問Qt實驗室的博客。 10 | 11 | 只有一個小組件作為背景,劇幕實際上是一張圖片,叫做fabric.jpg,它是ShaderEffect的資源。整個效果使用頂點著色器來擺動劇幕,使用片段著色器提供陰影的效果。下面是一個簡單的圖片,讓你更加容易理解代碼。 12 | 13 | ![](http://qmlbook.org/_images/curtain_diagram.png) 14 | 15 | 劇幕的波形陰影通過一個在劇幕寬度上的sin曲線使用7的振幅來計算(7*PI=221.99..)另一個重要的部分是擺動,當劇幕打開或者關閉時,使用動畫來播放劇幕的topWidth。bottomWidth使用SpringAnimation來跟隨topWidth變化。這樣我們就能創建出底部擺動的劇幕效果。計算得到的swing提供了搖擺的強度,用來對頂點的y值進行插值。 16 | 17 | 劇幕效果放在CurtainEffect.qml組件中,fabric圖像作為紋理資源。在陰影的使用上沒有新的東西加入,唯一不同的是在頂點著色器中操作gl_Postion和片段著色器中操作gl_FragColor。 18 | 19 | ``` 20 | import QtQuick 2.0 21 | 22 | ShaderEffect { 23 | anchors.fill: parent 24 | 25 | mesh: GridMesh { 26 | resolution: Qt.size(50, 50) 27 | } 28 | 29 | property real topWidth: open?width:20 30 | property real bottomWidth: topWidth 31 | property real amplitude: 0.1 32 | property bool open: false 33 | property variant source: effectSource 34 | 35 | Behavior on bottomWidth { 36 | SpringAnimation { 37 | easing.type: Easing.OutElastic; 38 | velocity: 250; mass: 1.5; 39 | spring: 0.5; damping: 0.05 40 | } 41 | } 42 | 43 | Behavior on topWidth { 44 | NumberAnimation { duration: 1000 } 45 | } 46 | 47 | 48 | ShaderEffectSource { 49 | id: effectSource 50 | sourceItem: effectImage; 51 | hideSource: true 52 | } 53 | 54 | Image { 55 | id: effectImage 56 | anchors.fill: parent 57 | source: "assets/fabric.jpg" 58 | fillMode: Image.Tile 59 | } 60 | 61 | vertexShader: " 62 | attribute highp vec4 qt_Vertex; 63 | attribute highp vec2 qt_MultiTexCoord0; 64 | uniform highp mat4 qt_Matrix; 65 | varying highp vec2 qt_TexCoord0; 66 | varying lowp float shade; 67 | 68 | uniform highp float topWidth; 69 | uniform highp float bottomWidth; 70 | uniform highp float width; 71 | uniform highp float height; 72 | uniform highp float amplitude; 73 | 74 | void main() { 75 | qt_TexCoord0 = qt_MultiTexCoord0; 76 | 77 | highp vec4 shift = vec4(0.0, 0.0, 0.0, 0.0); 78 | highp float swing = (topWidth - bottomWidth) * (qt_Vertex.y / height); 79 | shift.x = qt_Vertex.x * (width - topWidth + swing) / width; 80 | 81 | shade = sin(21.9911486 * qt_Vertex.x / width); 82 | shift.y = amplitude * (width - topWidth + swing) * shade; 83 | 84 | gl_Position = qt_Matrix * (qt_Vertex - shift); 85 | 86 | shade = 0.2 * (2.0 - shade ) * ((width - topWidth + swing) / width); 87 | }" 88 | 89 | fragmentShader: " 90 | uniform sampler2D source; 91 | varying highp vec2 qt_TexCoord0; 92 | varying lowp float shade; 93 | void main() { 94 | highp vec4 color = texture2D(source, qt_TexCoord0); 95 | color.rgb *= 1.0 - shade; 96 | gl_FragColor = color; 97 | }" 98 | } 99 | ``` 100 | 101 | 這個效果在curtaindemo.qml文件中使用。 102 | 103 | ``` 104 | import QtQuick 2.0 105 | 106 | Rectangle { 107 | id: root 108 | width: 480; height: 240 109 | color: '#1e1e1e' 110 | 111 | Image { 112 | anchors.centerIn: parent 113 | source: 'assets/wiesn.jpg' 114 | } 115 | 116 | CurtainEffect { 117 | id: curtain 118 | anchors.fill: parent 119 | } 120 | 121 | MouseArea { 122 | anchors.fill: parent 123 | onClicked: curtain.open = !curtain.open 124 | } 125 | } 126 | ``` 127 | 128 | 劇幕效果通過自定義的open屬性打開。我們使用了一個MouseArea來觸發打開和關閉劇幕。 129 | -------------------------------------------------------------------------------- /networking/httpuiserving_ui_via_http.md: -------------------------------------------------------------------------------- 1 | # 通過HTTP服務UI(Serving UI via HTTP) 2 | 3 | 通過HTTP加載一個簡單的用戶界面,我們需要一個web服務器,它為UI文件服務。但是首先我們需要有用戶界面,我們在項目裡創建一個創建了紅色矩形框的main.qml。 4 | 5 | ``` 6 | // main.qml 7 | import QtQuick 2.0 8 | 9 | Rectangle { 10 | width: 320 11 | height: 320 12 | color: '#ff0000' 13 | } 14 | ``` 15 | 16 | 我們加載一段python腳本來提供這個文件: 17 | 18 | ``` 19 | $ cd 20 | # python -m SimpleHTTPServer 8080 21 | ``` 22 | 23 | 現在我們可以通過[http://localhost:8000/main.qml](http://localhost:8000/main.qml)來訪問,你可以像下面這樣測試: 24 | 25 | ``` 26 | $ curl http://localhost:8000/main.qml 27 | ``` 28 | 29 | 或者你可以用瀏覽器來訪問。瀏覽器無法識別QML,並且無法通過文檔來渲染。我們需要創建一個可以瀏覽QML文檔的瀏覽器。為了渲染文檔,我們需要指出qmlscene的位置。不幸的是qmlscene只能讀取本地文件。我們為了突破這個限制,我們可以使用自己寫的qmlscene或者使用QML動態加載。我們選擇動態加載的方式。我們選擇一個加載元素來加載遠程的文檔。 30 | 31 | ``` 32 | // remote.qml 33 | import QtQuick 2.0 34 | 35 | Loader { 36 | id: root 37 | source: 'http://localhost:8080/main2.qml' 38 | onLoaded: { 39 | root.width = item.width 40 | root.height = item.height 41 | } 42 | } 43 | ``` 44 | 45 | 我們現在可以使用qmlscene來加載remote.qml文檔。這裡仍然有一個小問題。加載器將會調整加載項的大小。我們的qmlscene需要適配大小。可以使用--resize-to-root選項來運行qmlscene。 46 | 47 | ``` 48 | $ qmlscene --resize-to-root remote.qml 49 | ``` 50 | 51 | 按照root元素調整大小,告訴qmlscene按照root元素的大小調它的窗口大小。remote現在從本地服務器加載main.qml,並且可以自動調整加載的用戶界面。方便且簡單。 52 | 53 | **注意** 54 | 55 | **如果你不想使用一個本地服務器,你可以使用來自GitHub的gist服務。Gist是一個在線剪切板服務,就像PasteBin等等。可以在[https://gist.github.com](https://gist.github.com )下使用。我創建了一個簡單的gist例子,地址是[https://gist.github.com/jryannel/7983492](https://gist.github.com/jryannel/7983492)。這將會返回一個綠色矩形框。由于gist連接提供的是HTML代碼,我們需要連接一個/raw來讀取原始文件而不是HTML代碼。** 56 | 57 | ``` 58 | // remote.qml 59 | import QtQuick 2.0 60 | 61 | Loader { 62 | id: root 63 | source: 'https://gist.github.com/jryannel/7983492/raw' 64 | onLoaded: { 65 | root.width = item.width 66 | root.height = item.height 67 | } 68 | } 69 | ``` 70 | 71 | 從網絡加載另一個文件,你只需要引用組件名。例如一個Button.qml,只要它們在同一個遠程文件夾下就能夠像正常一樣訪問。 72 | 73 | ## 11.1.1 網絡組件(Networked Components) 74 | 75 | 我們做了一個小實驗。我們在遠程端添加一個按鈕作為可以復用的組件。 76 | 77 | ``` 78 | - src/main.qml 79 | - src/Button.qml 80 | ``` 81 | 82 | 我們修改main.qml來使用button: 83 | 84 | ``` 85 | import QtQuick 2.0 86 | 87 | Rectangle { 88 | width: 320 89 | height: 320 90 | color: '#ff0000' 91 | 92 | Button { 93 | anchors.centerIn: parent 94 | text: 'Click Me' 95 | onClicked: Qt.quit() 96 | } 97 | } 98 | ``` 99 | 100 | 再次加載我們的web服務器: 101 | 102 | ``` 103 | $ cd src 104 | # python -m SimpleHTTPServer 8080 105 | ``` 106 | 107 | 再次使用http加載遠mainQML文件: 108 | 109 | ``` 110 | $ qmlscene --resize-to-root remote.qml 111 | ``` 112 | 113 | 我們看到一個錯誤: 114 | 115 | ``` 116 | http://localhost:8080/main2.qml:11:5: Button is not a type 117 | ``` 118 | 119 | 所以,在遠程加載時,QML無法解決Button組件的問題。如果代碼使用本地加載qmlscene src/main.qml,將不會有問題。Qt能夠直接解析本地文件,並且檢測哪些組件可用,但是使用http的遠程訪問沒有“list-dir”函數。我們可以在main.qml中使用import聲明來強制QML加載元素: 120 | 121 | ``` 122 | import "http://localhost:8080" as Remote 123 | 124 | ... 125 | 126 | Remote.Button { ... } 127 | ``` 128 | 129 | 再次運行qmlscene後,它將正常工作: 130 | 131 | ``` 132 | $ qmlscene --resize-to-root remote.qml 133 | ``` 134 | 135 | 這是完整的代碼: 136 | 137 | ``` 138 | // main2.qml 139 | import QtQuick 2.0 140 | import "http://localhost:8080" 1.0 as Remote 141 | 142 | Rectangle { 143 | width: 320 144 | height: 320 145 | color: '#ff0000' 146 | 147 | Remote.Button { 148 | anchors.centerIn: parent 149 | text: 'Click Me' 150 | onClicked: Qt.quit() 151 | } 152 | } 153 | ``` 154 | 155 | 一個更好的選擇是在服務器端使用qmldir文件來控制輸出: 156 | 157 | ``` 158 | // qmldir 159 | Button 1.0 Button.qml 160 | ``` 161 | 162 | 然後更新main.qml: 163 | 164 | ``` 165 | import "http://localhost:8080" 1.0 as Remote 166 | 167 | ... 168 | 169 | Remote.Button { ... } 170 | ``` 171 | 172 | 當從本地文件系統使用組件時,它們的創建沒有延遲。當組件通過網絡加載時,它們的創建是異步的。創建時間的影響是未知的,當其它組件已經完成時,一個組件可能還沒有完成加載。當通過網絡加載組件時,需要考慮這些。 173 | -------------------------------------------------------------------------------- /particle_simulations/directed_particle.md: -------------------------------------------------------------------------------- 1 | # 粒子方向(Directed Particle) 2 | 3 | 我們已經看到了粒子的旋轉,但是我們的粒子需要一個軌蹟。軌蹟由速度或者粒子隨機方向的加速度指定,也可以叫做矢量空間。 4 | 5 | 有多種可用矢量空間用來定義粒子的速度或加速度: 6 | 7 | * 角度方向(AngleDirection)- 使用角度的方向變化。 8 | 9 | * 點方向(PointDirection)- 使用x,y組件組成的方向變化。 10 | 11 | * 目標方向(TargetDirection)- 朝著目標點的方向變化。 12 | 13 | ![](http://qmlbook.org/_images/particle_directions.png) 14 | 15 | 讓我們在場景下試著用速度方向將粒子從左邊移動到右邊。 16 | 17 | 首先使用角度方向(AngleDirection)。我們使用AngleDirection元素作為我們的發射器(Emitter)的速度屬性: 18 | 19 | ``` 20 | velocity: AngleDirection { } 21 | ``` 22 | 23 | 粒子的發射將會使用指定的角度屬性。角度值在0到360度之間,0度代表指向右邊。在我們的例子中,例子將會移動到右邊,所以0度已經指向右邊方向。粒子的角度變化在+/-15度之間: 24 | 25 | ``` 26 | velocity: AngleDirection { 27 | angle: 0 28 | angleVariation: 15 29 | } 30 | ``` 31 | 32 | 現在我們已經設置了方向,下面是指定粒子的速度。它由一個梯度值定義,這個梯度值定義了每秒像素的變化。正如我們設置大約640像素,梯度值為100,看起來是一個不錯的值。這意味著平均一個6.4秒生命週期的粒子可以穿越我們看到的區域。為了讓粒子的穿越看起來更加有趣,我們使用magnitudeVariation來設置梯度值的變化,這個值是我們的梯度值的一半: 33 | 34 | ``` 35 | velocity: AngleDirection { 36 | ... 37 | magnitude: 100 38 | magnitudeVariation: 50 39 | } 40 | ``` 41 | 42 | ![](http://qmlbook.org/_images/angledirection.png) 43 | 44 | 下面是完整的源碼,平均的生命週期被設置為6..4秒。我們設置發射器的寬度和高度為1個像素,這意味著所有的粒子都從相同的位置發射出去,然後基于我們給定的軌蹟運動。 45 | 46 | ``` 47 | Emitter { 48 | id: emitter 49 | anchors.left: parent.left 50 | anchors.verticalCenter: parent.verticalCenter 51 | width: 1; height: 1 52 | system: particleSystem 53 | lifeSpan: 6400 54 | lifeSpanVariation: 400 55 | size: 32 56 | velocity: AngleDirection { 57 | angle: 0 58 | angleVariation: 15 59 | magnitude: 100 60 | magnitudeVariation: 50 61 | } 62 | } 63 | ``` 64 | 65 | 那麼加速度做些什麼?加速度是每個粒子加速度矢量,它會在運動的時間中改變速度矢量。例如我們做一個星星按照弧形運動的軌蹟。我們將會改變我們的速度方向為-45度,然後移除變量,可以得到一個更連貫的弧形軌蹟: 66 | 67 | ``` 68 | velocity: AngleDirection { 69 | angle: -45 70 | magnitude: 100 71 | } 72 | ``` 73 | 74 | 加速度的方向為90度(向下),加速度為速度的四分之一: 75 | 76 | ``` 77 | acceleration: AngleDirection { 78 | angle: 90 79 | magnitude: 25 80 | } 81 | ``` 82 | 83 | 結果是中間左方到右下的一個弧。 84 | 85 | ![](http://qmlbook.org/_images/angledirection2.png) 86 | 這個值是在不斷的嘗試與錯誤中發現的。 87 | 88 | 下面是發射器完整的代碼。 89 | 90 | ``` 91 | Emitter { 92 | id: emitter 93 | anchors.left: parent.left 94 | anchors.verticalCenter: parent.verticalCenter 95 | width: 1; height: 1 96 | system: particleSystem 97 | emitRate: 10 98 | lifeSpan: 6400 99 | lifeSpanVariation: 400 100 | size: 32 101 | velocity: AngleDirection { 102 | angle: -45 103 | angleVariation: 0 104 | magnitude: 100 105 | } 106 | acceleration: AngleDirection { 107 | angle: 90 108 | magnitude: 25 109 | } 110 | } 111 | ``` 112 | 113 | 在下一個例子中,我們將使用點方向(PointDirection)矢量空間來再一次演示粒子從左到右的運動。 114 | 115 | 一個點方向(PointDirection)是由x和y組件組成的矢量空間。例如,如果你想粒子以45度的矢量運動,你需要為x,y指定相同的值。 116 | 117 | 在我們的例子中,我們希望粒子在從左到右的例子中建立一個15度的圓錐。我們指定一個坐標方向(PointDirection)作為我們速度矢量空間: 118 | 119 | ``` 120 | velocity: PointDirection { } 121 | ``` 122 | 123 | 為了達到運動速度每秒100個像素,我們設置x為100,。15度角(90度的1/6),我們指定y變量為100/6: 124 | 125 | ``` 126 | velocity: PointDirection { 127 | x: 100 128 | y: 0 129 | xVariation: 0 130 | yVariation: 100/6 131 | } 132 | ``` 133 | 134 | 結果是粒子的運動從左到右構成了一個15度的圓錐。 135 | 136 | ![](http://qmlbook.org/_images/pointdirection.png) 137 | 138 | 現在是最後一個方案,目標方向(TargetDirection)。目標方向允許我們指定發射器或者一個QML項的x,y坐標值。當一個QML項的中心點成為一個目標點時,你可以指定目標變化值是x目標值的1/6來完成一個15度的圓錐: 139 | 140 | ``` 141 | velocity: TargetDirection { 142 | targetX: 100 143 | targetY: 0 144 | targetVariation: 100/6 145 | magnitude: 100 146 | } 147 | ``` 148 | 149 | **注意** 150 | 151 | **當你期望發射粒子朝著指定的x,y坐標值流動時,目標方向是非常好的方案。** 152 | 153 | 我沒有再貼出結果圖,因為它與前一個結果相同,取而代之的有一個問題留給你。 154 | 155 | 在下圖的紅色和綠色圓指定每個目標項的目標方向速度的加速屬性。每個目標方向有相同的參數。那麼哪一個負責速度,哪一個負責加速度? 156 | 157 | ![](http://qmlbook.org/_images/directionquest.png) 158 | -------------------------------------------------------------------------------- /shader_effect/shader_elements.md: -------------------------------------------------------------------------------- 1 | # 著色器元素(Shader Elements) 2 | 3 | 為了對著色器編程,Qt Quick提供了兩個元素。ShaderEffectSource與ShaderEffect。ShaderEffect將會使用自定義的著色器,ShaderEffectSource可以將一個QML元素渲染為一個紋理然後再渲染這個紋理。由于ShaderEffect能夠應用自定義的著色器到它的矩形幾何形狀,並且能夠使用在著色器中操作資源。一個資源可以是一個圖片,它被作為一個紋理或者著色器資源。 4 | 5 | 默認下著色器使用這個資源並且不作任何改變進行渲染。 6 | 7 | ``` 8 | import QtQuick 2.0 9 | 10 | Rectangle { 11 | width: 480; height: 240 12 | color: '#1e1e1e' 13 | 14 | Row { 15 | anchors.centerIn: parent 16 | spacing: 20 17 | Image { 18 | id: sourceImage 19 | width: 80; height: width 20 | source: 'assets/tulips.jpg' 21 | } 22 | ShaderEffect { 23 | id: effect 24 | width: 80; height: width 25 | property variant source: sourceImage 26 | } 27 | ShaderEffect { 28 | id: effect2 29 | width: 80; height: width 30 | // the source where the effect shall be applied to 31 | property variant source: sourceImage 32 | // default vertex shader code 33 | vertexShader: " 34 | uniform highp mat4 qt_Matrix; 35 | attribute highp vec4 qt_Vertex; 36 | attribute highp vec2 qt_MultiTexCoord0; 37 | varying highp vec2 qt_TexCoord0; 38 | void main() { 39 | qt_TexCoord0 = qt_MultiTexCoord0; 40 | gl_Position = qt_Matrix * qt_Vertex; 41 | }" 42 | // default fragment shader code 43 | fragmentShader: " 44 | varying highp vec2 qt_TexCoord0; 45 | uniform sampler2D source; 46 | uniform lowp float qt_Opacity; 47 | void main() { 48 | gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; 49 | }" 50 | } 51 | } 52 | } 53 | ``` 54 | 55 | ![](http://qmlbook.org/_images/defaultshader.png) 56 | 57 | 在上邊這個例子中,我們在一行中顯示了3張圖片,第一張是原始圖片,第二張使用默認的著色器渲染出來的圖片,第三張使用了Qt5源碼中默認的頂點與片段著色器的代碼進行渲染的圖片。 58 | 59 | **注意** 60 | 61 | **如果你不想看到原始圖片,而只想看到被著色器渲染後的圖片,你可以設置Image為不可見(visible:false)。著色器仍然會使用圖片數據,但是圖像元素(Image Element)將不會被渲染。** 62 | 63 | 讓我們仔細看看著色器代碼。 64 | 65 | ``` 66 | vertexShader: " 67 | uniform highp mat4 qt_Matrix; 68 | attribute highp vec4 qt_Vertex; 69 | attribute highp vec2 qt_MultiTexCoord0; 70 | varying highp vec2 qt_TexCoord0; 71 | void main() { 72 | qt_TexCoord0 = qt_MultiTexCoord0; 73 | gl_Position = qt_Matrix * qt_Vertex; 74 | }" 75 | ``` 76 | 77 | 著色器代碼來自Qt這邊的一個字符串,綁定了頂點著色器(vertexShader)與片段著色器(fragmentShader)屬性。每個著色器代碼必須有一個main(){....}函數,它將被GPU執行。Qt已經默認提供了以qt_開頭的變量。 78 | 79 | 下面是這些變量簡短的介紹: 80 | 81 | * uniform-在處理過程中不能夠改變的值。 82 | 83 | * attribute-連接外部數據 84 | 85 | * varying-著色器之間的共享數據 86 | 87 | * highp-高精度值 88 | 89 | * lowp-低精度值 90 | 91 | * mat4-4x4浮點數(float)矩陣 92 | 93 | * vec2-包含兩個浮點數的向量 94 | 95 | * sampler2D-2D紋理 96 | 97 | * float-浮點數 98 | 99 | 可以查看[OpenGL ES 2.0 API Quick Reference Card](http://www.khronos.org/opengles/sdk/docs/reference_cards/OpenGL-ES-2_0-Reference-card.pdf)獲得更多信息。 100 | 101 | 現在我們可以更好的理解下面這些變量: 102 | 103 | * qt_Matrix:model-view-projection(模型-視圖-投影)矩陣 104 | 105 | * qt_Vertex:當前頂點坐標 106 | 107 | * qt_MultiTexCoord0:紋理坐標 108 | 109 | * qt_TexCoord0:共享紋理坐標 110 | 111 | 我們已經有可以使用的投影矩陣(projection matrix),當前頂點與紋理坐標。紋理坐標與作為資源(source)的紋理相關。在main()函數中,我們保存紋理坐標,留在後面的片段著色器中使用。每個頂點著色器都需要賦值給gl_Postion,在這裡使用項目矩陣乘以頂點,得到我們3D坐標系中的點。 112 | 113 | 片段著色器從頂點著色器中接收我們的紋理坐標,這個紋理仍然來自我們的QML資源屬性(source property)。在著色器代碼與QML之間傳遞變量是如此的簡單。此外我們的透明值,在著色器中也可以使用,變量是qt_Opacity。每 114 | 個片段著色器需要給gl_FragColor變量賦值,在這裡默認著色器代碼使用資源紋理(source texture)的像素顏色與透明值相乘。 115 | 116 | ``` 117 | fragmentShader: " 118 | varying highp vec2 qt_TexCoord0; 119 | uniform sampler2D source; 120 | uniform lowp float qt_Opacity; 121 | void main() { 122 | gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; 123 | }" 124 | ``` 125 | 126 | 在後面的例子中,我們將會展示一些簡單的著色器例子。首先我們會集中在片段著色器上,然後在回到頂點著色器上。 127 | -------------------------------------------------------------------------------- /model-view-delegate/basic_model.md: -------------------------------------------------------------------------------- 1 | # 基礎模型(Basic Model) 2 | 3 | 最基本的分離數據與顯示的方法是使用Repeater元素。它被用于實例化一組元素項,並且很容易與一個用于填充用戶界面的定位器相結合。 4 | 5 | 最基本的實現舉例,repeater元素用于實現子元素的標號。每個子元素都擁有一個可以訪問的屬性index,用于區分不同的子元素。在下面的例子中,一個repeater元素創建了10個子項,子項的數量由model屬性控制。對于每個子項Rectangle包含了一個Text元素,你可以將text屬性設置為index的值,因此可以看到子項的編號是0~9。 6 | 7 | ``` 8 | import QtQuick 2.0 9 | 10 | Column { 11 | spacing: 2 12 | 13 | Repeater { 14 | model: 10 15 | 16 | Rectangle { 17 | width: 100 18 | height: 20 19 | 20 | radius: 3 21 | 22 | color: "lightBlue" 23 | 24 | Text { 25 | anchors.centerIn: parent 26 | text: index 27 | } 28 | } 29 | } 30 | } 31 | ``` 32 | 33 | ![](http://qmlbook.org/_images/repeater-number.png) 34 | 35 | 這是一個不錯的編號列表,有時我們想顯示一些更復雜的數據。使用一個JavaScript序列來替換整形變量model的值可以達到我們的目的。序列可以使用任何類型的內容,可以是字符串,整數,或者對象。在下面的例子中,使用了一個字符串鏈表。我們仍然使用index的值作為變量,並且我們也訪問modelData中包含的每個元素的數據。 36 | 37 | ``` 38 | import QtQuick 2.0 39 | 40 | Column { 41 | spacing: 2 42 | 43 | Repeater { 44 | model: ["Enterprise", "Colombia", "Challenger", "Discovery", "Endeavour", "Atlantis"] 45 | 46 | Rectangle { 47 | width: 100 48 | height: 20 49 | 50 | radius: 3 51 | 52 | color: "lightBlue" 53 | 54 | Text { 55 | anchors.centerIn: parent 56 | text: index +": "+modelData 57 | } 58 | } 59 | } 60 | } 61 | ``` 62 | 63 | ![](http://qmlbook.org/_images/repeater-array.png) 64 | 65 | 將數據暴露成一組序列,你可以通過標號迅速的找到你需要的信息。想象一下這個模型的草圖,這是一個最簡單的模型,也是通常都會使用的模型,ListModel(鏈表模型)。一個鏈表模型由許多ListElement(鏈表元素)組成。在每個鏈表元素中,可以綁定值到屬性上。例如在下面這個例子中,每個元素都提供了一個名字和一個顏色。 66 | 67 | 每個元素中的屬性綁定連接到repeater實例化的子項上。這意味著變量name和surfaceColor可以被repeater創建的每個Rectangle和Text項引用。這不僅可以方便的訪問數據,也可以使源代碼更加容易閱讀。surfaceColor是名字左邊圓的顏色,而不是模糊的數據序列列i或者行j。 68 | 69 | ``` 70 | import QtQuick 2.0 71 | 72 | Column { 73 | spacing: 2 74 | 75 | Repeater { 76 | model: ListModel { 77 | ListElement { name: "Mercury"; surfaceColor: "gray" } 78 | ListElement { name: "Venus"; surfaceColor: "yellow" } 79 | ListElement { name: "Earth"; surfaceColor: "blue" } 80 | ListElement { name: "Mars"; surfaceColor: "orange" } 81 | ListElement { name: "Jupiter"; surfaceColor: "orange" } 82 | ListElement { name: "Saturn"; surfaceColor: "yellow" } 83 | ListElement { name: "Uranus"; surfaceColor: "lightBlue" } 84 | ListElement { name: "Neptune"; surfaceColor: "lightBlue" } 85 | } 86 | 87 | Rectangle { 88 | width: 100 89 | height: 20 90 | 91 | radius: 3 92 | 93 | color: "lightBlue" 94 | 95 | Text { 96 | anchors.centerIn: parent 97 | text: name 98 | } 99 | 100 | Rectangle { 101 | anchors.left: parent.left 102 | anchors.verticalCenter: parent.verticalCenter 103 | anchors.leftMargin: 2 104 | 105 | width: 16 106 | height: 16 107 | 108 | radius: 8 109 | 110 | border.color: "black" 111 | border.width: 1 112 | 113 | color: surfaceColor 114 | } 115 | } 116 | } 117 | } 118 | ``` 119 | 120 | ![](http://qmlbook.org/_images/repeater-model.png) 121 | 122 | repeater的內容的每個子項實例化時綁定了默認的屬性delegate(代理)。這意味著例1(第一個代碼段)的代碼與下面顯示的代碼是相同的。注意,唯一的不同是delegate屬性名,將會在後面詳細講解。 123 | 124 | ``` 125 | import QtQuick 2.0 126 | 127 | Column { 128 | spacing: 2 129 | 130 | Repeater { 131 | model: 10 132 | 133 | delegate: Rectangle { 134 | width: 100 135 | height: 20 136 | 137 | radius: 3 138 | 139 | color: "lightBlue" 140 | 141 | Text { 142 | anchors.centerIn: parent 143 | text: index 144 | } 145 | } 146 | } 147 | } 148 | ``` 149 | -------------------------------------------------------------------------------- /fluid_elements/states_and_transitions.md: -------------------------------------------------------------------------------- 1 | # 狀態與過渡(States and Transitions) 2 | 3 | 通常我們將用戶界面描述為一種狀態。一個狀態定義了一組屬性的改變,並且會在一定的條件下被觸發。另外在這些狀態轉化的過程中可以有一個過渡,定義了這些屬性的動畫或者一些附加的動作。當進入一個新的狀態時,動作也可以被執行。 4 | 5 | ## 5.2.1 狀態(States) 6 | 7 | 在QML中,使用State元素來定義狀態,需要與基礎元素對象(Item)的states序列屬性連接。狀態通過它的狀態名來鑑別,由組成它的一系列簡單的屬性來改變元素。默認的狀態在初始化元素屬性時定義,並命名為“”(一個空的字符串)。 8 | 9 | ``` 10 | Item { 11 | id: root 12 | states: [ 13 | State { 14 | name: "go" 15 | PropertyChanges { ... } 16 | }, 17 | State { 18 | name: "stop" 19 | PropertyChanges { ... } 20 | } 21 | ] 22 | } 23 | ``` 24 | 25 | 狀態的改變由分配一個元素新的狀態屬性名來完成。 26 | 27 | **注意** 28 | 29 | **另一種切換屬性的方法是使用狀態元素的when屬性。when屬性能夠被設置為一個表達式的結果,當結果為true時,狀態被使用**。 30 | 31 | ``` 32 | Item { 33 | id: root 34 | states: [ 35 | ... 36 | ] 37 | 38 | Button { 39 | id: goButton 40 | ... 41 | onClicked: root.state = "go" 42 | } 43 | } 44 | ``` 45 | 46 | ![](http://qmlbook.org/_images/trafficlight_sketch.png) 47 | 48 | 例如一個交通信號燈有兩個信號燈。上面的一個信號燈使用紅色,下面的信號燈使用綠色。在這個例子中,兩個信號燈不會同時發光。讓我們看看狀態圖。 49 | 50 | ![](http://qmlbook.org/_images/trafficlight_states.png) 51 | 52 | 當系統啟動時,它會自動切換到停止模式作為默認狀態。停止狀態改變了light1為紅色並且light2為黑色(關閉)。一個外部的事件能夠觸發現在的狀態變換為“go”狀態。在go狀態下,我們改變顏色屬性,light1變為黑色(關閉),light2變為綠色。 53 | 54 | 為了實現這個方案,我們給這兩個燈繪制一個用戶界面的草圖,為了簡單起見,我們使用兩個包含園邊的矩形框,設置園半徑為寬度的一半(寬度與高度相同)。 55 | 56 | ``` 57 | Rectangle { 58 | id: light1 59 | x: 25; y: 15 60 | width: 100; height: width 61 | radius: width/2 62 | color: "black" 63 | } 64 | 65 | Rectangle { 66 | id: light2 67 | x: 25; y: 135 68 | width: 100; height: width 69 | radius: width/2 70 | color: "black" 71 | } 72 | ``` 73 | 74 | 就像在狀態圖中定義的一樣,我們有一個“go”狀態和一個“stop”狀態,它們將會分別將交通燈改變為紅色和綠色。我們設置state屬性到stop來確保初始化狀態為stop狀態。 75 | 76 | **注意** 77 | 78 | **我們可以只使用“go”狀態來達到同樣的效果,設置顏色light1為紅色,顏色light2為黑色。初始化狀態“”(空字符串)定義初始化屬性,並且扮演類似“stop”狀態的角色。** 79 | 80 | ``` 81 | state: "stop" 82 | 83 | states: [ 84 | State { 85 | name: "stop" 86 | PropertyChanges { target: light1; color: "red" } 87 | PropertyChanges { target: light2; color: "black" } 88 | }, 89 | State { 90 | name: "go" 91 | PropertyChanges { target: light1; color: "black" } 92 | PropertyChanges { target: light2; color: "green" } 93 | } 94 | ] 95 | ``` 96 | 97 | PropertyChanges{ target: light2; color: "black" }在這個例子中不是必要的,因為light2初始化顏色已經是黑色了。在一個狀態中,只需要描述屬性如何從它們的默認狀態改變(而不是前一個狀態的改變)。 98 | 99 | 使用鼠標區域覆蓋整個交通燈,並且綁定在點擊時切換go和stop狀態。 100 | 101 | ``` 102 | MouseArea { 103 | anchors.fill: parent 104 | onClicked: parent.state = (parent.state == "stop"? "go" : "stop") 105 | } 106 | ``` 107 | 108 | ![](http://qmlbook.org/_images/trafficlight_ui.png) 109 | 110 | 我們現在已經成功實現了交通燈的狀態切換。為了讓用戶界面看起來更加自然,我們需要使用動畫效果來增加一些過渡。一個過渡能夠被狀態的改變觸發。 111 | 112 | **注意** 113 | 114 | **可以使用一個簡單邏輯的腳本來替換QML狀態。開發人員很容易落入這種陷阱,寫的代碼更像一個JavaScript程序而不是一個QML程序。** 115 | 116 | ## 5.2.2 過渡(Transitions) 117 | 118 | 一系列的過渡能夠被加入任何元素,一個過渡由狀態的改變觸發執行。你可以使用屬性的from:和to:來定義狀態改變的指定過渡。這兩個屬性就像一個過濾器,當過濾器為true時,過渡生效。你也可以使用“*”來表示任何狀態。例如from:"*"; to:"*"表示從任一狀態到另一個任一狀態的默認值,這意味著過渡用于每個狀態的切換。 119 | 120 | 在這個例子中,我們期望從狀態“go”到“stop”轉換時實現一個顏色改變的動畫。對于從“stop”到“go”狀態的改變,我們期望保持顏色的直接改變,不使用過渡。我們使用from和to來限制過渡只在從“go”到“stop”時生效。在過渡中我們給每個燈添加兩個顏色的動畫,這個動畫將按照狀態的描述來改變屬性。 121 | 122 | ``` 123 | transitions: [ 124 | Transition { 125 | from: "stop"; to: "go" 126 | ColorAnimation { target: light1; properties: "color"; duration: 2000 } 127 | ColorAnimation { target: light2; properties: "color"; duration: 2000 } 128 | } 129 | ] 130 | ``` 131 | 132 | 你可以點擊用戶界面來改變狀態。試試點擊用戶界面,當狀態從“stop”到“go”時,你將會發現改變立刻發生了。 133 | 134 | ![](http://qmlbook.org/_images/trafficlight_transition.png) 135 | 136 | 接下來,你可以修改下這個例子,例如縮小未點亮的等來突出點亮的等。為此,你需要在狀態中添加一個屬性用來縮放,並且操作一個動畫來播放縮放屬性的過渡。另一個選擇是可以添加一個“attention”狀態,燈會出現黃色閃爍,為此你需要添加為這個過渡添加一個一秒連續的動畫來顯示黃色(使用“to”屬性來實現,一秒後變為黑色)。也許你也可以改變緩衝曲線來使這個例子更加生動。 137 | -------------------------------------------------------------------------------- /quick_starter/positioning_element.md: -------------------------------------------------------------------------------- 1 | # 定位元素(Positioning Element) 2 | 3 | 有一些QML元素被用于放置元素對象,它們被稱作定位器,QtQuick模塊提供了Row,Column,Grid,Flow用來作為定位器。你可以在下面的插圖中看到它們使用相同內容的顯示效果。 4 | 5 | **注意** 6 | 7 | **在我們詳細介紹前,我們先介紹一些相關的元素,紅色(red),藍色(blue),綠色(green),高亮(lighter)與黑暗(darker)方塊,每一個組件都包含了一個48乘48的著色區域。下面是關于RedSquare(紅色方塊)的代碼:** 8 | 9 | ``` 10 | // RedSquare.qml 11 | 12 | import QtQuick 2.0 13 | 14 | Rectangle { 15 | width: 48 16 | height: 48 17 | color: "#ea7025" 18 | border.color: Qt.lighter(color) 19 | } 20 | ``` 21 | 22 | **請注意使用了Qt.lighter(color)來指定了基于填充色的邊界高亮色。我們將會在後面的例子中使用到這些元素,希望後面的代碼能夠容易讀懂。請記住每一個矩形框的初始化大小都是48乘48像素大小。** 23 | 24 | Column(列)元素將它的子對象通過頂部對齊的列方式進行排列。spacing屬性用來設置每個元素之間的間隔大小。 25 | 26 | ![](http://qmlbook.org/_images/column.png) 27 | 28 | ``` 29 | // column.qml 30 | 31 | import QtQuick 2.0 32 | 33 | DarkSquare { 34 | id: root 35 | width: 120 36 | height: 240 37 | 38 | Column { 39 | id: column 40 | anchors.centerIn: parent 41 | spacing: 8 42 | RedSquare { } 43 | GreenSquare { width: 96 } 44 | BlueSquare { } 45 | } 46 | } 47 | 48 | // M1<< 49 | ``` 50 | 51 | Row(行)元素將它的子對象從左到右,或者從右到左依次排列,排列方式取決于layoutDirection屬性。spacing屬性用來設置每個元素之間的間隔大小。 52 | 53 | ![](http://qmlbook.org/_images/row.png) 54 | 55 | ``` 56 | // row.qml 57 | 58 | import QtQuick 2.0 59 | 60 | BrightSquare { 61 | id: root 62 | width: 400; height: 120 63 | 64 | Row { 65 | id: row 66 | anchors.centerIn: parent 67 | spacing: 20 68 | BlueSquare { } 69 | GreenSquare { } 70 | RedSquare { } 71 | } 72 | } 73 | ``` 74 | 75 | Grid(柵格)元素通過設置rows(行數)和columns(列數)將子對象排列在一個柵格中。可以只限制行數或者列數。如果沒有設置它們中的任意一個,柵格元素會自動計算子項目總數來獲得配置,例如,設置rows(行數)為3,添加了6個子項目到元素中,那麼會自動計算columns(列數)為2。屬性flow(流)與layoutDirection(布局方向)用來控制子元素的加入順序。spacing屬性用來控制所有元素之間的間隔。 76 | 77 | ![](http://qmlbook.org/_images/grid.png) 78 | 79 | ``` 80 | // grid.qml 81 | 82 | import QtQuick 2.0 83 | 84 | BrightSquare { 85 | id: root 86 | width: 160 87 | height: 160 88 | 89 | Grid { 90 | id: grid 91 | rows: 2 92 | columns: 2 93 | anchors.centerIn: parent 94 | spacing: 8 95 | RedSquare { } 96 | RedSquare { } 97 | RedSquare { } 98 | RedSquare { } 99 | } 100 | 101 | } 102 | ``` 103 | 104 | 最後一個定位器是Flow(流)。通過flow(流)屬性和layoutDirection(布局方向)屬性來控制流的方向。它能夠從頭到底的橫向布局,也可以從左到右或者從右到左進行布局。作為加入流中的子對象,它們在需要時可以被包裝成新的行或者列。為了讓一個流可以工作,必須指定一個寬度或者高度,可以通過屬性直接設定,或者通過anchor(錨定)布局設置。 105 | 106 | ![](http://qmlbook.org/_images/flow.png) 107 | 108 | ``` 109 | // flow.qml 110 | 111 | import QtQuick 2.0 112 | 113 | BrightSquare { 114 | id: root 115 | width: 160 116 | height: 160 117 | 118 | Flow { 119 | anchors.fill: parent 120 | anchors.margins: 20 121 | spacing: 20 122 | RedSquare { } 123 | BlueSquare { } 124 | GreenSquare { } 125 | } 126 | } 127 | ``` 128 | 129 | 通常Repeater(重復元素)與定位器一起使用。它的工作方式就像for循環與迭代器的模式一樣。在這個最簡單的例子中,僅僅提供了一個循環的例子。 130 | 131 | ![](http://qmlbook.org/_images/repeater.png) 132 | 133 | ``` 134 | // repeater.qml 135 | 136 | import QtQuick 2.0 137 | 138 | DarkSquare { 139 | id: root 140 | width: 252 141 | height: 252 142 | property variant colorArray: ["#00bde3", "#67c111", "#ea7025"] 143 | 144 | 145 | Grid{ 146 | anchors.fill: parent 147 | anchors.margins: 8 148 | spacing: 4 149 | Repeater { 150 | model: 16 151 | Rectangle { 152 | width: 56; height: 56 153 | property int colorIndex: Math.floor(Math.random()*3) 154 | color: root.colorArray[colorIndex] 155 | border.color: Qt.lighter(color) 156 | Text { 157 | anchors.centerIn: parent 158 | color: "#f0f0f0" 159 | text: "Cell " + index 160 | } 161 | } 162 | } 163 | } 164 | } 165 | ``` 166 | 167 | 在這個重復元素的例子中,我們使用了一些新的方法。我們使用一個顏色數組定義了一組顏色屬性。重復元素能夠創建一連串的矩形框(16個,就像模型中定義的那樣)。每一次的循環都會創建一個矩形框作為repeater的子對象。在矩形框中,我們使用了JS數學函數Math.floor(Math.random()*3)來選擇顏色。這個函數會給我們生成一個0~2的隨機數,我們使用這個數在我們的顏色數組中選擇顏色。注意之前我們說過JavaScript是QtQuick中的一部分,所以這些典型的庫函數我們都可以使用。 168 | 169 | 一個重復元素循環時有一個index(索引)屬性值。當前的循環索引(0,1,2,....15)。我們可以使用這個索引值來做一些操作,例如在我們這個例子中使用Text(文本)顯示當前索引值。 170 | 171 | **注意** 172 | 173 | **高級的大數據模型處理和使用動態代理的動態視圖會在模型與視圖(model-view)章節中講解。當有一小部分的靜態數據需要顯示時,使用重復元素是最好的方式。** 174 | -------------------------------------------------------------------------------- /shader_effect/fragement_shader.md: -------------------------------------------------------------------------------- 1 | # 片段著色器(Fragement Shader) 2 | 3 | 片段著色器調用每個需要渲染的像素。我們將開發一個紅色透鏡,它將會增加圖片的紅色通道的值。 4 | 5 | **配置場景(Setting up the scene)** 6 | 7 | 首先我們配置我們的場景,在區域中央使用一個網格顯示我們的源圖片(source image)。 8 | 9 | ``` 10 | import QtQuick 2.0 11 | 12 | Rectangle { 13 | width: 480; height: 240 14 | color: '#1e1e1e' 15 | 16 | Grid { 17 | anchors.centerIn: parent 18 | spacing: 20 19 | rows: 2; columns: 4 20 | Image { 21 | id: sourceImage 22 | width: 80; height: width 23 | source: 'assets/tulips.jpg' 24 | } 25 | } 26 | } 27 | ``` 28 | 29 | ![](http://qmlbook.org/_images/redlense1.png) 30 | 31 | **紅色著色器(A red Shader)** 32 | 33 | 下一步我們添加一個著色器,顯示一個紅色矩形框。由于我們不需要紋理,我們從頂點著色器中移除紋理。 34 | 35 | ``` 36 | vertexShader: " 37 | uniform highp mat4 qt_Matrix; 38 | attribute highp vec4 qt_Vertex; 39 | void main() { 40 | gl_Position = qt_Matrix * qt_Vertex; 41 | }" 42 | fragmentShader: " 43 | uniform lowp float qt_Opacity; 44 | void main() { 45 | gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * qt_Opacity; 46 | }" 47 | ``` 48 | 49 | 在片段著色器中,我們簡單的給gl_FragColor賦值為vec4(1.0, 0.0, 0.0, 1.0),它代表紅色, 並且不透明(alpha=1.0)。 50 | 51 | ![](http://qmlbook.org/_images/redlense2.png) 52 | 53 | **使用紋理的紅色著色器(A red shader with texture)** 54 | 55 | 現在我們想要將這個紅色應用在紋理的每個像素上。我們需要將紋理加回頂點著色器。由于我們不再在頂點著色器中做任何其它的事情,所以默認的頂點著色器已經滿足我們的要求。 56 | 57 | ``` 58 | ShaderEffect { 59 | id: effect2 60 | width: 80; height: width 61 | property variant source: sourceImage 62 | visible: root.step>1 63 | fragmentShader: " 64 | varying highp vec2 qt_TexCoord0; 65 | uniform sampler2D source; 66 | uniform lowp float qt_Opacity; 67 | void main() { 68 | gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(1.0, 0.0, 0.0, 1.0) * qt_Opacity; 69 | }" 70 | } 71 | ``` 72 | 73 | 完整的著色器重新包含我們的源圖片作為屬性,由于我們沒有特殊指定,使用默認的頂點著色器,我沒有重寫頂點著色器。 74 | 75 | 在片段著色器中,我們提取紋理片段texture2D(source,qt_TexCoord0),並且與紅色一起應用。 76 | 77 | ![](http://qmlbook.org/_images/redlense3.png) 78 | 79 | **紅色通道屬性(The red channel property)** 80 | 81 | 這樣的代碼用來修改紅色通道的值看起來不是很好,所以我們想要將這個值包含在QML這邊。我們在ShaderEffect中增加一個redChannel屬性,並在我們的片段著色器中申明一個uniform lowpfloat redChannel。這就是從一個著色器代碼中標記一個值到QML這邊的方法,非常簡單。 82 | 83 | ``` 84 | ShaderEffect { 85 | id: effect3 86 | width: 80; height: width 87 | property variant source: sourceImage 88 | property real redChannel: 0.3 89 | visible: root.step>2 90 | fragmentShader: " 91 | varying highp vec2 qt_TexCoord0; 92 | uniform sampler2D source; 93 | uniform lowp float qt_Opacity; 94 | uniform lowp float redChannel; 95 | void main() { 96 | gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(redChannel, 1.0, 1.0, 1.0) * qt_Opacity; 97 | }" 98 | } 99 | ``` 100 | 101 | 為了讓這個透鏡更真實,我們改變vec4顏色為vec4(redChannel, 1.0, 1.0, 1.0),這樣其它顏色與1.0相乘,只有紅色部分使用我們的redChannel變量。 102 | 103 | ![](http://qmlbook.org/_images/redlense4.png) 104 | 105 | **紅色通道的動畫(The red channel animated**) 106 | 107 | 由于redChannel屬性僅僅是一個正常的屬性,我們也可以像其它QML中的屬性一樣使用動畫。我們使用QML屬性在GPU上改變這個值,來影響我們的著色器,這真酷! 108 | 109 | ``` 110 | ShaderEffect { 111 | id: effect4 112 | width: 80; height: width 113 | property variant source: sourceImage 114 | property real redChannel: 0.3 115 | visible: root.step>3 116 | NumberAnimation on redChannel { 117 | from: 0.0; to: 1.0; loops: Animation.Infinite; duration: 4000 118 | } 119 | 120 | fragmentShader: " 121 | varying highp vec2 qt_TexCoord0; 122 | uniform sampler2D source; 123 | uniform lowp float qt_Opacity; 124 | uniform lowp float redChannel; 125 | void main() { 126 | gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(redChannel, 1.0, 1.0, 1.0) * qt_Opacity; 127 | }" 128 | } 129 | ``` 130 | 131 | 下面是最後的結果。 132 | 133 | ![](http://qmlbook.org/_images/redlense5.png) 134 | 135 | 在這4秒內,第二排的著色器紅色通道的值從0.0到1.0。圖片從沒有紅色信息(0.0 red)到一個正常的圖片(1.0 red)。 136 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](README.md) 4 | * [Meet Qt 5](meet_qt_5/README.md) 5 | * [序(Preface)](meet_qt_5/preface.md) 6 | * [Qt5介紹(Qt5 Introduction)](meet_qt_5/qt5qt5_introduction.md) 7 | * [Qt模組(Qt Building Blocks)](meet_qt_5/qtqt_building_blocks.md) 8 | * [Qt專案(Qt Project)](meet_qt_5/qtqt_project.md) 9 | * [Get Start](get_start/README.md) 10 | * [安裝Qt5軟件工具包(Installing Qt 5 SDK)](get_start/qt5installing_qt_5_sdk.md) 11 | * [你好世界(Hello World)](get_start/hello_world.md) 12 | * [應用程序類型(Application Types)](get_start/application_types.md) 13 | * [總結( Summary)](get_start/summary.md) 14 | * [Qt Creator IDE](qt_creator_ide/README.md) 15 | * [用戶界面(The User Interface)](qt_creator_ide/the_user_interface.md) 16 | * [注冊你的Qt工具箱(Registering your Qt Kit)](qt_creator_ide/qtregistering_your_qt_kit.md) 17 | * [項目管理(Managing Projects)](qt_creator_ide/managing_projects.md) 18 | * [使用編輯器(Using the Editor)](qt_creator_ide/using_the_editor.md) 19 | * [定位器(Locator)](qt_creator_ide/locator.md) 20 | * [調試(Debugging)](qt_creator_ide/debugging.md) 21 | * [快捷鍵(Shortcuts)](qt_creator_ide/shortcuts.md) 22 | * [Quick Starter](quick_starter/README.md) 23 | * [QML語法(QML Syntax)](quick_starter/qmlqml_syntax.md) 24 | * [基本元素(Basic Elements)](quick_starter/basic_elements.md) 25 | * [組件(Compontents)](quick_starter/compontents.md) 26 | * [簡單的轉換(Simple Transformations)](quick_starter/simple_transformations.md) 27 | * [定位元素(Positioning Element)](quick_starter/positioning_element.md) 28 | * [布局元素(Layout Items)](quick_starter/layout_items.md) 29 | * [輸入元素(Input Element)](quick_starter/input_element.md) 30 | * [ 高級用法(Advanced Techniques)](quick_starter/advanced_techniques.md) 31 | * [Fluid Elements](fluid_elements/README.md) 32 | * [動畫(Animations)](fluid_elements/animations.md) 33 | * [狀態與過渡(States and Transitions)](fluid_elements/states_and_transitions.md) 34 | * [高級用法(Advanced Techniques)](fluid_elements/advanced_techniques.md) 35 | * [Model-View-Delegate](model-view-delegate/README.md) 36 | * [概念(Concept)](model-view-delegate/concept.md) 37 | * [基礎模型(Basic Model)](model-view-delegate/basic_model.md) 38 | * [動態視圖(Dynamic Views)](model-view-delegate/dynamic_views.md) 39 | * [代理(Delegate)](model-view-delegate/delegate.md) 40 | * [高級用法(Advanced Techniques)](model-view-delegate/advanced_techniques.md) 41 | * [總結(Summary)](model-view-delegate/summary.md) 42 | * [Canvas Element](canvas_element/README.md) 43 | * [便捷的接口(Convenient API)](canvas_element/convenient_api.md) 44 | * [漸變(Gradients)](canvas_element/gradients.md) 45 | * [陰影(Shadows)](canvas_element/shadows.md) 46 | * [圖片(Images)](canvas_element/images.md) 47 | * [轉換(Transformation)](canvas_element/transformation.md) 48 | * [組合模式(Composition Mode)](canvas_element/composition_mode.md) 49 | * [像素緩衝(Pixels Buffer)](canvas_element/pixels_buffer.md) 50 | * [畫布繪制(Canvas Paint)](canvas_element/canvas_paint.md) 51 | * [HTML5畫布移植(Porting from HTML5 Canvas)](canvas_element/html5porting_from_html5_canvas.md) 52 | * [Particle Simulations](particle_simulations/README.md) 53 | * [概念(Concept)](particle_simulations/concept.md) 54 | * [簡單的模擬(Simple Simulation)](particle_simulations/simple_simulation.md) 55 | * [粒子參數(Particle Parameters)](particle_simulations/particle_parameters.md) 56 | * [粒子方向(Directed Particle)](particle_simulations/directed_particle.md) 57 | * [粒子畫筆(Particle Painter)](particle_simulations/particle_painter.md) 58 | * [粒子控制(Affecting Particles)](particle_simulations/affecting_particles.md) 59 | * [粒子組(Particle Group)](particle_simulations/particle_group.md) 60 | * [總結(Summary)](particle_simulations/summary.md) 61 | * [Shader Effect](shader_effect/README.md) 62 | * [OpenGL著色器(OpenGL Shader)](shader_effect/openglopengl_shader.md) 63 | * [著色器元素(Shader Elements)](shader_effect/shader_elements.md) 64 | * [片段著色器(Fragement Shader)](shader_effect/fragement_shader.md) 65 | * [波浪效果(Wave Effect)](shader_effect/wave_effect.md) 66 | * [頂點著色器(Vertex Shader)](shader_effect/vertex_shader.md) 67 | * [劇幕效果(Curtain Effect)](shader_effect/curtain_effect.md) 68 | * [Qt圖像效果庫(Qt GraphicsEffect Library)](shader_effect/qtqt_graphicseffect_library.md) 69 | * [Multimedia](multimedia/README.md) 70 | * [媒體播放(Playing Media)](multimedia/playing_media.md) 71 | * [聲音效果(Sounds Effects)](multimedia/sounds_effects.md) 72 | * [視頻流(Video Streams)](multimedia/video_streams.md) 73 | * [捕捉圖像(Capturing Images)](multimedia/capturing_images.md) 74 | * [高級用法(Advanced Techniques)](multimedia/advanced_techniques.md) 75 | * [總結(Summary)](multimedia/summary.md) 76 | * [Networking](networking/README.md) 77 | * [通過HTTP服務UI(Serving UI via HTTP)](networking/httpuiserving_ui_via_http.md) 78 | * [模板(Templating)](networking/templating.md) 79 | * [HTTP請求(HTTP Requests)](networking/httphttp_requests.md) 80 | * [本地文件(Local files)](networking/local_files.md) 81 | * [REST接口(REST API)](networking/restrest_api.md) 82 | * [使用開放授權登陸驗證(Authentication using OAuth)](networking/authentication_using_oauth.md) 83 | * [Engine IO](networking/engine_io.md) 84 | * [Web Sockets](networking/web_sockets.md) 85 | * [總結(Summary)](networking/summary.md) 86 | 87 | -------------------------------------------------------------------------------- /networking/httphttp_requests.md: -------------------------------------------------------------------------------- 1 | # HTTP請求(HTTP Requests) 2 | 3 | 從c++方面來看,Qt中完成http請求通常是使用QNetworkRequest和QNetworkReply,然後使用Qt/C++將響應推送到集成的QML。所以我們嘗試使用QtQuick的工具給我們的網絡信息尾部封裝了小段信息,然後推送這些信息。為此我們使用一個幫助對象來構造http請求,和循環響應。它使用java腳本的XMLHttpRequest對象的格式。 4 | 5 | XMLHttpRequest對象允許用戶注冊一個響應操作函數和一個鏈接。一個請求能夠使用http動作來發送(如get,post,put,delete,等等)。當響應到達時,會調用注冊的操作函數。操作函數會被調用多次。每次調用請求的狀態都已經改變(例如信息頭部已接收,或者響應完成)。 6 | 7 | 下面是一個簡短的例子: 8 | 9 | ``` 10 | function request() { 11 | var xhr = new XMLHttpRequest(); 12 | xhr.onreadystatechange = function() { 13 | if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) { 14 | print('HEADERS_RECEIVED'); 15 | } else if(xhr.readyState === XMLHttpRequest.DONE) { 16 | print('DONE'); 17 | } 18 | } 19 | xhr.open("GET", "http://example.com"); 20 | xhr.send(); 21 | } 22 | ``` 23 | 24 | 從一個響應中你可以獲取XML格式的數據或者是原始文本。可以遍歷XML結果但是通常使用原始文本來匹配JSON格式響應。使用JSON.parse(text)可以JSON文檔將轉換為JS對象使用。 25 | 26 | ``` 27 | ... 28 | } else if(xhr.readyState === XMLHttpRequest.DONE) { 29 | var object = JSON.parse(xhr.responseText.toString()); 30 | print(JSON.stringify(object, null, 2)); 31 | } 32 | ``` 33 | 34 | 在響應操作中,我們訪問原始響應文本並且將它轉換為一個javascript對象。JSON對象是一個可以使用的JS對象(在javascript中,一個對象可以是對象或者一個數組)。 35 | 36 | **注意** 37 | 38 | **toString()轉換似乎讓代碼更加穩定。在不使用顯式的轉換下我有幾次都解析錯誤。不確定是什麼問題引起的。** 39 | 40 | ## 11.3.1 Flickr調用(Flickr Call) 41 | 42 | 讓我們看看更加真實的例子。一個典型的例子是使用網絡相冊服務來取得公共訂閱中新上傳的圖片。我們可以使用[http://api.flicker.com/services/feeds/photos_public.gne](http://api.flicker.com/services/feeds/photos_public.gne)鏈接。不幸的是它默認返回XML流格式的數據,在qml中可以很方便的使用XmlListModel來解析。為了達到只關注JSON數據的目的,我們需要在請求中附加一些參數可以得到JSON響應:[http://api.flickr.com/services/feeds/photo_public.gne?format=json&nojsoncallback=1](http://api.flickr.com/services/feeds/photo_public.gne?format=json&nojsoncallback=1)。這將會返回一個沒有JSON回調的JSON響應。 43 | 44 | **注意** 45 | **一個JSON回調將JSON響應包裝在一個函數調用中。這是一個HTML編程中的快捷方式,使用腳本標記來創建一個JSON請求。響應將觸發本地定義的回調函數。在QML中沒有JSON回調的工作機制。** 46 | 47 | 使用curl來查看響應: 48 | 49 | ``` 50 | curl "http://api.flickr.com/services/feeds/photos_public.gne?format=json&nojsoncallback=1&tags=munich" 51 | ``` 52 | 53 | 響應如下: 54 | 55 | ``` 56 | { 57 | "title": "Recent Uploads tagged munich", 58 | ... 59 | "items": [ 60 | { 61 | "title": "Candle lit dinner in Munich", 62 | "media": {"m":"http://farm8.staticflickr.com/7313/11444882743_2f5f87169f_m.jpg"}, 63 | ... 64 | },{ 65 | "title": "Munich after sunset: a train full of \"must haves\" =", 66 | "media": {"m":"http://farm8.staticflickr.com/7394/11443414206_a462c80e83_m.jpg"}, 67 | ... 68 | } 69 | ] 70 | ... 71 | } 72 | ``` 73 | 74 | JSON文檔已經定義了結構體。一個對象包含一個標題和子項的屬性。標題是一個字符串,子項是一組對象。當轉換文本為一個JSON文檔後,你可以單獨訪問這些條目,它們都是可用的JS對象或者結構體數組。 75 | 76 | ``` 77 | // JS code 78 | obj = JSON.parse(response); 79 | print(obj.title) // => "Recent Uploads tagged munich" 80 | for(var i=0; i: 22 | width: 120; height: 240 23 | 24 | // color property 25 | color: "#D8D8D8" 26 | 27 | // Declare a nested element (child of root) 28 | Image { 29 | id: rocket 30 | 31 | // reference the parent 32 | x: (parent.width - width)/2; y: 40 33 | 34 | source: 'assets/rocket.png' 35 | } 36 | 37 | // Another child of root 38 | Text { 39 | // un-named element 40 | 41 | // reference element by id 42 | y: rocket.y + rocket.height + 20 43 | 44 | // reference root element 45 | width: root.width 46 | 47 | horizontalAlignment: Text.AlignHCenter 48 | text: 'Rocket' 49 | } 50 | } 51 | ``` 52 | 53 | * import聲明導入了一個指定的模塊版本。一般來說會導入QtQuick2.0來作為初始元素的引用。 54 | 55 | * 使用//可以單行注釋,使用/**/可以多行注釋,就像C/C++和JavaScript一樣。 56 | 57 | * 每一個QML文件都需要一個根元素,就像HTML一樣。 58 | 59 | * 一個元素使用它的類型聲明,然後使用{}進行包含。 60 | 61 | * 元素擁有屬性,他們按照name:value的格式來賦值。 62 | 63 | * 任何在QML文檔中的元素都可以使用它們的id進行訪問(id是一個任意的標識符)。 64 | 65 | * 元素可以嵌套,這意味著一個父元素可以擁有多個子元素。子元素可以通過訪問parent關鍵字來訪問它們的父元素。 66 | 67 | **建議** 68 | 69 | **你會經常使用id或者關鍵字parent來訪問你的父對象。有一個比較好的方法是命名你的根元素對象id為root(id:root),這樣就不用去思考你的QML文檔中的根元素應該用什麼方式命名了。** 70 | 71 | **提示** 72 | 73 | **你可以在你的操作系統命令行模式下使用QtQuick運行環境來運行這個例子,比如像下面這樣:** 74 | 75 | ``` 76 | $ $QTDIR/bin/qmlscene rectangle.qml 77 | ``` 78 | 79 | 將$QTDIR替換為你的Qt的安裝路徑。qmlscene會執行Qt Quick運行環境初始化,並且解釋這個QML文件。 80 | 81 | 在Qt Creator中你可以打開對應的項目文件然後運行rectangle.qml文檔。 82 | 83 | ## 4.1.1 屬性(Properties) 84 | 85 | 元素使用他們的元素類型名進行聲明,使用它們的屬性或者創建自定義屬性來定義。一個屬性對應一個值,例如 width:100,text: 'Greeting', color: '#FF0000'。一個屬性有一個類型定義並且需要一個初始值。 86 | 87 | ``` 88 | Text { 89 | // (1) identifier 90 | id: thisLabel 91 | 92 | // (2) set x- and y-position 93 | x: 24; y: 16 94 | 95 | // (3) bind height to 2 * width 96 | height: 2 * width 97 | 98 | // (4) custom property 99 | property int times: 24 100 | 101 | // (5) property alias 102 | property alias anotherTimes: thisLabel.times 103 | 104 | // (6) set text appended by value 105 | text: "Greetings " + times 106 | 107 | // (7) font is a grouped property 108 | font.family: "Ubuntu" 109 | font.pixelSize: 24 110 | 111 | // (8) KeyNavigation is an attached property 112 | KeyNavigation.tab: otherLabel 113 | 114 | // (9) signal handler for property changes 115 | onHeightChanged: console.log('height:', height) 116 | 117 | // focus is neeed to receive key events 118 | focus: true 119 | 120 | // change color based on focus value 121 | color: focus?"red":"black" 122 | } 123 | ``` 124 | 125 | 讓我們來看看不同屬性的特點: 126 | 127 | 1. id是一個非常特殊的屬性值,它在一個QML文件中被用來引用元素。id不是一個字符串,而是一個標識符和QML語法的一部分。一個id在一個QML文檔中是唯一的,並且不能被設置為其它值,也無法被查詢(它的行為更像C++世界裡的指針)。 128 | 129 | 2. 一個屬性能夠設置一個值,這個值依賴于它的類型。如果沒有對一個屬性賦值,那麼它將會被初始化為一個默認值。你可以查看特定的元素的文檔來獲得這些初始值的信息。 130 | 131 | 3. 一個屬性能夠依賴一個或多個其它的屬性,這種操作稱作屬性綁定。當它依賴的屬性改變時,它的值也會更新。這就像訂了一個協議,在這個例子中height始終是width的兩倍。 132 | 133 | 4. 添加自己定義的屬性需要使用property修飾符,然後跟上類型,名字和可選擇的初始化值(property : )。如果沒有初始值將會給定一個系統初始值作為初始值。**注意如果屬性名與已定義的默認屬性名不重復,使用default關鍵字你可以將一個屬性定義為默認屬性。這在你添加子元素時用得著,如果他們是可視化的元素,子元素會自動的添加默認屬性的子類型鏈表(children property list)**。 134 | 135 | 5. 另一個重要的聲明屬性的方法是使用alias關鍵字(property alias : )。alias關鍵字允許我們轉發一個屬性或者轉發一個屬性對象自身到另一個作用域。我們將在後面定義組件導出內部屬性或者引用根級元素id會使用到這個技術。一個屬性別名不需要類型,它使用引用的屬性類型或者對象類型。 136 | 137 | 6. text屬性依賴于自定義的timers(int整型數據類型)屬性。int整型數據會自動的轉換為string字符串類型數據。這樣的表達方式本身也是另一種屬性綁定的例子,文本結果會在times屬性每次改變時刷新。 138 | 139 | 7. 一些屬性是按組分配的屬性。當一個屬性需要結構化並且相關的屬性需要聯系在一起時,我們可以這樣使用它。另一個組屬性的編碼方式是 font{family: "UBuntu"; pixelSize: 24 }。 140 | 141 | 8. 一些屬性是元素自身的附加屬性。這樣做是為了全局的相關元素在應用程序中只出現一次(例如鍵盤輸入)。編碼方式.: 。 142 | 143 | 9. 對于每個元素你都可以提供一個信號操作。這個操作在屬性值改變時被調用。例如這裡我們完成了當height(高度)改變時會使用控制台輸出一個信息。 144 | 145 | **警告** 146 | 147 | **一個元素id應該只在當前文檔中被引用。QML提供了動態作用域的機制,後加載的文檔會覆蓋之前加載文檔的元素id號,這樣就可以引用已加載並且沒有被覆蓋的元素id,這有點類似創建全局變量。但不幸的是這樣的代碼閱讀性很差。目前這個還沒有辦法解決這個問題,所以你使用這個機制的時候最好仔細一些甚至不要使用這種機制。如果你想向文檔外提供元素的調用,你可以在根元素上使用屬性導出的方式來提供。** 148 | 149 | ## 4.1.2 腳本(Scripting) 150 | 151 | QML與JavaScript是最好的配合。在JavaScrpit的章節中我們將會更加詳細的介紹這種關系,現在我們只需要了解這種關系就可以了。 152 | 153 | ``` 154 | Text { 155 | id: label 156 | 157 | x: 24; y: 24 158 | 159 | // custom counter property for space presses 160 | property int spacePresses: 0 161 | 162 | text: "Space pressed: " + spacePresses + " times" 163 | 164 | // (1) handler for text changes 165 | onTextChanged: console.log("text changed to:", text) 166 | 167 | // need focus to receive key events 168 | focus: true 169 | 170 | // (2) handler with some JS 171 | Keys.onSpacePressed: { 172 | increment() 173 | } 174 | 175 | // clear the text on escape 176 | Keys.onEscapePressed: { 177 | label.text = '' 178 | } 179 | 180 | // (3) a JS function 181 | function increment() { 182 | spacePresses = spacePresses + 1 183 | } 184 | } 185 | ``` 186 | 187 | 1. 文本改變操作onTextChanged會將每次空格鍵按下導致的文本改變輸出到控制台。 188 | 189 | 2. 當文本元素接收到空格鍵操作(用戶在鍵盤上點擊空格鍵),會調用JavaScript函數increment()。 190 | 191 | 3. 定義一個JavaScript函數使用這種格式function (){....},在這個例子中是增加spacePressed的計數。每次spacePressed的增加都會導致它綁定的屬性更新。 192 | 193 | **注意** 194 | 195 | **QML的:(屬性綁定)與JavaScript的=(賦值)是不同的。綁定是一個協議,並且存在于整個生命週期。然而JavaScript賦值(=)只會產生一次效果。當一個新的綁定生效或者使用JavaScript賦值給屬性時,綁定的生命週期就會結束。例如一個按鍵的操作設置文本屬性為一個空的字符串將會銷毀我們的增值顯示:** 196 | 197 | ``` 198 | Keys.onEscapePressed: { 199 | label.text = '' 200 | } 201 | ``` 202 | 203 | **在點擊取消(ESC)後,再次點擊空格鍵(space-bar)將不會更新我們的顯示,之前的text屬性綁定(text: "Space pressed:" + spacePresses + "times")被銷毀。** 204 | 205 | **當你對改變屬性的策略有衝突時(文本的改變基于一個增值的綁定並且可以被JavaScript賦值清零),類似于這個例子,你最好不要使用綁定屬性。你需要使用賦值的方式來改變屬性,屬性綁定會在賦值操作後被銷毀(銷毀協議!)。** 206 | 207 | -------------------------------------------------------------------------------- /quick_starter/basic_elements.md: -------------------------------------------------------------------------------- 1 | # 基本元素(Basic Elements) 2 | 3 | 元素可以被分為可視化元素與非可視化元素。一個可視化元素(例如矩形框Rectangle)有著幾何形狀並且可以在屏幕上顯示。一個非可視化元素(例如計時器Timer)提供了常用的功能,通常用于操作可視化元素。 4 | 5 | 現在我們將專注于幾個基礎的可視化元素,例如Item(基礎元素對象),Rectangle(矩形框),Text(文本),Image(圖像)和MouseArea(鼠標區域)。 6 | 7 | ## 4.2.1 基礎元素對象(Item Element) 8 | 9 | Item(基礎元素對象)是所有可視化元素的基礎對象,所有其它的可視化元素都繼承自Item。它自身不會有任何繪制操作,但是定義了所有可視化元素共有的屬性: 10 | 11 | | Group(分組) | Properties(屬性) | 12 | | -- | -- | 13 | | Geometry(幾何屬性) | x,y(坐標)定義了元素左上角的位置,width,height(長和寬)定義元素的顯示範圍,z(堆疊次序)定義元素之間的重疊順序。 | 14 | | Layout handling(布局操作)| anchors(錨定),包括左(left),右(right),上(top),下(bottom),水平與垂直居中(vertical center,horizontal center),與margins(間距)一起定義了元素與其它元素之間的位置關系。 | 15 | | Key handlikng(按鍵操作) | 附加屬性key(按鍵)和keyNavigation(按鍵定位)屬性來控制按鍵操作,處理輸入焦點(focus)可用操作。 | 16 | | Transformation(轉換) | 縮放(scale)和rotate(旋轉)轉換,通用的x,y,z屬性列表轉換(transform),旋轉基點設置(transformOrigin)。 | 17 | | Visual(可視化) | 不透明度(opacity)控制透明度,visible(是否可見)控制元素是否顯示,clip(裁剪)用來限制元素邊界的繪制,smooth(平滑)用來提高渲染質量。 | 18 | | State definition(狀態定義) | states(狀態列表屬性)提供了元素當前所支持的狀態列表,當前屬性的改變也可以使用transitions(轉變)屬性列表來定義狀態轉變動畫。 | 19 | 20 | 為了更好的理解不同的屬性,我們將會在這章中盡量的介紹這些元素的顯示效果。請記住這些基本的屬性在所有可視化元素中都是可以使用的,並且在這些元素中的工作方式都是相同的。 21 | 22 | **注意** 23 | 24 | **Item(基本元素對象)通常被用來作為其它元素的容器使用,類似HTML語言中的div元素(div element)。** 25 | 26 | ## 4.2.2 矩形框元素(Rectangle Element) 27 | 28 | Rectangle(矩形框)是基本元素對象的一個擴展,增加了一個顏色來填充它。它還支持邊界的定義,使用border.color(邊界顏色),border.width(邊界寬度)來自定義邊界。你可以使用radius(半徑)屬性來創建一個圓角矩形。 29 | 30 | ``` 31 | Rectangle { 32 | id: rect1 33 | x: 12; y: 12 34 | width: 76; height: 96 35 | color: "lightsteelblue" 36 | } 37 | Rectangle { 38 | id: rect2 39 | x: 112; y: 12 40 | width: 76; height: 96 41 | border.color: "lightsteelblue" 42 | border.width: 4 43 | radius: 8 44 | } 45 | ``` 46 | 47 | ![](http://qmlbook.org/_images/rectangle2.png) 48 | 49 | **注意** 50 | 51 | **顏色的命名是來自SVG顏色的名稱(查看[http://www.w3.org/TR/css3-color/#svg-color]( http://www.w3.org/TR/css3-color/#svg-color)可以獲取更多的顏色名稱)。你也可以使用其它的方法來指定顏色,比如RGB字符串('#FF4444'),或者一個顏色名字(例如'white')。** 52 | 53 | 此外,填充的顏色與矩形的邊框也支持自定義的漸變色。 54 | 55 | ``` 56 | Rectangle { 57 | id: rect1 58 | x: 12; y: 12 59 | width: 176; height: 96 60 | gradient: Gradient { 61 | GradientStop { position: 0.0; color: "lightsteelblue" } 62 | GradientStop { position: 1.0; color: "slategray" } 63 | } 64 | border.color: "slategray" 65 | } 66 | ``` 67 | 68 | ![](http://qmlbook.org/_images/rectangle3.png) 69 | 70 | 一個漸變色是由一系列的梯度值定義的。每一個值定義了一個位置與顏色。位置標記了y軸上的位置(0 = 頂,1 = 底)。GradientStop(傾斜點)的顏色標記了顏色的位置。 71 | 72 | **注意** 73 | 74 | **一個矩形框如果沒有width/height(寬度與高度)將不可見。如果你有幾個相互關聯width/height(寬度與高度)的矩形框,在你組合邏輯中出了錯後可能就會發生矩形框不可見,請注意這一點。** 75 | 76 | **注意** 77 | 78 | **這個函數無法創建一個梯形,最好使用一個已有的圖像來創建梯形。有一種可能是在旋轉梯形時,旋轉的矩形幾何結構不會發生改變,但是這會導致幾何元素相同的可見區域的混淆。從作者的觀點來看類似的情況下最好使用設計好的梯形圖形來完成繪制。** 79 | 80 | ## 4.2.3 文本元素(Text Element) 81 | 82 | 顯示文本你需要使用Text元素(Text Element)。它最值得注意的屬性時字符串類型的text屬性。這個元素會使用給出的text(文本)與font(字體)來計算初始化的寬度與高度。可以使用字體屬性組來(font property group)來改變當前的字體,例如font.family,font.pixelSize,等等。改變文本的顏色值只需要改變顏色屬性就可以了。 83 | 84 | ``` 85 | Text { 86 | text: "The quick brown fox" 87 | color: "#303030" 88 | font.family: "Ubuntu" 89 | font.pixelSize: 28 90 | } 91 | ``` 92 | 93 | ![](http://qmlbook.org/_images/text.png) 94 | 95 | 文本可以使用horizontalAlignment與verticalAlignment屬性來設置它的對齊效果。為了提高文本的渲染效果,你可以使用style和styleColor屬性來配置文字的外框效果,浮雕效果或者凹陷效果。對于過長的文本,你可能需要使用省略號來表示,例如A very ... long text,你可以使用elide屬性來完成這個操作。elide屬性允許你設置文本左邊,右邊或者中間的省略位置。如果你不想'....'省略號出現,並且希望使用文字換行的方式顯示所有的文本,你可以使用wrapMode屬性(這個屬性只在明確設置了寬度後才生效): 96 | 97 | ``` 98 | Text { 99 | width: 40; height: 120 100 | text: 'A very long text' 101 | // '...' shall appear in the middle 102 | elide: Text.ElideMiddle 103 | // red sunken text styling 104 | style: Text.Sunken 105 | styleColor: '#FF4444' 106 | // align text to the top 107 | verticalAlignment: Text.AlignTop 108 | // only sensible when no elide mode 109 | // wrapMode: Text.WordWrap 110 | } 111 | ``` 112 | 113 | 一個text元素(text element)只顯示的文本,它不會渲染任何背景修飾。除了顯示的文本,text元素背景是透明的。為一個文本元素提供背景是你自己需要考慮的問題。 114 | 115 | **注意** 116 | 117 | **知道一個文本元素(Text Element)的初始寬度與高度是依賴于文本字符串和設置的字體這一點很重要。一個沒有設置寬度或者文本的文本元素(Text Element)將不可見,默認的初始寬度是0。** 118 | 119 | **注意** 120 | 121 | **通常你想要對文本元素布局時,你需要區分文本在文本元素內部的邊界對齊和由元素邊界自動對齊。前一種情況你需要使用horizontalAlignment和verticalAlignment屬性來完成,後一種情況你需要操作元素的幾何形狀或者使用anchors(錨定)來完成。** 122 | 123 | ## 4.2.4 圖像元素(Image Element) 124 | 125 | 126 | 一個圖像元素(Image Element)能夠顯示不同格式的圖像(例如PNG,JPG,GIF,BMP)。想要知道更加詳細的圖像格式支持信息,可以查看Qt的相關文檔。source屬性(source property)提供了圖像文件的鏈接信息,fillMode(文件模式)屬性能夠控制元素對象的大小調整行為。 127 | 128 | ``` 129 | Image { 130 | x: 12; y: 12 131 | // width: 48 132 | // height: 118 133 | source: "assets/rocket.png" 134 | } 135 | Image { 136 | x: 112; y: 12 137 | width: 48 138 | height: 118/2 139 | source: "assets/rocket.png" 140 | fillMode: Image.PreserveAspectCrop 141 | clip: true 142 | } 143 | ``` 144 | 145 | ![](http://qmlbook.org/_images/image.png) 146 | 147 | **注意** 148 | 149 | **一個URL可以是使用'/'語法的本地路徑("./images/home.png")或者一個網絡鏈接("[http://example.org/home.png](http://example.org/home.png)")。** 150 | 151 | **注意** 152 | 153 | **圖像元素(Image element)使用PreserveAspectCrop可以避免裁剪圖像數據被渲染到圖像邊界外。默認情況下裁剪是被禁用的(clip:false)。你需要打開裁剪(clip:true)來約束邊界矩形的繪制。這對任何可視化元素都是有效的。** 154 | 155 | **建議** 156 | 157 | **使用QQmlImageProvider你可以通過C++代碼來創建自己的圖像提供器,這允許你動態創建圖像並且使用線程加載。** 158 | 159 | ## 4.2.5 鼠標區域元素(MouseArea Element) 160 | 161 | 162 | 為了與不同的元素交互,你通常需要使用MouseArea(鼠標區域)元素。這是一個矩形的非可視化元素對象,你可以通過它來捕捉鼠標事件。當用戶與可視化端口交互時,mouseArea通常被用來與可視化元素對象一起執行命令。 163 | 164 | ``` 165 | Rectangle { 166 | id: rect1 167 | x: 12; y: 12 168 | width: 76; height: 96 169 | color: "lightsteelblue" 170 | MouseArea { 171 | id: area 172 | width: parent.width 173 | height: parent.height 174 | onClicked: rect2.visible = !rect2.visible 175 | } 176 | } 177 | 178 | Rectangle { 179 | id: rect2 180 | x: 112; y: 12 181 | width: 76; height: 96 182 | border.color: "lightsteelblue" 183 | border.width: 4 184 | radius: 8 185 | } 186 | ``` 187 | 188 | ![](http://qmlbook.org/_images/mousearea1.png) 189 | 190 | ![](http://qmlbook.org/_images/mousearea2.png) 191 | 192 | **注意** 193 | 194 | **這是QtQuick中非常重要的概念,輸入處理與可視化顯示分開。這樣你的交互區域可以比你顯示的區域大很多。** 195 | -------------------------------------------------------------------------------- /canvas_element/html5porting_from_html5_canvas.md: -------------------------------------------------------------------------------- 1 | # HTML5畫布移植(Porting from HTML5 Canvas) 2 | 3 | * [https://developer.mozilla.org/en/Canvas_tutorial/Transformations](https://developer.mozilla.org/en/Canvas_tutorial/Transformations) 4 | 5 | * [http://en.wikipedia.org/wiki/Spirograph](http://en.wikipedia.org/wiki/Spirograph) 6 | 7 | 移植一個HTML5畫布圖像到QML畫布非常簡單。在成百上千的例子中,我們選擇了一個來移植。 8 | 9 | **螺旋圖形(Spiro Graph)** 10 | 11 | 我們使用一個來自Mozila項目的螺旋圖形例子來作為我們的基礎示例。原始的HTML5代碼被作為畫布教程發布。 12 | 13 | 下面是我們需要修改的代碼: 14 | 15 | * Qt Quick要求定義變量使用,所以我們需要添加var的定義: 16 | ``` 17 | for (var i=0;i<3;i++) { 18 | ... 19 | } 20 | ``` 21 | 22 | * 修改繪制方法接收Context2D對象: 23 | ``` 24 | function draw(ctx) { 25 | ... 26 | } 27 | ``` 28 | 29 | * 由于不同的大小,我們需要對每個螺旋適配轉換: 30 | ``` 31 | ctx.translate(20+j*50,20+i*50); 32 | ``` 33 | 34 | 最後我們實現onPaint操作。在onPaint中我們請求一個context,並且調用我們的繪制方法。 35 | 36 | ``` 37 | onPaint: { 38 | var ctx = getContext("2d"); 39 | draw(ctx); 40 | } 41 | ``` 42 | 43 | 下面這個結果就是我們使用QML畫布移植的螺旋圖形。 44 | 45 | ![](http://qmlbook.org/_images/spirograph.png) 46 | 47 | **發光線(Glowing Lines)** 48 | 49 | 下面有一個更加復雜的移植來自W3C組織。[原始的發光線](http://www.w3.org/TR/2dcontext/#examples)有些很不錯的地方,這使得移植更加具有挑戰性。 50 | 51 | ![](http://qmlbook.org/_images/html_glowlines.png) 52 | 53 | ``` 54 | 55 | 56 | 57 | Pretty Glowing Lines 58 | 59 | 60 | 61 | 62 | 121 | 122 | 123 | ``` 124 | 125 | 在HTML5中,context2D對象可以隨意在畫布上繪制。在QML中,只能在onPaint操作中繪制。在HTML5中,通常調用setInterval使用計時器觸發線段的繪制或者清屏。由于QML中不同的操作方法,僅僅只是調用這些函數不能實現我們想要的結果,因為我們需要通過onPaint操作來實現。我們也需要修改顏色的格式。讓我們看看需要改變哪些東西。 126 | 127 | 修改從畫布元素開始。為了簡單,我們使用畫布元素(Canvas)作為我們QML文件的根元素。 128 | 129 | ``` 130 | import QtQuick 2.0 131 | 132 | Canvas { 133 | id: canvas 134 | width: 800; height: 450 135 | 136 | ... 137 | } 138 | ``` 139 | 140 | 代替直接調用的setInterval函數,我們使用兩個計時器來請求重新繪制。一個計時器觸發間隔較短,允許我們可以執行一些代碼。我們無法告訴繪制函數哪個操作是我想觸發的,我們為每個操作定義一個布爾標識,當重新繪制請求時,我們請求一個操作並且觸發它。 141 | 142 | 下面是線段繪制的代碼,清屏操作類似。 143 | 144 | ``` 145 | ... 146 | property bool requestLine: false 147 | 148 | Timer { 149 | id: lineTimer 150 | interval: 40 151 | repeat: true 152 | triggeredOnStart: true 153 | onTriggered: { 154 | canvas.requestLine = true 155 | canvas.requestPaint() 156 | } 157 | } 158 | 159 | Component.onCompleted: { 160 | lineTimer.start() 161 | } 162 | ... 163 | ``` 164 | 165 | 現在我們已經有了告訴onPaint操作中我們需要執行哪個操作的指示。當我們進入onPaint處理每個繪制請求時,我們需要提取畫布元素中的初始化變量。 166 | 167 | ``` 168 | Canvas { 169 | ... 170 | property real hue: 0 171 | property real lastX: width * Math.random(); 172 | property real lastY: height * Math.random(); 173 | ... 174 | } 175 | ``` 176 | 177 | 現在我們的繪制函數應該像這樣: 178 | 179 | ``` 180 | onPaint: { 181 | var context = getContext('2d') 182 | if(requestLine) { 183 | line(context) 184 | requestLine = false 185 | } 186 | if(requestBlank) { 187 | blank(context) 188 | requestBlank = false 189 | } 190 | } 191 | ``` 192 | 193 | 線段繪制函數提取畫布作為一個參數。 194 | 195 | ``` 196 | function line(context) { 197 | context.save(); 198 | context.translate(canvas.width/2, canvas.height/2); 199 | context.scale(0.9, 0.9); 200 | context.translate(-canvas.width/2, -canvas.height/2); 201 | context.beginPath(); 202 | context.lineWidth = 5 + Math.random() * 10; 203 | context.moveTo(lastX, lastY); 204 | lastX = canvas.width * Math.random(); 205 | lastY = canvas.height * Math.random(); 206 | context.bezierCurveTo(canvas.width * Math.random(), 207 | canvas.height * Math.random(), 208 | canvas.width * Math.random(), 209 | canvas.height * Math.random(), 210 | lastX, lastY); 211 | 212 | hue += Math.random()*0.1 213 | if(hue > 1.0) { 214 | hue -= 1 215 | } 216 | context.strokeStyle = Qt.hsla(hue, 0.5, 0.5, 1.0); 217 | // context.shadowColor = 'white'; 218 | // context.shadowBlur = 10; 219 | context.stroke(); 220 | context.restore(); 221 | } 222 | ``` 223 | 224 | 最大的變化是使用QML的Qt.rgba()和Qt.hsla()。在QML中需要把變量值適配在0.0到1.0之間。 225 | 226 | 同樣應用在清屏函數中。 227 | 228 | ``` 229 | function blank(context) { 230 | context.fillStyle = Qt.rgba(0,0,0,0.1) 231 | context.fillRect(0, 0, canvas.width, canvas.height); 232 | } 233 | ``` 234 | 235 | 下面是最終結果(目前沒有陰影)類似下面這樣。 236 | 237 | ![](http://qmlbook.org/_images/glowlines.png) 238 | 239 | 查看下面的鏈接獲得更多的信息: 240 | 241 | * [W3C HTML Canvas 2D Context Specification](http://www.w3.org/TR/2dcontext/) 242 | 243 | * [Mozilla Canvas Documentation](https://developer.mozilla.org/en/HTML/Canvas) 244 | 245 | * [HTML5 Canvas Tutorial](http://www.html5canvastutorials.com/) 246 | 247 | -------------------------------------------------------------------------------- /networking/web_sockets.md: -------------------------------------------------------------------------------- 1 | # Web Sockets 2 | 3 | webSockets不是Qt提供的。將WebSockets加入到Qt/QML中需要花費一些工作。從作者的角度來看WebSockets有巨大的潛力來添加HTTP服務缺少的功能-通知。HTTP給了我們get和post的功能,但是post還不是一個通知。目前客戶端輪詢服務器來獲得應用程序的服務,服務器也需要能通知客戶端變化和事件。你可以與QML接口比較:屬性,函數,信號。也可以叫做獲取/設置/調用和通知。 4 | 5 | QML WebSocket插件將會在Qt5中加入。你可以試試來自qt playground的web sockets插件。為了測試,我們使用一個現有的web socket服務實現了echo server。 6 | 7 | 首先確保你使用的Qt5.2.x。 8 | 9 | ``` 10 | $ qmake --version 11 | ... Using Qt version 5.2.0 ... 12 | ``` 13 | 14 | 然後你需要克隆web socket的代碼庫,並且編譯它。 15 | 16 | ``` 17 | $ git clone git@gitorious.org:qtplayground/websockets.git 18 | $ cd websockets 19 | $ qmake 20 | $ make 21 | $ make install 22 | ``` 23 | 24 | 現在你可以在qml模塊中使用web socket。 25 | 26 | ``` 27 | import Qt.WebSockets 1.0 28 | 29 | WebSocket { 30 | id: socket 31 | } 32 | ``` 33 | 34 | 測試你的web socket,我們使用來自[http://websocket.org](http://websocket.org)的echo server 。 35 | 36 | ``` 37 | import QtQuick 2.0 38 | import Qt.WebSockets 1.0 39 | 40 | Text { 41 | width: 480 42 | height: 48 43 | 44 | horizontalAlignment: Text.AlignHCenter 45 | verticalAlignment: Text.AlignVCenter 46 | 47 | WebSocket { 48 | id: socket 49 | url: "ws://echo.websocket.org" 50 | active: true 51 | onTextMessageReceived: { 52 | text = message 53 | } 54 | onStatusChanged: { 55 | if (socket.status == WebSocket.Error) { 56 | console.log("Error: " + socket.errorString) 57 | } else if (socket.status == WebSocket.Open) { 58 | socket.sendTextMessage("ping") 59 | } else if (socket.status == WebSocket.Closed) { 60 | text += "\nSocket closed" 61 | } 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | 你可以看到我們使用socket.sendTextMessage("ping")作為響應在文本區域中。 68 | 69 | ![](http://qmlbook.org/_images/ws_echo.png) 70 | 71 | ## 11.8.1 WS Server 72 | 73 | 你可以使用Qt WebSocket的C++部分來創建你自己的WS Server或者使用一個不同的WS實現。它非常有趣,是因為它允許連接使用大量擴展的web應用程序服務的高質量渲染的QML。在這個例子中,我們將使用基于web socket的ws模塊的Node JS。你首先需要安裝node.js。然後創建一個ws_server文件夾,使用node package manager(npm)安裝ws包。 74 | 75 | ``` 76 | $ cd ws_server 77 | $ npm install ws 78 | ``` 79 | 80 | npm工具下載並安裝了ws包到你的本地依賴文件夾中。 81 | 82 | 一個server.js文件是我們服務器的實現。服務器代碼將在端口3000創建一個web socket服務並監聽連接。在一個連接加入後,它將會發送一個歡迎並等待客戶端信息。每個客戶端發送到socket信息都會發送回客戶端。 83 | 84 | ``` 85 | var WebSocketServer = require('ws').Server; 86 | 87 | var server = new WebSocketServer({ port : 3000 }); 88 | 89 | server.on('connection', function(socket) { 90 | console.log('client connected'); 91 | socket.on('message', function(msg) { 92 | console.log('Message: %s', msg); 93 | socket.send(msg); 94 | }); 95 | socket.send('Welcome to Awesome Chat'); 96 | }); 97 | 98 | console.log('listening on port ' + server.options.port); 99 | ``` 100 | 101 | 你需要獲取使用的JavaScript標記和回調函數。 102 | 103 | ## 11.8.2 WS Client 104 | 105 | 在客戶端我們需要一個鏈表視圖來顯示信息,和一個文本輸入來輸入新的聊天信息。 106 | 107 | 在例子中我們使用一個白色的標簽。 108 | 109 | ``` 110 | // Label.qml 111 | import QtQuick 2.0 112 | 113 | Text { 114 | color: '#fff' 115 | horizontalAlignment: Text.AlignLeft 116 | verticalAlignment: Text.AlignVCenter 117 | } 118 | ``` 119 | 120 | 我們的聊天視圖是一個鏈表視圖,文本被加入到鏈表模型中。每個條目顯示使用行前綴和信息標簽。我們使用單元將它分為24列。 121 | 122 | ``` 123 | // ChatView.qml 124 | import QtQuick 2.0 125 | 126 | ListView { 127 | id: root 128 | width: 100 129 | height: 62 130 | 131 | model: ListModel {} 132 | 133 | function append(prefix, message) { 134 | model.append({prefix: prefix, message: message}) 135 | } 136 | 137 | delegate: Row { 138 | width: root.width 139 | height: 18 140 | property real cw: width/24 141 | Label { 142 | width: cw*1 143 | height: parent.height 144 | text: model.prefix 145 | } 146 | Label { 147 | width: cw*23 148 | height: parent.height 149 | text: model.message 150 | } 151 | } 152 | } 153 | ``` 154 | 155 | 聊天輸入框是一個簡單的使用顏色包裹邊界的文本輸入。 156 | 157 | ``` 158 | // ChatInput.qml 159 | import QtQuick 2.0 160 | 161 | FocusScope { 162 | id: root 163 | width: 240 164 | height: 32 165 | Rectangle { 166 | anchors.fill: parent 167 | color: '#000' 168 | border.color: '#fff' 169 | border.width: 2 170 | } 171 | 172 | property alias text: input.text 173 | 174 | signal accepted(string text) 175 | 176 | TextInput { 177 | id: input 178 | anchors.left: parent.left 179 | anchors.right: parent.right 180 | anchors.verticalCenter: parent.verticalCenter 181 | anchors.leftMargin: 4 182 | anchors.rightMargin: 4 183 | onAccepted: root.accepted(text) 184 | color: '#fff' 185 | focus: true 186 | } 187 | } 188 | ``` 189 | 190 | 當web socket返回一個信息後,它將會把信息添加到聊天視圖中。這也同樣適用于狀態改變。也可以當用戶輸入一個聊天信息,將聊天信息拷貝添加到客戶端的聊天視圖中,並將信息發送給服務器。 191 | 192 | ``` 193 | // ws_client.qml 194 | import QtQuick 2.0 195 | import Qt.WebSockets 1.0 196 | 197 | Rectangle { 198 | width: 360 199 | height: 360 200 | color: '#000' 201 | 202 | ChatView { 203 | id: box 204 | anchors.left: parent.left 205 | anchors.right: parent.right 206 | anchors.top: parent.top 207 | anchors.bottom: input.top 208 | } 209 | ChatInput { 210 | id: input 211 | anchors.left: parent.left 212 | anchors.right: parent.right 213 | anchors.bottom: parent.bottom 214 | focus: true 215 | onAccepted: { 216 | print('send message: ' + text) 217 | socket.sendTextMessage(text) 218 | box.append('>', text) 219 | text = '' 220 | } 221 | } 222 | WebSocket { 223 | id: socket 224 | 225 | url: "ws://localhost:3000" 226 | active: true 227 | onTextMessageReceived: { 228 | box.append('<', message) 229 | } 230 | onStatusChanged: { 231 | if (socket.status == WebSocket.Error) { 232 | box.append('#', 'socket error ' + socket.errorString) 233 | } else if (socket.status == WebSocket.Open) { 234 | box.append('#', 'socket open') 235 | } else if (socket.status == WebSocket.Closed) { 236 | box.append('#', 'socket closed') 237 | } 238 | } 239 | } 240 | } 241 | ``` 242 | 243 | 你首先需要運行服務器,然後是客戶端。在我們簡單例子中沒有客戶端重連的機制。 244 | 245 | 運行服務器 246 | 247 | ``` 248 | $ cd ws_server 249 | $ node server.js 250 | ``` 251 | 252 | 運行客戶端 253 | 254 | ``` 255 | $ cd ws_client 256 | $ qmlscene ws_client.qml 257 | ``` 258 | 259 | 當輸入文本並點擊發送後,你可以看到類似下面這樣。 260 | 261 | ![](http://qmlbook.org/_images/ws_client.png) 262 | -------------------------------------------------------------------------------- /networking/restrest_api.md: -------------------------------------------------------------------------------- 1 | # REST接口(REST API) 2 | 3 | 為了使用web服務,我們首先需要創建它。我們使用Flask(http://flask.pocoo.org),一個基于python創建簡單的顏色web服務的HTTP服務器應用。你也可以使用其它的web服務器,只要它接收和返回JSON數據。通過web服務來管理一組已經命名的顏色。在這個例子中,管理意味著CRUD(創建-讀取-更新-刪除)。 4 | 5 | 在Flask中一個簡單的web服務可以寫入一個文件。我們使用一個空的服務器.py文件開始,在這個文件中我們創建一些規則並且從額外的JSON文件中加載初始顏色。你可以查看Flask文檔獲取更多的幫助。 6 | 7 | ``` 8 | from flask import Flask, jsonify, request 9 | import json 10 | 11 | colors = json.load(file('colors.json', 'r')) 12 | 13 | app = Flask(__name__) 14 | 15 | # ... service calls go here 16 | 17 | if __name__ == '__main__': 18 | app.run(debug = True) 19 | ``` 20 | 21 | 當你運行這個腳本後,它會在http://localhost:5000。 22 | 23 | 我們開始添加我們的CRUD(創建,讀取,更新,刪除)到我們的web服務。 24 | 25 | ## 11.5.1 讀取請求(Read Request) 26 | 27 | 從web服務讀取數據,我們提供GET方法來讀取所有的顏色。 28 | 29 | ``` 30 | @app.route('/colors', methods = ['GET']) 31 | def get_colors(): 32 | return jsonify( { "colors" : colors }) 33 | ``` 34 | 35 | 這將會返回‘/colors’下的顏色。我們使用curl來創建一個http請求測試。 36 | 37 | ``` 38 | curl -i -GET http://localhost:5000/colors 39 | ``` 40 | 41 | 這將會返回給我們JSON數據的顏色鏈表。 42 | 43 | ## 11.5.2 讀取接口(Read Entry) 44 | 45 | 為了通過名字讀取顏色,我們提供更加詳細的後綴,定位在‘/colors/'下。名稱是後綴的參數,用來識別一個獨立的顏色。 46 | 47 | ``` 48 | @app.route('/colors/', methods = ['GET']) 49 | def get_color(name): 50 | for color in colors: 51 | if color["name"] == name: 52 | return jsonify( color ) 53 | ``` 54 | 55 | 我們再次使用curl測試,例如獲取一個紅色的接口。 56 | 57 | ``` 58 | curl -i -GET http://localhost:5000/colors/red 59 | ``` 60 | 61 | 這將返回一個JSON數據的顏色。 62 | 63 | ## 11.5.3 創建接口(Create Entry) 64 | 65 | 目前我們僅僅使用了HTTP GET方法。為了在服務器端創建一個接口,我們使用POST方法,並且將新的顏色信息發使用POST數據發送。後綴與獲取所有顏色相同,但是我們需要使用一個POST請求。 66 | 67 | ``` 68 | @app.route('/colors', methods= ['POST']) 69 | def create_color(): 70 | color = { 71 | 'name': request.json['name'], 72 | 'value': request.json['value'] 73 | } 74 | colors.append(color) 75 | return jsonify( color ), 201 76 | ``` 77 | 78 | curl非常靈活,允許我們使用JSON數據作為新的接口包含在POST請求中。 79 | 80 | ``` 81 | curl -i -H "Content-Type: application/json" -X POST -d '{"name":"gray1","value":"#333"}' http://localhost:5000/colors 82 | ``` 83 | 84 | ## 11.5.4 更新接口(Update Entry) 85 | 86 | 我們使用PUT HTTP方法來添加新的update接口。後綴與取得一個顏色接口相同。當顏色更新後,我們獲取更新後JSON數據的顏色。 87 | 88 | ``` 89 | @app.route('/colors/', methods= ['PUT']) 90 | def update_color(name): 91 | for color in colors: 92 | if color["name"] == name: 93 | color['value'] = request.json.get('value', color['value']) 94 | return jsonify( color ) 95 | ``` 96 | 97 | 在curl請求中,我們用JSON數據來定義更新值,後綴名用來識別哪個顏色需要更新。 98 | 99 | ``` 100 | curl -i -H "Content-Type: application/json" -X PUT -d '{"value":"#666"}' http://localhost:5000/colors/red 101 | ``` 102 | 103 | ## 11.5.5 刪除接口(Delete Entry) 104 | 105 | 使用DELETE HTTP來完成刪除接口。使用與顏色相同的後綴,但是使用DELETE HTTP方法。 106 | 107 | ``` 108 | @app.route('/colors/', methods=['DELETE']) 109 | def delete_color(name): 110 | success = False 111 | for color in colors: 112 | if color["name"] == name: 113 | colors.remove(color) 114 | success = True 115 | return jsonify( { 'result' : success } ) 116 | ``` 117 | 118 | 這個請求看起來與GET請求一個顏色類似。 119 | 120 | ``` 121 | curl -i -X DELETE http://localhost:5000/colors/red 122 | ``` 123 | 124 | 現在我們能夠讀取所有顏色,讀取指定顏色,創建新的顏色,更新顏色和刪除顏色。我們知道使用HTTP後綴來訪問我們的接口。 125 | 126 | | 動作 | HTTP協議 | 後綴 | 127 | | -- | -- | -- | 128 | | 讀取所有 | GET | http://localhost:5000/colors | 129 | | 創建接口 | POST | http://localhost:5000/colors | 130 | | 讀取接口 | GET | http://localhost:5000/colors/name | 131 | | 更新接口 | PUT | http://localhost:5000/colors/name | 132 | | 刪除接口 | DELETE | http://localhost:500/colors/name | 133 | 134 | REST服務已經完成,我們現在只需要關注QML和客戶端。為了創建一個簡單好用的接口,我們需要映射每個動作為一個獨立的HTTP請求,並且給我們的用戶提供一個簡單的接口。 135 | 136 | ## 11.5.6 REST客戶端(REST Client) 137 | 138 | 為了展示REST客戶端,我們寫了一個小的顏色表格。這個顏色表格顯示了通過HTTP請求從web服務取得的顏色。我們的用戶界面提供以下命令: 139 | 140 | * 獲取顏色鏈表 141 | 142 | * 創建顏色 143 | 144 | * 讀取最後的顏色 145 | 146 | * 更新最後的顏色 147 | 148 | * 刪除最後的顏色 149 | 150 | 我們將我們的接口包裝在一個JS文件中,叫做colorservice.js,並將它導入到我們的UI中作為服務(Service)。在服務模塊中,我們創建了幫助函數來為我們構造HTTP請求: 151 | 152 | ``` 153 | // colorservice.js 154 | function request(verb, endpoint, obj, cb) { 155 | print('request: ' + verb + ' ' + BASE + (endpoint?'/' + endpoint:'')) 156 | var xhr = new XMLHttpRequest(); 157 | xhr.onreadystatechange = function() { 158 | print('xhr: on ready state change: ' + xhr.readyState) 159 | if(xhr.readyState === XMLHttpRequest.DONE) { 160 | if(cb) { 161 | var res = JSON.parse(xhr.responseText.toString()) 162 | cb(res); 163 | } 164 | } 165 | } 166 | xhr.open(verb, BASE + (endpoint?'/' + endpoint:'')); 167 | xhr.setRequestHeader('Content-Type', 'application/json'); 168 | xhr.setRequestHeader('Accept', 'application/json'); 169 | var data = obj?JSON.stringify(obj):'' 170 | xhr.send(data) 171 | } 172 | ``` 173 | 174 | 包含四個參數。verb,定義了使用HTTP的動作(GET,POST,PUT,DELETE)。第二個參數是作為基礎地址的後綴(例如’[http://localhost:5000/colors](http://localhost:5000/colors)')。第三個參數是可選對象,作為JSON數據發送給服務的數據。最後一個選項是定義當響應返回時的回調。回調接收一個響應數據的響應對象。 175 | 176 | 在我們發送請求前,我們需要明確我們發送和接收的JSON數據修改的請求頭。 177 | 178 | ``` 179 | // colorservice.js 180 | function get_colors(cb) { 181 | // GET http://localhost:5000/colors 182 | request('GET', null, null, cb) 183 | } 184 | 185 | function create_color(entry, cb) { 186 | // POST http://localhost:5000/colors 187 | request('POST', null, entry, cb) 188 | } 189 | 190 | function get_color(name, cb) { 191 | // GET http://localhost:5000/colors/ 192 | request('GET', name, null, cb) 193 | } 194 | 195 | function update_color(name, entry, cb) { 196 | // PUT http://localhost:5000/colors/ 197 | request('PUT', name, entry, cb) 198 | } 199 | 200 | function delete_color(name, cb) { 201 | // DELETE http://localhost:5000/colors/ 202 | request('DELETE', name, null, cb) 203 | } 204 | ``` 205 | 206 | 這些代碼在服務實現中。在UI中我們使用服務來實現我們的命令。我們有一個存儲id的ListModel和存儲數據的gridModel為GridView提供數據。命令使用Button元素來發送。 207 | 208 | 讀取服務器顏色鏈表。 209 | 210 | ``` 211 | // rest.qml 212 | import "colorservice.js" as Service 213 | ... 214 | // read colors command 215 | Button { 216 | text: 'Read Colors'; 217 | onClicked: { 218 | Service.get_colors( function(resp) { 219 | print('handle get colors resp: ' + JSON.stringify(resp)); 220 | gridModel.clear(); 221 | var entries = resp.data; 222 | for(var i=0; i> 180 | ``` 181 | 182 | ![](http://qmlbook.org/_images/listview-highlight.png) 183 | 184 | 當使用高亮與鏈表視圖(ListView)結合時,一些屬性可以用來控制它的行為。highlightRangeMode控制了高亮如何影響視圖中當前的顯示。默認設置ListView.NoHighLighRange意味著高亮與視圖中的元素距離不相關。 185 | 186 | ListView.StrictlyEnforceRnage確保了高亮始終可見,如果某個動作嘗試將高亮移出當前視圖可見範圍,當前元素將會自動切換,確保了高亮始終可見。 187 | 188 | ListView.ApplyRange,它嘗試保持高亮代理始終可見,但是不會強制切換當前元素始終可見。如果在需要的情況下高亮代理允許被移出當前視圖。 189 | 190 | 在默認配置下,視圖負責高亮移動到指定位置,移動的速度與大小的改變能夠被控制,使用一個速度值或者一個動作持續時間來完成它。這些屬性包括highlightMoveSpeed,highlightMoveDuration,highlightResizeSpeed和highlightResizeDuration。默認下速度被設置為每秒400像素,動作持續時間為-1,表明速度和距離控制了動作的持續時間。如果速度與動作持續時間都被設置,動畫將會採用速度較快的結果來完成。 191 | 192 | 為了更加詳細的控制高亮的移動,highlightFollowCurrentItem屬性設置為false。這意味著視圖將不再負責高亮代理的移動。取而代之可以通過一個行為(Bahavior)或者一個動畫來控制它。 193 | 194 | 在下面的例子中,高亮代理的y坐標屬性與ListView.view.currentItem.y屬性綁定。這確保了高亮始終跟隨當前元素。然而,由于我們沒有讓視圖來移動這個高亮代理,我們需要控制這個元素如何移動,通過Behavior on y來完成這個操作,在下面的例子中,移動分為三步完成:淡出,移動,淡入。注意怎樣使用SequentialAnimation和PropertyAnimation元素與NumberAnimation結合創建更加復雜的移動效果。 195 | 196 | ``` 197 | Component { 198 | id: highlightComponent 199 | 200 | Item { 201 | width: ListView.view.width 202 | height: ListView.view.currentItem.height 203 | 204 | y: ListView.view.currentItem.y 205 | 206 | Behavior on y { 207 | SequentialAnimation { 208 | PropertyAnimation { target: highlightRectangle; property: "opacity"; to: 0; duration: 200 } 209 | NumberAnimation { duration: 1 } 210 | PropertyAnimation { target: highlightRectangle; property: "opacity"; to: 1; duration: 200 } 211 | } 212 | } 213 | 214 | Rectangle { 215 | id: highlightRectangle 216 | anchors.fill: parent 217 | color: "lightGreen" 218 | } 219 | } 220 | } 221 | ``` 222 | 223 | ## 6.3.3 頁眉與頁腳(Header and Footer) 224 | 225 | 這一節是鏈表視圖最後的內容,我們能夠向鏈表視圖中插入一個頁眉(header)元素和一個頁腳(footer)元素。這部分是鏈表的開始或者結尾處被作為代理元素特殊的區域。對于一個水平鏈表視圖,不會存在頁眉或者頁腳,但是也有開始和結尾處,這取決于layoutDirection的設置。 226 | 227 | 下面這個例子展示了如何使用一個頁眉和頁腳來突出鏈表的開始與結尾。這些特殊的鏈表元素也有其它的作用,例如,它們能夠保持鏈表中的按鍵加載更多的內容。 228 | 229 | ``` 230 | import QtQuick 2.0 231 | 232 | Rectangle { 233 | width: 80 234 | height: 300 235 | 236 | color: "white" 237 | 238 | ListView { 239 | anchors.fill: parent 240 | anchors.margins: 20 241 | 242 | clip: true 243 | 244 | model: 4 245 | 246 | delegate: numberDelegate 247 | spacing: 5 248 | 249 | header: headerComponent 250 | footer: footerComponent 251 | } 252 | 253 | Component { 254 | id: headerComponent 255 | 256 | Rectangle { 257 | width: 40 258 | height: 20 259 | 260 | color: "yellow" 261 | } 262 | } 263 | 264 | Component { 265 | id: footerComponent 266 | 267 | Rectangle { 268 | width: 40 269 | height: 20 270 | 271 | color: "red" 272 | } 273 | } 274 | 275 | Component { 276 | id: numberDelegate 277 | 278 | Rectangle { 279 | width: 40 280 | height: 40 281 | 282 | color: "lightGreen" 283 | 284 | Text { 285 | anchors.centerIn: parent 286 | 287 | font.pixelSize: 10 288 | 289 | text: index 290 | } 291 | } 292 | } 293 | } 294 | ``` 295 | 296 | **注意** 297 | 298 | **頁眉與頁腳代理元素不遵循鏈表視圖(ListView)的間隔(spacing)屬性,它們被直接放在相鄰的鏈表元素之上或之下。這意味著頁眉與頁腳的間隔必須通過頁眉與頁腳元素自己設置。** 299 | 300 | ![](http://qmlbook.org/_images/listview-header-footer.png) 301 | 302 | ## 6.3.4 網格視圖(The GridView) 303 | 304 | 使用網格視圖(GridView)與使用鏈表視圖(ListView)的方式非常類似。真正不同的地方是網格視圖(GridView)使用了一個二維數組來存放元素,而鏈表視圖(ListView)是使用的線性鏈表來存放元素。 305 | 306 | ![](http://qmlbook.org/_images/gridview-basic.png) 307 | 308 | 與鏈表視圖(ListView)比較,網格視圖(GridView)不依賴于元素間隔和大小來配置元素。它使用單元寬度(cellWidth)與單元高度(cellHeight)屬性來控制數組內的二維元素的內容。每個元素從左上角開始依次放入單元格。 309 | 310 | ``` 311 | import QtQuick 2.0 312 | 313 | Rectangle { 314 | width: 240 315 | height: 300 316 | 317 | color: "white" 318 | 319 | GridView { 320 | anchors.fill: parent 321 | anchors.margins: 20 322 | 323 | clip: true 324 | 325 | model: 100 326 | 327 | cellWidth: 45 328 | cellHeight: 45 329 | 330 | delegate: numberDelegate 331 | } 332 | 333 | Component { 334 | id: numberDelegate 335 | 336 | Rectangle { 337 | width: 40 338 | height: 40 339 | 340 | color: "lightGreen" 341 | 342 | Text { 343 | anchors.centerIn: parent 344 | 345 | font.pixelSize: 10 346 | 347 | text: index 348 | } 349 | } 350 | } 351 | } 352 | ``` 353 | 354 | 一個網格視圖(GridView)也包含了頁腳與頁眉,也可以使用高亮代理並且支持捕捉模式(snap mode)的多種反彈行為。它也可以使用不同的方向(orientations)與定向(directions)來定位。 355 | 356 | 定向使用flow屬性來控制。它可以被設置為GridView.LeftToRight或者GridView.TopToBottom。模型的值從左往右向網格中填充,行添加是從上往下。視圖使用一個垂直方向的滾動條。後面添加的元素也是由上到下,由左到右。 357 | 358 | 此外還有flow屬性和layoutDirection屬性,能夠適配網格從左到右或者從右到左,這依賴于你使用的設置值。 359 | -------------------------------------------------------------------------------- /model-view-delegate/delegate.md: -------------------------------------------------------------------------------- 1 | # 代理(Delegate) 2 | 3 | 當使用模型與視圖來自定義用戶界面時,代理在創建顯示時扮演了大量的角色。在模型中的每個元素通過代理來實現可視化,用戶真實可見的是這些代理元素。 4 | 5 | 每個代理訪問到索引號或者綁定的屬性,一些是來自數據模型,一些來自視圖。來自模型的數據將會通過屬性傳遞到代理。來自視圖的數據將會通過屬性傳遞視圖中與代理相關的狀態信息。 6 | 7 | 通常使用的視圖綁定屬性是ListView.isCurrentItem和ListView.view。第一個是一個布爾值,標識這個元素是否是視圖當前元素,這個值是只讀的,引用自當前視圖。通過訪問視圖,可以創建可復用的代理,這些代理在被包含時會自動匹配視圖的大小。在下面這個例子中,每個代理的width(寬度)屬性與視圖的width(寬度)屬性綁定,每個代理的背景顏色color依賴于綁定的屬性ListView.isCurrentItem屬性。 8 | 9 | ``` 10 | import QtQuick 2.0 11 | 12 | Rectangle { 13 | width: 120 14 | height: 300 15 | 16 | color: "white" 17 | 18 | ListView { 19 | anchors.fill: parent 20 | anchors.margins: 20 21 | 22 | clip: true 23 | 24 | model: 100 25 | 26 | delegate: numberDelegate 27 | spacing: 5 28 | 29 | focus: true 30 | } 31 | 32 | Component { 33 | id: numberDelegate 34 | 35 | Rectangle { 36 | width: ListView.view.width 37 | height: 40 38 | 39 | color: ListView.isCurrentItem?"gray":"lightGray" 40 | 41 | Text { 42 | anchors.centerIn: parent 43 | 44 | font.pixelSize: 10 45 | 46 | text: index 47 | } 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | ![](http://qmlbook.org/_images/delegates-basic.png) 54 | 55 | 如果在模型中的每個元素與一個動作相關,例如點擊作用于一個元素時,這個功能是代理完成的。這是由事件管理分配給視圖的,這個操作控制了視圖中元素的導航,代理控制了特定元素上的動作。 56 | 57 | 最基礎的方法是在每個代理中創建一個MouseArea(鼠標區域)並且響應onClicked信號。在後面章節中將會演示這個例子。 58 | 59 | ## 6.4.1 動畫添加與移除元素(Animating Added and Removed Items) 60 | 61 | 在某些情況下,視圖中的顯示內容會隨著時間而改變。由于模型數據的改變,元素會添加或者移除。在這些情況下,一個比較好的做法是使用可視化隊列給用戶一個方向的感覺來幫助用戶知道哪些數據被加入或者移除。 62 | 63 | 為了方便使用,QML視圖為每個代理綁定了兩個信號,onAdd和onRemove。使用動畫連接它們,可以方便創建識別哪些內容被添加或刪除的動畫。 64 | 65 | 下面這個例子演示了如何動態填充一個鏈表模型(ListModel)。在屏幕下方,有一個添加新元素的按鈕。當點擊它時,會調用模型的append方法來添加一個新的元素。這個操作會觸發視圖創建一個新的代理,並發送GridView.onAdd信號。SequentialAnimation隊列動畫與這個信號連接綁定,使用代理的scale屬性來放大視圖元素。 66 | 67 | 當視圖中的一個代理點擊時,將會調用模型的remove方法將一個元素從模型中移除。這個操作將會導致GridView.onRemove信號的發送,觸發另一個SequentialAnimation。這時,代理的銷毀將會延遲直到動畫完成。為了完成這個操作,PropertyAction元素需要在動畫前設置GridView.delayRemove屬性為true,並在動畫後設置為false。這樣確保了動畫在代理項移除前完成。 68 | 69 | ``` 70 | import QtQuick 2.0 71 | 72 | Rectangle { 73 | width: 480 74 | height: 300 75 | 76 | color: "white" 77 | 78 | ListModel { 79 | id: theModel 80 | 81 | ListElement { number: 0 } 82 | ListElement { number: 1 } 83 | ListElement { number: 2 } 84 | ListElement { number: 3 } 85 | ListElement { number: 4 } 86 | ListElement { number: 5 } 87 | ListElement { number: 6 } 88 | ListElement { number: 7 } 89 | ListElement { number: 8 } 90 | ListElement { number: 9 } 91 | } 92 | 93 | Rectangle { 94 | anchors.left: parent.left 95 | anchors.right: parent.right 96 | anchors.bottom: parent.bottom 97 | anchors.margins: 20 98 | 99 | height: 40 100 | 101 | color: "darkGreen" 102 | 103 | Text { 104 | anchors.centerIn: parent 105 | 106 | text: "Add item!" 107 | } 108 | 109 | MouseArea { 110 | anchors.fill: parent 111 | 112 | onClicked: { 113 | theModel.append({"number": ++parent.count}); 114 | } 115 | } 116 | 117 | property int count: 9 118 | } 119 | 120 | GridView { 121 | anchors.fill: parent 122 | anchors.margins: 20 123 | anchors.bottomMargin: 80 124 | 125 | clip: true 126 | 127 | model: theModel 128 | 129 | cellWidth: 45 130 | cellHeight: 45 131 | 132 | delegate: numberDelegate 133 | } 134 | 135 | Component { 136 | id: numberDelegate 137 | 138 | Rectangle { 139 | id: wrapper 140 | 141 | width: 40 142 | height: 40 143 | 144 | color: "lightGreen" 145 | 146 | Text { 147 | anchors.centerIn: parent 148 | 149 | font.pixelSize: 10 150 | 151 | text: number 152 | } 153 | 154 | MouseArea { 155 | anchors.fill: parent 156 | 157 | onClicked: { 158 | if (!wrapper.GridView.delayRemove) 159 | theModel.remove(index); 160 | } 161 | } 162 | 163 | GridView.onRemove: SequentialAnimation { 164 | PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: true } 165 | NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 250; easing.type: Easing.InOutQuad } 166 | PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: false } 167 | } 168 | 169 | GridView.onAdd: SequentialAnimation { 170 | NumberAnimation { target: wrapper; property: "scale"; from: 0; to: 1; duration: 250; easing.type: Easing.InOutQuad } 171 | } 172 | } 173 | } 174 | } 175 | ``` 176 | 177 | ## 6.4.2 形變的代理(Shape-Shifting Delegates) 178 | 179 | 在使用鏈表時通常會使用當前項激活時展開的機制。這個操作可以被用于動態的將當前項目填充到整個屏幕來添加一個新的用戶界面,或者為鏈表中的當前項提供更多的信息。 180 | 181 | 在下面的例子中,當點擊鏈表項時,鏈表項都會展開填充整個鏈表視圖(ListView)。額外的間隔區域被用于添加更多的信息,這種機制使用一個狀態來控制,當一個鏈表項展開時,代理項都能輸入expanded(展開)狀態,在這種狀態下一些屬性被改變。 182 | 183 | 首先,包裝器(wrapper)的高度(height)被設置為鏈表視圖(ListView)的高度。標簽圖片被放大並且下移,使圖片從小圖片的位置移向大圖片的位置。除了這些之外,兩個隱藏項,實際視圖(factsView)與關閉按鍵(closeButton)切換它的opactiy(透明度)顯示出來。最後設置鏈表視圖(ListView)。 184 | 185 | 設置鏈表視圖(ListView)包含了設置內容Y坐標(contentsY),這是視圖頂部可見的部分代理的Y軸坐標。另一個變化是設置視圖的交互(interactive)為false。這個操作阻止了視圖的移動,用戶不再能夠通過滾動條切換當前項。 186 | 187 | 由于設置第一個鏈表項為可點擊,向它輸入一個expanded(展開)狀態,導致了它的代理項被填充到整個鏈表並且內容重置。當點擊關閉按鈕時,清空狀態,導致它的代理項返回上一個狀態,並且重新設置鏈表視圖(ListView)有效。 188 | 189 | ``` 190 | import QtQuick 2.0 191 | 192 | Item { 193 | width: 300 194 | height: 480 195 | 196 | ListView { 197 | id: listView 198 | 199 | anchors.fill: parent 200 | 201 | delegate: detailsDelegate 202 | model: planets 203 | } 204 | 205 | ListModel { 206 | id: planets 207 | 208 | ListElement { name: "Mercury"; imageSource: "images/mercury.jpeg"; facts: "Mercury is the smallest planet in the Solar System. It is the closest planet to the sun. It makes one trip around the Sun once every 87.969 days." } 209 | ListElement { name: "Venus"; imageSource: "images/venus.jpeg"; facts: "Venus is the second planet from the Sun. It is a terrestrial planet because it has a solid, rocky surface. The other terrestrial planets are Mercury, Earth and Mars. Astronomers have known Venus for thousands of years." } 210 | ListElement { name: "Earth"; imageSource: "images/earth.jpeg"; facts: "The Earth is the third planet from the Sun. It is one of the four terrestrial planets in our Solar System. This means most of its mass is solid. The other three are Mercury, Venus and Mars. The Earth is also called the Blue Planet, 'Planet Earth', and 'Terra'." } 211 | ListElement { name: "Mars"; imageSource: "images/mars.jpeg"; facts: "Mars is the fourth planet from the Sun in the Solar System. Mars is dry, rocky and cold. It is home to the largest volcano in the Solar System. Mars is named after the mythological Roman god of war because it is a red planet, which signifies the colour of blood." } 212 | } 213 | 214 | Component { 215 | id: detailsDelegate 216 | 217 | Item { 218 | id: wrapper 219 | 220 | width: listView.width 221 | height: 30 222 | 223 | Rectangle { 224 | anchors.left: parent.left 225 | anchors.right: parent.right 226 | anchors.top: parent.top 227 | 228 | height: 30 229 | 230 | color: "#ffaa00" 231 | 232 | Text { 233 | anchors.left: parent.left 234 | anchors.verticalCenter: parent.verticalCenter 235 | 236 | font.pixelSize: parent.height-4 237 | 238 | text: name 239 | } 240 | } 241 | 242 | Rectangle { 243 | id: image 244 | 245 | color: "black" 246 | 247 | anchors.right: parent.right 248 | anchors.top: parent.top 249 | anchors.rightMargin: 2 250 | anchors.topMargin: 2 251 | 252 | width: 26 253 | height: 26 254 | 255 | Image { 256 | anchors.fill: parent 257 | 258 | fillMode: Image.PreserveAspectFit 259 | 260 | source: imageSource 261 | } 262 | } 263 | 264 | MouseArea { 265 | anchors.fill: parent 266 | onClicked: parent.state = "expanded" 267 | } 268 | 269 | Item { 270 | id: factsView 271 | 272 | anchors.top: image.bottom 273 | anchors.left: parent.left 274 | anchors.right: parent.right 275 | anchors.bottom: parent.bottom 276 | 277 | opacity: 0 278 | 279 | Rectangle { 280 | anchors.fill: parent 281 | 282 | color: "#cccccc" 283 | 284 | Text { 285 | anchors.fill: parent 286 | anchors.margins: 5 287 | 288 | clip: true 289 | wrapMode: Text.WordWrap 290 | 291 | font.pixelSize: 12 292 | 293 | text: facts 294 | } 295 | } 296 | } 297 | 298 | Rectangle { 299 | id: closeButton 300 | 301 | anchors.right: parent.right 302 | anchors.top: parent.top 303 | anchors.rightMargin: 2 304 | anchors.topMargin: 2 305 | 306 | width: 26 307 | height: 26 308 | 309 | color: "red" 310 | 311 | opacity: 0 312 | 313 | MouseArea { 314 | anchors.fill: parent 315 | onClicked: wrapper.state = "" 316 | } 317 | } 318 | 319 | states: [ 320 | State { 321 | name: "expanded" 322 | 323 | PropertyChanges { target: wrapper; height: listView.height } 324 | PropertyChanges { target: image; width: listView.width; height: listView.width; anchors.rightMargin: 0; anchors.topMargin: 30 } 325 | PropertyChanges { target: factsView; opacity: 1 } 326 | PropertyChanges { target: closeButton; opacity: 1 } 327 | PropertyChanges { target: wrapper.ListView.view; contentY: wrapper.y; interactive: false } 328 | } 329 | ] 330 | 331 | transitions: [ 332 | Transition { 333 | NumberAnimation { 334 | duration: 200; 335 | properties: "height,width,anchors.rightMargin,anchors.topMargin,opacity,contentY" 336 | } 337 | } 338 | ] 339 | } 340 | } 341 | } 342 | ``` 343 | 344 | ![](http://qmlbook.org/_images/delegates-expanding-small.png) 345 | 346 | ![](http://qmlbook.org/_images/delegates-expanding-large.png) 347 | 348 | 這個技術展示了展開代理來填充視圖能夠簡單的通過代理的形變來完成。例如當瀏覽一個歌曲的鏈表時,可以通過放大當前項來對該項添加更多的說明。 349 | -------------------------------------------------------------------------------- /shader_effect/vertex_shader.md: -------------------------------------------------------------------------------- 1 | # 頂點著色器(Vertex Shader) 2 | 3 | 頂點著色器用來操作ShaderEffect提供的頂點。正常情況下,ShaderEffect有4個頂點(左上top-left,右上top-right,左下bottom-left,右下bottom-right)。每個頂點使用vec4類型記錄。為了實現頂點著色器的可視化,我們將編寫一個吸收的效果。這個效果通常被用來讓一個矩形窗口消失為一個點。 4 | 5 | ![](http://qmlbook.org/_images/genieeffect.png) 6 | 7 | **配置場景(Setting up the scene)** 8 | 9 | 首先我們再一次配置場景。 10 | 11 | ``` 12 | import QtQuick 2.0 13 | 14 | Rectangle { 15 | width: 480; height: 240 16 | color: '#1e1e1e' 17 | 18 | Image { 19 | id: sourceImage 20 | width: 160; height: width 21 | source: "assets/lighthouse.jpg" 22 | visible: false 23 | } 24 | Rectangle { 25 | width: 160; height: width 26 | anchors.centerIn: parent 27 | color: '#333333' 28 | } 29 | ShaderEffect { 30 | id: genieEffect 31 | width: 160; height: width 32 | anchors.centerIn: parent 33 | property variant source: sourceImage 34 | property bool minimized: false 35 | MouseArea { 36 | anchors.fill: parent 37 | onClicked: genieEffect.minimized = !genieEffect.minimized 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | 這個場景使用了一個黑色背景,並且提供了一個使用圖片作為資源紋理的ShaderEffect。使用image元素的原圖片是不可見的,只是給我們的吸收效果提供資源。此外我們在ShaderEffect的位置添加了一個同樣大小的黑色矩形框,這樣我們可以更加明確的知道我們需要點擊哪裡來重置效果。 44 | 45 | ![](http://qmlbook.org/_images/geniescene.png) 46 | 47 | 點擊圖片將會觸發效果,MouseArea覆蓋了ShaderEffect。在onClicked操作中,我們綁定了自定義的布爾變量屬性minimized。我們稍後使用這個屬性來觸發效果。 48 | 49 | **最小化與正常化(Minimize and normalize)** 50 | 51 | 在我們配置好場景後,我們定義一個real類型的屬性,叫做minimize,這個屬性包含了我們當前最小化的值。這個值在0.0到1.0之間,由一個連續的動畫來控制它。 52 | 53 | ``` 54 | property real minimize: 0.0 55 | 56 | SequentialAnimation on minimize { 57 | id: animMinimize 58 | running: genieEffect.minimized 59 | PauseAnimation { duration: 300 } 60 | NumberAnimation { to: 1; duration: 700; easing.type: Easing.InOutSine } 61 | PauseAnimation { duration: 1000 } 62 | } 63 | 64 | SequentialAnimation on minimize { 65 | id: animNormalize 66 | running: !genieEffect.minimized 67 | NumberAnimation { to: 0; duration: 700; easing.type: Easing.InOutSine } 68 | PauseAnimation { duration: 1300 } 69 | } 70 | ``` 71 | 72 | 這個動畫綁定了由minimized屬性觸發。現在我們已經配置好我們的環境,最後讓我們看看頂點著色器的代碼。 73 | 74 | ``` 75 | vertexShader: " 76 | uniform highp mat4 qt_Matrix; 77 | attribute highp vec4 qt_Vertex; 78 | attribute highp vec2 qt_MultiTexCoord0; 79 | varying highp vec2 qt_TexCoord0; 80 | uniform highp float minimize; 81 | uniform highp float width; 82 | uniform highp float height; 83 | void main() { 84 | qt_TexCoord0 = qt_MultiTexCoord0; 85 | highp vec4 pos = qt_Vertex; 86 | pos.y = mix(qt_Vertex.y, height, minimize); 87 | pos.x = mix(qt_Vertex.x, width, minimize); 88 | gl_Position = qt_Matrix * pos; 89 | }" 90 | ``` 91 | 92 | 頂點著色器被每個頂點調用,在我們這個例子中,一共調用了四次。默認下提供qt已定義的參數,如qt_Matrix,qt_Vertex,qt_MultiTexCoord0,qt_TexCoord0。我們在之前已經討論過這些變量。此外我們從ShaderEffect中鏈接minimize,width與height的值到我們的頂點著色器代碼中。在main函數中,我們將當前紋理值保存在qt_TexCoord()中,讓它在片段著色器中可用。現在我們拷貝當前位置,並修改頂點的x,y的位置。 93 | 94 | ``` 95 | highp vec4 pos = qt_Vertex; 96 | pos.y = mix(qt_Vertex.y, height, minimize); 97 | pos.x = mix(qt_Vertex.x, width, minimize); 98 | ``` 99 | 100 | mix(...)函數提供了一種在兩個參數之間(0.0到1.0)的線性插值的算法。在我們的例子中,在當前y值與高度值之間基于minimize的值插值獲得y值,x的值獲取類似。記住minimize的值是由我們的連續動畫控制,並且在0.0到1.0之間(反之亦然)。 101 | 102 | ![](http://qmlbook.org/_images/genieminimize.png) 103 | 104 | 這個結果的效果不是真正吸收效果,但是已經能朝著這個目標完成了一大步。 105 | 106 | **基礎彎曲(Primitive Bending)** 107 | 108 | 我們已經完成了最小化我們的坐標。現在我們想要修改一下對x值的操作,讓它依賴當前的y值。這個改變很簡單。y值計算在前。x值的插值基于當前頂點的y坐標。 109 | 110 | ``` 111 | highp float t = pos.y / height; 112 | pos.x = mix(qt_Vertex.x, width, t * minimize); 113 | ``` 114 | 115 | 這個結果造成當y值比較大時,x的位置更靠近width的值。也就是說上面2個頂點根本不受影響,它們的y值始終為0,下面兩個頂點的x坐標值更靠近width的值,它們最後轉向同一個x值。 116 | 117 | ![](http://qmlbook.org/_images/geniebending.png) 118 | 119 | ``` 120 | import QtQuick 2.0 121 | 122 | Rectangle { 123 | width: 480; height: 240 124 | color: '#1e1e1e' 125 | 126 | Image { 127 | id: sourceImage 128 | width: 160; height: width 129 | source: "assets/lighthouse.jpg" 130 | visible: false 131 | } 132 | Rectangle { 133 | width: 160; height: width 134 | anchors.centerIn: parent 135 | color: '#333333' 136 | } 137 | ShaderEffect { 138 | id: genieEffect 139 | width: 160; height: width 140 | anchors.centerIn: parent 141 | property variant source: sourceImage 142 | property real minimize: 0.0 143 | property bool minimized: false 144 | 145 | 146 | SequentialAnimation on minimize { 147 | id: animMinimize 148 | running: genieEffect.minimized 149 | PauseAnimation { duration: 300 } 150 | NumberAnimation { to: 1; duration: 700; easing.type: Easing.InOutSine } 151 | PauseAnimation { duration: 1000 } 152 | } 153 | 154 | SequentialAnimation on minimize { 155 | id: animNormalize 156 | running: !genieEffect.minimized 157 | NumberAnimation { to: 0; duration: 700; easing.type: Easing.InOutSine } 158 | PauseAnimation { duration: 1300 } 159 | } 160 | 161 | 162 | vertexShader: " 163 | uniform highp mat4 qt_Matrix; 164 | uniform highp float minimize; 165 | uniform highp float height; 166 | uniform highp float width; 167 | attribute highp vec4 qt_Vertex; 168 | attribute highp vec2 qt_MultiTexCoord0; 169 | varying highp vec2 qt_TexCoord0; 170 | void main() { 171 | qt_TexCoord0 = qt_MultiTexCoord0; 172 | // M1>> 173 | highp vec4 pos = qt_Vertex; 174 | pos.y = mix(qt_Vertex.y, height, minimize); 175 | highp float t = pos.y / height; 176 | pos.x = mix(qt_Vertex.x, width, t * minimize); 177 | gl_Position = qt_Matrix * pos; 178 | ``` 179 | 180 | **更好的彎曲(Better Bending)** 181 | 182 | 現在簡單的彎曲並不能真正的滿足我們的要求,我們將添加幾個部件來提升它的效果。首先我們增加動畫,支持一個自定義的彎曲屬性。這是非常必要的,由于彎曲立即發生,y值的最小化需要被推遲。兩個動畫在同一持續時間計算總和(300+700+100與700+1300)。 183 | 184 | ``` 185 | property real bend: 0.0 186 | property bool minimized: false 187 | 188 | 189 | // change to parallel animation 190 | ParallelAnimation { 191 | id: animMinimize 192 | running: genieEffect.minimized 193 | SequentialAnimation { 194 | PauseAnimation { duration: 300 } 195 | NumberAnimation { 196 | target: genieEffect; property: 'minimize'; 197 | to: 1; duration: 700; 198 | easing.type: Easing.InOutSine 199 | } 200 | PauseAnimation { duration: 1000 } 201 | } 202 | // adding bend animation 203 | SequentialAnimation { 204 | NumberAnimation { 205 | target: genieEffect; property: 'bend' 206 | to: 1; duration: 700; 207 | easing.type: Easing.InOutSine } 208 | PauseAnimation { duration: 1300 } 209 | } 210 | } 211 | ``` 212 | 213 | 此外,為了使彎曲更加平滑,不再使用y值影響x值的彎曲函數,pos.x現在依賴新的彎曲屬性動畫: 214 | 215 | ``` 216 | highp float t = pos.y / height; 217 | t = (3.0 - 2.0 * t) * t * t; 218 | pos.x = mix(qt_Vertex.x, width, t * bend); 219 | ``` 220 | 221 | 彎曲從0.0平滑開始,逐漸加快,在1.0時逐漸平滑。下面是這個函數在指定範圍內的曲線圖。對于我們,只需要關注0到1的區間。 222 | 223 | ![](http://qmlbook.org/_images/curve.png) 224 | 225 | 想要獲得最大化的視覺改變,需要增加我們的頂點數量。可以使用網眼(mesh)來增加頂點: 226 | 227 | ``` 228 | mesh: GridMesh { resolution: Qt.size(16, 16) } 229 | ``` 230 | 231 | 現在ShaderEffect被分布為16x16頂點的網格,替換了之前2x2的頂點。這樣頂點之間的插值將會看起來更加平滑。 232 | 233 | ![](http://qmlbook.org/_images/geniesmoothbending.png) 234 | 235 | 你可以看見曲線的變化,在最後讓彎曲變得非常平滑。這讓彎曲有了更加強大的效果。 236 | 237 | **側面收縮(Choosing Sides)** 238 | 239 | 最後一個增強,我們希望能夠收縮邊界。邊界朝著吸收的點消失。直到現在它總是在朝著width值的點消失。添加一個邊界屬性,我們能夠修改這個點在0到width之間。 240 | 241 | ``` 242 | ShaderEffect { 243 | ... 244 | property real side: 0.5 245 | 246 | vertexShader: " 247 | ... 248 | uniform highp float side; 249 | ... 250 | pos.x = mix(qt_Vertex.x, side * width, t * bend); 251 | " 252 | } 253 | ``` 254 | 255 | ![](http://qmlbook.org/_images/geniehalfside.png) 256 | 257 | **包裝(Packing)** 258 | 259 | 最後將我們的效果包裝起來。將我們吸收效果的代碼提取到一個叫做GenieEffect的自定義組件中。它使用ShaderEffect作為根元素。移除掉MouseArea,這不應該放在組件中。綁定minimized屬性來觸發效果。 260 | 261 | ``` 262 | import QtQuick 2.0 263 | 264 | ShaderEffect { 265 | id: genieEffect 266 | width: 160; height: width 267 | anchors.centerIn: parent 268 | property variant source 269 | mesh: GridMesh { resolution: Qt.size(10, 10) } 270 | property real minimize: 0.0 271 | property real bend: 0.0 272 | property bool minimized: false 273 | property real side: 1.0 274 | 275 | 276 | ParallelAnimation { 277 | id: animMinimize 278 | running: genieEffect.minimized 279 | SequentialAnimation { 280 | PauseAnimation { duration: 300 } 281 | NumberAnimation { 282 | target: genieEffect; property: 'minimize'; 283 | to: 1; duration: 700; 284 | easing.type: Easing.InOutSine 285 | } 286 | PauseAnimation { duration: 1000 } 287 | } 288 | SequentialAnimation { 289 | NumberAnimation { 290 | target: genieEffect; property: 'bend' 291 | to: 1; duration: 700; 292 | easing.type: Easing.InOutSine } 293 | PauseAnimation { duration: 1300 } 294 | } 295 | } 296 | 297 | ParallelAnimation { 298 | id: animNormalize 299 | running: !genieEffect.minimized 300 | SequentialAnimation { 301 | NumberAnimation { 302 | target: genieEffect; property: 'minimize'; 303 | to: 0; duration: 700; 304 | easing.type: Easing.InOutSine 305 | } 306 | PauseAnimation { duration: 1300 } 307 | } 308 | SequentialAnimation { 309 | PauseAnimation { duration: 300 } 310 | NumberAnimation { 311 | target: genieEffect; property: 'bend' 312 | to: 0; duration: 700; 313 | easing.type: Easing.InOutSine } 314 | PauseAnimation { duration: 1000 } 315 | } 316 | } 317 | 318 | vertexShader: " 319 | uniform highp mat4 qt_Matrix; 320 | attribute highp vec4 qt_Vertex; 321 | attribute highp vec2 qt_MultiTexCoord0; 322 | uniform highp float height; 323 | uniform highp float width; 324 | uniform highp float minimize; 325 | uniform highp float bend; 326 | uniform highp float side; 327 | varying highp vec2 qt_TexCoord0; 328 | void main() { 329 | qt_TexCoord0 = qt_MultiTexCoord0; 330 | highp vec4 pos = qt_Vertex; 331 | pos.y = mix(qt_Vertex.y, height, minimize); 332 | highp float t = pos.y / height; 333 | t = (3.0 - 2.0 * t) * t * t; 334 | pos.x = mix(qt_Vertex.x, side * width, t * bend); 335 | gl_Position = qt_Matrix * pos; 336 | }" 337 | } 338 | ``` 339 | 340 | 你現在可以像這樣簡單的使用這個效果: 341 | 342 | ``` 343 | import QtQuick 2.0 344 | 345 | Rectangle { 346 | width: 480; height: 240 347 | color: '#1e1e1e' 348 | 349 | GenieEffect { 350 | source: Image { source: 'assets/lighthouse.jpg' } 351 | MouseArea { 352 | anchors.fill: parent 353 | onClicked: parent.minimized = !parent.minimized 354 | } 355 | } 356 | } 357 | ``` 358 | 359 | 我們簡化了代碼,移除了背景矩形框,直接使用圖片完成效果,替換了在一個單獨的圖像元素中加載它。 360 | --------------------------------------------------------------------------------