├── other ├── book_format.md ├── enginsh_to_chinses.md ├── README.md ├── 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 ├── assets.md └── collaboration_correction.md ├── qt_and_c++ ├── advanced_techniques.md ├── common_qt_classes.md ├── build_systems.md ├── models_in_c++.md ├── README.md ├── file_io.md ├── a_simple_model.md ├── cmake.md ├── associative_containers.md ├── sequential_containers.md ├── qmake.md ├── the_qobject.md ├── qstring.md ├── more_complex_data.md └── a_boilerplate_application.md ├── 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 ├── 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 ├── storage ├── other_storage_apis.md ├── README.md ├── settings.md └── local_storage_-_sql.md ├── extending_qml_with_c++ ├── README.md ├── the_application_window.md ├── reading_data.md ├── summary.md ├── writing_data.md ├── using_fileio.md ├── using_actions.md ├── formatting_the_table.md ├── finishing_touch.md ├── fileio_implementation.md ├── plugin_content.md ├── creating_the_plugin.md └── understanding_the_qml_run-time.md ├── multimedia ├── summary.md ├── README.md ├── video_streams.md ├── sounds_effects.md ├── advanced_techniques.md ├── capturing_images.md └── playing_media.md ├── dynamic_qml ├── README.md ├── creating_and_destroying_objects.md ├── summary.md ├── managing_dynamically_created_elements.md ├── binding_indirectly.md ├── dynamically_instantiating_items_from_text.md ├── dynamically_loading_and_instantiating_items.md ├── loading_components_dynamically.md ├── connecting_indirectly.md └── tracking_dynamic_objects.md ├── meet_qt_5 ├── README.md ├── qt_project.md ├── preface.md ├── qt_building_blocks.md └── qt5_introduction.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 ├── model-view-delegate ├── README.md ├── summary.md ├── concept.md └── basic_model.md ├── shader_effect ├── openglopengl_shader.md ├── README.md ├── wave_effect.md ├── qtqt_graphicseffect_library.md ├── curtain_effect.md ├── shader_elements.md └── fragement_shader.md ├── javascript ├── browserhtml_vs_qtquickqml.md ├── README.md ├── js_objects.md ├── the_language.md └── creating_a_js_console.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 ├── assets └── preface.md ├── httpuiserving_ui_via_http.md └── README.md /other/book_format.md: -------------------------------------------------------------------------------- 1 | # 格式定义 2 | -------------------------------------------------------------------------------- /other/enginsh_to_chinses.md: -------------------------------------------------------------------------------- 1 | # 术语英汉对照表 2 | -------------------------------------------------------------------------------- /other/README.md: -------------------------------------------------------------------------------- 1 | # 其它(Other) 2 | 3 | 该章节介绍了一些其它相关的内容,原文内不存在本章节。 4 | -------------------------------------------------------------------------------- /qt_and_c++/advanced_techniques.md: -------------------------------------------------------------------------------- 1 | # 进阶技巧(Advanced Techniques) 2 | -------------------------------------------------------------------------------- /fluid_elements/advanced_techniques.md: -------------------------------------------------------------------------------- 1 | # 高级用法(Advanced Techniques) 2 | 3 | 后续添加。 4 | -------------------------------------------------------------------------------- /quick_starter/advanced_techniques.md: -------------------------------------------------------------------------------- 1 | # 高级用法(Advanced Techniques) 2 | 3 | 后续添加。 4 | -------------------------------------------------------------------------------- /other/process_images/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/1.PNG -------------------------------------------------------------------------------- /other/process_images/10.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/10.PNG -------------------------------------------------------------------------------- /other/process_images/11.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/11.PNG -------------------------------------------------------------------------------- /other/process_images/12.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/12.PNG -------------------------------------------------------------------------------- /other/process_images/13.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/13.PNG -------------------------------------------------------------------------------- /other/process_images/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/2.PNG -------------------------------------------------------------------------------- /other/process_images/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/3.PNG -------------------------------------------------------------------------------- /other/process_images/4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/4.PNG -------------------------------------------------------------------------------- /other/process_images/5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/5.PNG -------------------------------------------------------------------------------- /other/process_images/6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/6.PNG -------------------------------------------------------------------------------- /other/process_images/7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/7.PNG -------------------------------------------------------------------------------- /other/process_images/8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/8.PNG -------------------------------------------------------------------------------- /other/process_images/9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwc1987/QmlBook-In-Chinese/HEAD/other/process_images/9.PNG -------------------------------------------------------------------------------- /networking/summary.md: -------------------------------------------------------------------------------- 1 | # 总结(Summary) 2 | 3 | 这章我们讨论了关于QML的网络应用。请记住Qt已在本地端提供了丰富的网络接口可以在QML中使用。但是这一章的我们是想推动QML的网络运用和如何与云服务集成。 4 | -------------------------------------------------------------------------------- /storage/other_storage_apis.md: -------------------------------------------------------------------------------- 1 | # 其它存储接口(Other Storage APIs) 2 | 3 | 直接从QML中存储信息,上面的这些方法是主要存储方法。事实上QtQuick最有效的存储方法是使用C++扩展接口调用本地存储系统或者类似Qt云存储使用网络编程接口调用远程存储系统。 4 | -------------------------------------------------------------------------------- /extending_qml_with_c++/README.md: -------------------------------------------------------------------------------- 1 | # C++扩展QML(Extending QML with C++) 2 | 3 | QML执行在受限的空间中,QML作为一种语言提供的功能有时是被限制的。通过C++写的本地函数可以扩展QML运行时的功能。应用程序可以充分利用基础平台的性能和自由度。 4 | 5 | 6 | -------------------------------------------------------------------------------- /multimedia/summary.md: -------------------------------------------------------------------------------- 1 | # 总结(Summary) 2 | 3 | Qt的媒体应用程序接口提供了播放和捕捉视频和音频的机制。通过VideoOutput元素,视频源能够在我们的用户界面上显示。通过MediaPlayer元素,可以操作大多数的播放,SoundEffect被用于低延迟的声音。Camera元素被用来截图或者显示一个实时的视频流。 4 | -------------------------------------------------------------------------------- /dynamic_qml/README.md: -------------------------------------------------------------------------------- 1 | # 动态QML(Dynamic QML) 2 | 3 | 到现在,我们已经将QML作为一个工具用来构造静态场景和静态场景的导航。根据不同的状态和逻辑规则,一个实时动态的用户界面已经被创建。通过使用QML和JavaScript以更加动态的方式,进一步的扩大灵活性。组件可以在运行时加载和实例化,元素能够被销毁。动态创建的用户界面能够被存储在磁盘上,并且恢复。 4 | -------------------------------------------------------------------------------- /meet_qt_5/README.md: -------------------------------------------------------------------------------- 1 | # Qt5概述 2 | 3 | 教程将介绍使用Qt5.x版本开发应用程序的相关技术。教程更侧重讲解新的Qt Quick开发技巧,在讲解Qt Quick扩展内容时会涉及部分Qt C++内容。 4 | 5 | 本章是对Qt5的概述,通过一个Qt5的应用程序示例展示Qt5中一种新的开发模式。此外,本章旨在全面概述Qt5,以及如何与Qt5的开发者取得联系。 6 | 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /qt_and_c++/common_qt_classes.md: -------------------------------------------------------------------------------- 1 | # Qt通用类(Common Qt Classes) 2 | 3 | 类```QObject```组成了Qt的基础,但是在这个框架里还有很多的类。在我们继续探寻如何扩展QML之前,我们需要先了解一些有用的Qt基础类。 4 | 5 | 在这一节中的示例代码需要使用Qt Test库。它提供一种非常好的方法来测试Qt的API并将其存储供以后参考使用。测试库提供的```QVERIFY```与```QCOMPARE```函数断言一个正确条件。我们也将使用域来避免名称校验冲突。所以不要对后面的代码有困惑。 6 | 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /quick_starter/README.md: -------------------------------------------------------------------------------- 1 | # QML快速入门(Quick Starter) 2 | 3 | **注意** 4 | 5 | **最后一次构建:2014年1月20日下午18:00。** 6 | 7 | **这章的源代码能够在[assetts folder](http://qmlbook.org/assets)找到。** 8 | 9 | 这章概述了QML语言,Qt5中大量使用了这种声明用户界面的语言。我们将会讨论QML语言,一个树形结构的元素,跟着是一些最基本的元素概述。然后我们会简短的介绍怎样创建我们自己的元素,这些元素被叫做组件,并如何使用属性操作来转换元素。最后我们会介绍如何对元素进行布局,如何向用户提供输入。 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /storage/README.md: -------------------------------------------------------------------------------- 1 | # 存储(Storage) 2 | 3 | 本章将介绍在Qt5中使用QtQuick存储数据。QtQuick只提供了有限的方法来直接存储本地数据。在这样的场景下,它更多的扮演了一个浏览者的角色。在大多数项目中,存储数据由C++后端来完成,并需要将这个功能导入到QtQuick前端。QtQucik没有提供类似Qt C++的主机文件系统接口来读取和写入文件。所以后端工程师需要编写一个这样的插件或者使用网络通道与本地服务器通信来提供这些功能。 4 | 5 | 每个应用程序都需要持续的存储少量或者大量的信息。可以存储在本地文件系统或者远程服务器上。一些信息将会被结构化、简单化例如程序配置信息,一些信息将会巨大并且复杂例如文档文件,一些信息将会巨大并且结构化需要与某种数据库连接。在这章我们将会讨论如何使用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 | -------------------------------------------------------------------------------- /dynamic_qml/creating_and_destroying_objects.md: -------------------------------------------------------------------------------- 1 | # 创建与销毁对象(Creating and Destroying Objects) 2 | 3 | 加载元素使得动态填充用户界面成为可能。但是接口的结构仍然是静态的。通过JavaScript可以更近一步的完成QML元素的动态实例化。 4 | 5 | 在我们深入讨论动态创建元素的细节之前,我们需要明白工作的流程。当从一个文件或者网络加载一块QML时,组件已经被创建。组件封装了解释执行的QML代码用来创建项。这意味着一块QML代码和实例化项是分为两个步骤进行的。首先在组件中解释执行QML代码,然后组件被用来实例化创建项对象。 6 | 7 | 除了从存储在文件或者服务器上的QML代码创建元素,也可以直接从包含QML代码的文本字符串中创建QML对象。动态创建项也类似的方式再处理一次就可以了。 8 | 9 | -------------------------------------------------------------------------------- /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/qt_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集成开发环境(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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dynamic_qml/summary.md: -------------------------------------------------------------------------------- 1 | # 总结(Summary) 2 | 3 | 在这一章中,我们主要讨论了动态创建QML元素。折让我们可以自由的创建QML场景,了解了用户可配置与插件结构。 4 | 5 | 动态加载一个QML元素最简单的方法是使用加载元素(Loader element)。它可以作为一个占位符内容被加载。 6 | 7 | 使用一种更加动态的方法,Qt.createQmlObject方法可以用于实例化QML字符串。然后这种方法有局限性。最全面的解决方案是动态创建使用Qt.createComponent函数创建组件。然后通过调用组件的createObject函数来创建对象。 8 | 9 | 由于绑定与信号连接依赖于对象id,或者访问实例化对象。对于动态创建的对象需要另外一种方法,为了创建绑定,需要使用绑定元素(Binding element),连接元素(Connections element)使得与动态创建对象连接信号成为可能。 10 | 11 | 对于动态创建项,最大的挑战是跟踪它们。可以使用链表模型(ListModel)来完成这件事。有了一个模型用来跟踪动态创建项,可以实现序列化和反序列化函数,可以存储和恢复动态创建场景。 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /extending_qml_with_c++/the_application_window.md: -------------------------------------------------------------------------------- 1 | # 应用程序窗口(The Application Window) 2 | 3 | 使用Qt Creator的QtQuick Application向导创建一个基于QtQuick controls的应用程序。我们将不再使用新的QML格式,这在一本书里面将很难解释,即使新格式使用ui.qml文件将比之前更加容易达到目的。所以你可以移除/删除格式文件。 4 | 5 | 一个应用程序窗口基础配置包含了一个工具栏,菜单栏和状态栏。我们只使用菜单栏创建一些典型的菜单条目来打开和保存文档。基础配置的窗口只会显示一个空的窗口。 6 | 7 | ``` 8 | import QtQuick 2.4 9 | import QtQuick.Controls 1.3 10 | import QtQuick.Window 2.2 11 | import QtQuick.Dialogs 1.2 12 | 13 | ApplicationWindow { 14 | id: root 15 | title: qsTr("City UI") 16 | width: 640 17 | height: 480 18 | visible: true 19 | } 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dynamic_qml/managing_dynamically_created_elements.md: -------------------------------------------------------------------------------- 1 | # 管理动态创建的元素(Managing Dynamically Created Elements) 2 | 3 | 在QML场景下,动态创建的对象可以像其它的对象一样处理。然而,也有一些缺陷需要处理。最重要的是创建环境的概念。 4 | 5 | 一个动态创建对象的创建环境是它被创建时的环境。这与它的父对象所在的环境不一定相同。当创建环境被销毁,会影响涉及绑定属性的对象。这意味着在对象的整个生命周期,在代码的一个地方实现动态对象创建是非常重要的。 6 | 7 | 动态创建的对象也可以动态销毁。当这样做时,有一个法则:永远不要尝试销毁一个你没有创建的对象。这也包括你已经创建的元素,但不要使用动态机制比如Component.createObject或者createQmlObject。 8 | 9 | 对象的销毁依赖于它的析构函数被调用。这个函数接收一个可选参数用于指定这个对象还可以存在多少毫秒后被销毁。这是非常有用的,例如让对象完成一个完整的过渡。 10 | 11 | ``` 12 | item = Qt.createQmlObject(...); 13 | ... 14 | item.destroy(); 15 | ``` 16 | 17 | **注意** 18 | 19 | **可以从一个对象内部实现销毁,例如创建一个可以自销毁的弹出窗口。** 20 | 21 | 22 | -------------------------------------------------------------------------------- /javascript/browserhtml_vs_qtquickqml.md: -------------------------------------------------------------------------------- 1 | # 浏览器/HTML与QtQuick/QML对比(Browser/HTML vs QtQuick/QML) 2 | 3 | 浏览器在运行时渲染HTML,执行HTML中相关的JavaScript。现今的web应用中相对于HTML包含了更多的JavaScript。浏览器中JavaScript运行在一些浏览器附加的标准ECMAScript环境。一个典型的浏览器中的JS环境知道访问浏览器窗口的窗口对象。也简单的基于JQuery的DOM选择器来提供CSS选择器。额外使用setTimeout函数在超时时调用函数。除了这些,JS存在于一个标准的JavaScript环境,类似于QML/JS。 4 | 5 | 不同的是JS出现在HTML与QML中的方式。在HTML中,你只能在事件操作(event handlers),例如页面加载(page loaded),鼠标点击(mouse pressed)中添加JS。例如通常在页面加载中初始化你的JS,这在QML中与组件加载完成(Component.onCompleted)类似。例如你不能使用JS来绑定属性(至少不是直接绑定,AngularJS增强了DOM树允许这种操作,但这和典型HTML相去甚远)。 6 | 7 | 所以在QML中JS是一种更加优秀的语言,并且与QML的渲染树高度集成。使得语言更具有可读性。除了这些,开发过HTML/JS应用程序的人会觉得在QML/JS中开发非常容易上手。 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /extending_qml_with_c++/reading_data.md: -------------------------------------------------------------------------------- 1 | # 读取数据(Reading Data) 2 | 3 | 我们让打开动作打开一个文件对话框。当用户已选择一个文件后,在文件对话框上的```onAccepted```方法被调用。这里我们调用```readDocument()```函数。```readDocument```函数将来自文件对话框的地址设置到我们的```FileIO```对象,并调用```read()```方法。从```FileIO```中加载的文本使用```JSON.parse()```方法解析,并将结果对象作为数据模型直接设置到表格视图上。这样非常方便。 4 | 5 | ``` 6 | Action { 7 | id: open 8 | ... 9 | onTriggered: { 10 | openDialog.open() 11 | } 12 | } 13 | 14 | ... 15 | 16 | FileDialog { 17 | id: openDialog 18 | onAccepted: { 19 | root.readDocument() 20 | } 21 | } 22 | 23 | function readDocument() { 24 | io.source = openDialog.fileUrl 25 | io.read() 26 | view.model = JSON.parse(io.text) 27 | } 28 | 29 | 30 | FileIO { 31 | id: io 32 | } 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /extending_qml_with_c++/summary.md: -------------------------------------------------------------------------------- 1 | # 总结(Summary) 2 | 3 | 插件的创建非常简单,但是它可以复用,并且为不同的应用程序扩展类型。使用创建的插件是非常灵活的解决方案。例如你可以只使用```qmlscene```开始创建UI。打开CityUI项目文件夹,从```qmlscene```的```main.qml```开始。我真的鼓励大家使用```与qmlscene```一起工作的方式写应用程序。对于UI开发者,这将是一个巨大的改变,也是一个好的习惯来保证清晰的分离。 4 | 5 | 使用插件有一个缺点,对于简单的应用程序开发增加了难度。你需要为你的应用程序开发插件。如果这是一个问题,你也可以使用与```FileIO```对象相同的机制使用```qmlRegisterType```直接注册到你的```main.cpp```中。QML代码保持一样就可以了。 6 | 7 | 通常在大型项目中,你不会像这样使用应用程序。你有一个与```qmlscene```类似的简单的qml运行环境,并且需要所有本地的功能插件。你的项目使用这些qml扩展插件,也是简单纯粹的qml项目。这为UI的变换提供了最大的灵活性并移除了编译步骤。在编辑完成一个QML文件后,你只需要运行UI。这允许用户界面开发者保持灵活性并迅速的使所有的小修改立刻得到响应。 8 | 9 | 插件提供了健壮和清晰的C++后台开发与QML前端开发的分离。当开发QML插件时,通常在QML端有一个想法,并在使用C+=实现前,可以使用QML的样本模型进行API验证。如果API是C++人员写的,通常会犹豫去改变它或者重写它。复制一个QML提供的API通常更加灵活并且初始投资更少。当使用插件切换一个样本模型API和一个真是API时,仅仅只需要改变qml运行环境的导入路径。 10 | -------------------------------------------------------------------------------- /extending_qml_with_c++/writing_data.md: -------------------------------------------------------------------------------- 1 | # 写入数据(Writing Data) 2 | 3 | 我们连接保存动作到```saveDocument()```函数来保存文档。保存文档函数从视图中取出模型,模型是一个JS对象,并使用```JSON.stringify()```函数将它转换为一个字符串。将结果字符串设置到```FileIO```对象的文本属性中,并调用```write()```来保存数据到磁盘中。在```stringify```函数上参数```null```和```4```将会使用4个空格缩进格式化JSON数据结果。这只是为了保存文档更好阅读。 4 | 5 | ``` 6 | Action { 7 | id: save 8 | ... 9 | onTriggered: { 10 | saveDocument() 11 | } 12 | } 13 | 14 | function saveDocument() { 15 | var data = view.model 16 | io.text = JSON.stringify(data, null, 4) 17 | io.write() 18 | } 19 | 20 | FileIO { 21 | id: io 22 | } 23 | ``` 24 | 25 | 从根本上说,这个应用程序就是读取,写入和现实一个JSON文档。考虑下如果使用XML格式读取和写入,会花多少时间。使用JSON格式你只需要读取/写入一个文本文件或者发送/接收一个文本缓存。 26 | 27 | ![](http://qmlbook.github.io/_images/cityui_table.png) 28 | -------------------------------------------------------------------------------- /extending_qml_with_c++/using_fileio.md: -------------------------------------------------------------------------------- 1 | # 使用FileIO(Using FileIO) 2 | 3 | 现在我们可以使用新创建的文件访问一些简单的数据。这个例子中我们想要读取一个JSON格式下的城市数据并且在表格中显示。我们将使用两个项目,一个是扩展插件项目(叫做```fileio```),提供读取和写入文件的方法。另外一个项目通过fileio读取/写入文件将数据显示在表格中(```CityUI```)。这个例子中使用的数据在```cities.json```文件中。 4 | 5 | ![](http://qmlbook.github.io/_images/cityui_mock.png) 6 | 7 | JSON只是文本,它被格式化为可以转换为一个有效的JS对象/数组并返回一个文本。我们使用```FileIO```读取格式化的JSON数据并使用```JSON.parse()```将它转换为一个JS对象。数据在后面被用作一个表格视图的数据模型。我们粗略的阅读函数文档就可以获取这些内容。为了保存数据我们将转换回文本格式并使用写入函数保存。 8 | 9 | 城市的JSON数据是一个格式化文本文件,包含了一组城市数据条目,每个条目包含了关于城市数据。 10 | 11 | ``` 12 | [ 13 | { 14 | "area": "1928", 15 | "city": "Shanghai", 16 | "country": "China", 17 | "flag": "22px-Flag_of_the_People's_Republic_of_China.svg.png", 18 | "population": "13831900" 19 | }, 20 | ... 21 | ] 22 | ``` 23 | -------------------------------------------------------------------------------- /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 | # 1.1 序 2 | 3 | Qt**历史** 4 | 5 | Qt4自2005年发布已为成千上万的应用程序甚至桌面操作系统、移动操作系统提供了稳定、可靠的开发框架。计算机用户的使用模式近年发生了变化,用户正在从传统PC转向笔记本电脑或智能手机。传统PC被越来越多的触摸屏设备取代,计算机的用户体验模型也在跟随改变。在这之前Windows UI占据了我们的世界,但现在我们会花更多的时间使用其它的UI语言开发便携式设备用户界面。 6 | 7 | Qt4的设计用于满足开发者在主流桌面操作系统上有一套表现一致的窗口组件可以使用。如今Qt的使用者面临了新的问题,他们需要提供可触碰交互的用户界面以满足软件界面需求,并在主流桌面操作系统和移动操作系统上实现这些界面。从Qt4.7版本开始引进了Qt Quick,它让Qt的使用者可以用简单的元素对象创建一套界面组件,并通过组合界面组件的方式来完成软件界面需求。 8 | 9 | ## 1.1.1 Qt5与Qt4 10 | 11 | Qt5是Qt4版本完整的更新。自Qt4.8版本发布,Qt4已经发布了7年,现在这个工具将会更加令人惊奇。 12 | 13 | Qt5主要特性: 14 | 15 | * 出色的图形能力:Qt Quick2基于OpenGL\(ES\)场景实现,重写的图形堆栈让开发者可以轻松实现图形特效。 16 | 17 | * 高效的开发模式:使用QML和JavaScript创建用户界面,后端使用C++处理数据。前后端的分离让前端开发人员可以快速迭代并专注于用户界面开发,后端的C++开发人员则专注于软件的稳定性、高性能和扩展能力。 18 | 19 | * 跨平台能力:基于Qt平台的统一抽象实现,能够方便地将Qt移植到大多数操作系统平台。Qt5由基础模块和附加模块组成,操作系统开发者只需移植基础模块就可以保证Qt最小运行环境。 20 | 21 | * 开源:Qt是由[qt.io](http://qt.io/)主导的开源项目,由社区驱动开发。 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /assets/preface.md: -------------------------------------------------------------------------------- 1 | ## 序 2 | 3 | **历史** 4 | 5 | Qt4自2005年发布至今,已为成千上万的应用程序,甚至包括台式机操作系统和移动操作系统提供了坚实的基础。计算机用户的使用模式近年来发生了变化,用户正在从传统台式机转向笔记本电脑或智能手机。传统台式机设备被越来越多的触摸屏设备取代,台式机的用户体验模型也在跟随改变。在过去,Window UI占据了我们的世界,但现在我们会花更多的时间在其它的UI语言上用于适配移动端的用户体验。 6 | 7 | Qt4旨在满足开发者可以在主流平台桌面系统上有一套表现一致的用户界面窗口。Qt的用户如今面临的问题也在改变,他们需要提供可触摸交互的用户界面以满足软件客户的用户界面需求,并在主流的桌面系统和移动系统上实现这些界面。Qt4.7开始引进Qt Quick技术,该技术让使用者用简单的元素创建一套用户界面组件,通过组合这些用户界面组件来完成软件用户的界面需求。 8 | 9 | ### 1.1.1 Qt5优势 10 | 11 | Qt5是Qt4的更新升级,Qt4版本至Qt4.8已经演进了7年,是时候让这个不可思议的工具更加强大了。 12 | 13 | Qt5主要优势如下: 14 | 15 | - 图形处理:Qt Quick2是基于OpenGL(ES)场景实现,重新实现了图形堆栈,并让开发者可以更方便地实现新的图形效果。 16 | - 生产效率:QML和JavaScript语言是创建用户界面的主要手段,后端则由C++来驱动。JavaScript与C++的分割让前端开发人员可以快速迭代并专注于创建漂亮的用户界面,而后端的C++开发人员则专注于软件的稳定性、高性能和扩展能力。 17 | - 可移植性:基于Qt跨平台性质,能更方便地将Qt移植到更多的平台上。Qt5提出了基础模块和附加模块的概念,操作系统开发者只需要专注于基础模块的移植就能使Qt正常运行。 18 | - 开源化:Qt是由Qt-Porject(qt-project.org)主持的开源项目,它的开发由Qt社区驱动的。 19 | -------------------------------------------------------------------------------- /qt_and_c++/build_systems.md: -------------------------------------------------------------------------------- 1 | # 编译系统(Build Systems) 2 | 3 | 在不同的平台上稳定的编译软件是一个复杂的任务。你将会遇到不同环境下的不同编译器,路径和库变量的问题。Qt的目的是防止应用开发者遭遇这些跨平台问题。为了完成这个任务,Qt引进了```qmake```编译文件生成器。```qmake```操作以```.pro``` 4 | 结尾的项目文件。这个项目文件包含了关于应用程序的说明和需要读取的资源文件。用qmake执行这个项目文件会为你生成一个在unix和mac的```Makefile``` 5 | ,如果在windows下使用mingw编译工具链也会生成。否则可能会创建一个visual studio项目或者一个xcode项目。 6 | 7 | 在unix下使用Qt编译如下: 8 | 9 | ``` 10 | $ edit myproject.pro 11 | $ qmake // generates Makefile 12 | $ make 13 | ``` 14 | 15 | Qt也允许你使用影子编译。影子编译会在你的源码位置外的路径进行编译。假设我们有一个myproject文件夹,里面有一个myproject.pro文件。如下输入命令: 16 | 17 | ``` 18 | $ mkdir build 19 | $ cd build 20 | $ qmake ../myproject/myproject.pro 21 | ``` 22 | 23 | 我们创建一个编译文件夹并且在这个编译文件中使用qmake指向我们项目文件夹中的项目文件。这将配置makefile使用编译文件夹替代我们的源代码文件夹来存放所有的编译中间件和结果。这允许我们同时为不同的qt版本和编译配置创建不同的编译文件夹并且不会弄乱我们的源代码文件夹。 24 | 25 | 当你使用Qt Creator时,它会在后代为你做这些事情,通常你不在需要担心这些步骤。对于比较大的项目,建议使用命令行方式来编译你的Qt项目可以更加深入的了解编译流。 26 | 27 | -------------------------------------------------------------------------------- /dynamic_qml/binding_indirectly.md: -------------------------------------------------------------------------------- 1 | # (间接绑定)Binding Indirectly 2 | 3 | 与无法直接连接动态创建元素的信号类似,也无法脱离桥接元素(bridge element)与动态创建元素绑定属性。为了绑定任意元素的属性,包括动态创建元素,需要使用绑定元素(Binding element)。 4 | 5 | 绑定元素(Bindging element)允许你指定一个目标元素(target element),一个属性用来绑定,一个值用来绑定这个属性。通过使用绑定元素(Binding elelemt),例如,绑定一个动态加载元素(dynamically loaded element)的属性。在这个章节中有个入门实例如下所示。 6 | 7 | ``` 8 | Loader { 9 | id: dialLoader 10 | 11 | anchors.left: parent.left 12 | anchors.right: parent.right 13 | anchors.top: parent.top 14 | anchors.bottom: analogButton.top 15 | 16 | onLoaded: { 17 | binder.target = dialLoader.item; 18 | } 19 | } 20 | Binding { 21 | id: binder 22 | 23 | property: "speed" 24 | value: speed 25 | } 26 | ``` 27 | 28 | 通常不会设置一个绑定的目标元素,或者不会有一个给定的属性。当绑定激活时使用绑定元素的属性来限制时间。例如,它可以用来限制用户界面的特定模式。 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /extending_qml_with_c++/using_actions.md: -------------------------------------------------------------------------------- 1 | # 使用动作(Using Actions) 2 | 3 | 为了更好的使用/复用我们的命令,我们使用QML```Action```类型。这将允许我们在后面可以使用相同的动作,也可以用于潜在的工具栏。打开,保存和退出动作是标准动作。打开和保存动作不会包含任何逻辑,我们后面再来添加。菜单栏由一个文件菜单和这三个动作条目组成。此外我们已经准备了一个文件对话框,它可以让我们选择我们的城市文档。对话框在定义时是不可见的,需要使用```open()```方法来显示它。 4 | 5 | ``` 6 | ... 7 | Action { 8 | id: save 9 | text: qsTr("&Save") 10 | shortcut: StandardKey.Save 11 | onTriggered: { } 12 | } 13 | 14 | Action { 15 | id: open 16 | text: qsTr("&Open") 17 | shortcut: StandardKey.Open 18 | onTriggered: {} 19 | } 20 | 21 | Action { 22 | id: exit 23 | text: qsTr("E&xit") 24 | onTriggered: Qt.quit(); 25 | } 26 | 27 | menuBar: MenuBar { 28 | Menu { 29 | title: qsTr("&File") 30 | MenuItem { action: open } 31 | MenuItem { action: save } 32 | MenuSeparator { } 33 | MenuItem { action: exit } 34 | } 35 | } 36 | 37 | ... 38 | 39 | FileDialog { 40 | id: openDialog 41 | onAccepted: { } 42 | } 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /dynamic_qml/dynamically_instantiating_items_from_text.md: -------------------------------------------------------------------------------- 1 | # 从文本中动态实例化项(Dynamically Instantiating Items from Text) 2 | 3 | 有时,可以很方便的从QML文本字符串中实例化一个对象。别的不说,这比将代码从源文件中分离后拿出来快。为了实现这个功能,需要使用Qt.createQmlObject函数。 4 | 5 | 这个函数接受三个参数:qml,parent和filepath。qml参数包含了用来实例化的QML代码字符串。parent参数为新创建的对象提供了一个父对象。filepath参数用于存储创建对象时的错误报告。这个函数的结果返回一个新的对象或者一个NULL。 6 | 7 | **警告** 8 | 9 | **createQmlObject函数通常会立即返回结果。为了成功调用这个函数,所有的依赖调用需要保证已经被加载。这意味着如果函数调用了未加载的组件,这个调用就会失败并且返回null。为了更好的处理这个问题,必须使用createComponent/createObject方法。** 10 | 11 | 使用Qt.createQmlObject函数创建对象与其它的动态创建对象类似。这说明与其它创建的QML对象一样,也没有id。在下面的例子中,当根元素创建完成后,从内联QML代码中实例化了一个新的矩形元素(Rectangle element)。 12 | 13 | ``` 14 | import QtQuick 2.0 15 | 16 | Item { 17 | id: root 18 | 19 | width: 1024 20 | height: 600 21 | 22 | function createItem() { 23 | Qt.createQmlObject("import QtQuick 2.0; Rectangle { x: 100; y: 100; width: 100; 24 | height:100; color: \"blue\" }", root, "dynamicItem"); 25 | } 26 | 27 | Component.onCompleted: root.createItem(); 28 | } 29 | ``` 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /extending_qml_with_c++/formatting_the_table.md: -------------------------------------------------------------------------------- 1 | # 格式化表格(Formatting the Table) 2 | 3 | 城市数据的内容应该被现实在一个表格中。我们使用```TableView``` 4 | 控制并定义4列:城市,国家,面积,人口。每一列都是典型的```TableViewColumn```。然后我们添加列的标识并移除要求自定义列代理的操作。 5 | 6 | ``` 7 | TableView { 8 | id: view 9 | anchors.fill: parent 10 | TableViewColumn { 11 | role: 'city' 12 | title: "City" 13 | width: 120 14 | } 15 | TableViewColumn { 16 | role: 'country' 17 | title: "Country" 18 | width: 120 19 | } 20 | TableViewColumn { 21 | role: 'area' 22 | title: "Area" 23 | width: 80 24 | } 25 | TableViewColumn { 26 | role: 'population' 27 | title: "Population" 28 | width: 80 29 | } 30 | } 31 | ``` 32 | 33 | 现在应用程序能够显示一个包含文件菜单的菜单栏和一个包含4个表头的空表格。下一步是我们的```FileIO```扩展将有用的数据填充到表格中。 34 | 35 | ![](http://qmlbook.github.io/_images/cityui_empty.png) 36 | 37 | 文档cities.json是一组城市条目。这里是一个例子。 38 | 39 | ``` 40 | [ 41 | { 42 | "area": "1928", 43 | "city": "Shanghai", 44 | "country": "China", 45 | "flag": "22px-Flag_of_the_People's_Republic_of_China.svg.png", 46 | "population": "13831900" 47 | }, 48 | ... 49 | ] 50 | ``` 51 | 52 | 我们任务是允许用户选择文件,读取它,转换它,并将它设置到表格视图中。 53 | -------------------------------------------------------------------------------- /qt_and_c++/models_in_c++.md: -------------------------------------------------------------------------------- 1 | # C++数据模型(Models in C++) 2 | 3 | 在QML中的数据模型为链表视图,路径视图和其它需要为模型中的每个子项创建一个代理引用的视图提供数据。视图只创建可是区域内或者缓冲范围内的引用。这使得即使包含成千上万的子项模型仍然可以保持流畅的用户界面。代理扮演了用来渲染模型子项数据的模板。总之:视图使用代理作为模板来渲染模型中的子项。模型为视图提供数据。 4 | 5 | 当你不想使用C++时,你可以在QML环境中定义模型,你有多重方法为一个视图提供模型。使用C++操作数据或者使用包含了大型数据的C++模型比在QML环境中达到相同目的更加稳定可靠。但是当你值需要少量数据时,QML模型时非常适合的。 6 | 7 | ``` 8 | ListView { 9 | // using a integer as model 10 | model: 5 11 | delegate: Text { text: 'index: ' + index } 12 | } 13 | 14 | ListView { 15 | // using a JS array as model 16 | model: ['A', 'B', 'C', 'D', 'E'] 17 | delegate: Text { 'Char['+ index +']: ' + modelData } 18 | } 19 | 20 | ListView { 21 | // using a dynamic QML ListModel as model 22 | model: ListModel { 23 | ListElement { char: 'A' } 24 | ListElement { char: 'B' } 25 | ListElement { char: 'C' } 26 | ListElement { char: 'D' } 27 | ListElement { char: 'E' } 28 | } 29 | delegate: Text { 'Char['+ index +']: ' + model.char } 30 | } 31 | ``` 32 | 33 | QML视图知道如何操作不同的模型。对于来自C++的模型需要遵循一个特定的协议。这个协议与动态行为一起被定义在一个API(```QAbstractItemModel```)中。这个API时为桌面窗口开发的,它可以灵活的作为一个树的基础或者多列表格或者链表。在QML中我们通常值使用API的链表版本(```QAbstractListModel```)。API包含了一些需要强制实现的函数,另外一些函数时可选的。可选部分通常是用来动态添加或者删除数据。 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /qt_and_c++/README.md: -------------------------------------------------------------------------------- 1 | # Qt and C++ 2 | 3 | Qt是QML与JavaScript的C++扩展工具包。有许多语言与Qt绑定,但是由于Qt是由C++开发的,C++的精神贯穿了整个Qt。在这一节中,我们着重从C++的角度介绍Qt,使用C++开发的本地插件来了解如何更好的扩展QML。通过C++,可以扩展和控制提供给QML的执行环境。 4 | 5 | 这章将讲解Qt,正如Qt所要求的一样,需要读者有一定的C++基础知识。Qt不依赖于先进的C++特性,我认为Qt风格的C++代码可读性非常高,所以不要担心你的C++方面比较差。 6 | 7 | 从C++的角度分析Qt,你会发现Qt通过内省数据的机制实现了许多现代语言的特性。这是通过使用基础类QObject实现的。内省数据,源数据,类运行时的数据维护。原生的C++是不会完成这些事情的。这使得动态查询对象信息,例如它们的属性成为可能。 8 | 9 | Qt使用源对象信息实现了信号与槽的回调绑定。每个信号能够连接任意数量的槽函数或者其它的信号。当一个信号从一个对象实例从发送后,会调用连接信号的槽函数。发送信号的对象不需要知道接收槽对象的任何信息,反之亦然。这一机制可以创建复用性非常高的组件,并减少组件之间的依赖。 10 | 11 | 内省特性也用于创建动态语言的绑定,使得QML可以调用暴露的C++对象实例,并且可以从JavaScript中调用C++函数。除了绑定Qt C++, 绑定标准的JavaScript也是一种非常流行的方式,还有Python的绑定,叫做PyQt。 12 | 13 | 除了这些核心概念,Qt可以使用C++开发跨平台应用程序。Qt C++在不同的操作系统上提供了一套平台抽象,允许开发者专注于手上的任务,不需要你去了解如何在不同的操作系统上打开一个文件。这意味着你可以在Windows,OS X和Linux重复编译相同的代码,由Qt去解决在不同平台上的适配问题。最终保持本地构建的应用程序与目标平台的窗口风格上看起来一致。随着移动平台的桌面更新,Qt也提供相同的代码在不同的移动平台上编译,例如IOS,Android,Jolla,BlackBerry,Ubuntu Phone,Tizen。 14 | 15 | 这样不仅仅是代码可以重用,开发者的技能也可以重用。了解Qt的团队比只专注于单平台特定技能的团队可以接触更多的平台,由于Qt的灵活性,团队可以使用相同的技术创建不同平台的组件。 16 | 17 | ![](http://qmlbook.github.io/_images/yourapplication.png) 18 | 19 | 对于所有平台,Qt提供了一套基本类,例如支持完整unicode编码的字符串,链表容器,向量容器,缓冲容器。它也提供了目标平台的通用主循环抽象和跨平台的线程支持,网络支持。Qt的主旨是为Qt的开发者提供所有必须的功能。对于特定领域的任务,例如本地库接口,Qt也提供了一些帮助类来使得这些操作更加简单。 20 | 21 | 22 | -------------------------------------------------------------------------------- /extending_qml_with_c++/finishing_touch.md: -------------------------------------------------------------------------------- 1 | # 收尾工作(Finishing Touch) 2 | 3 | 这个应用程序还没有真正的完成。我们想要显示旗帜,并允许用户通过从数据模型中移除城市来修改文档。 4 | 5 | 这些旗帜被存放在```main.qml```文件夹下的```flags```文件夹中。为了在表格列中显示它们,我们需要定义一个渲染旗帜图片的代理。 6 | 7 | ``` 8 | TableViewColumn { 9 | delegate: Item { 10 | Image { 11 | anchors.centerIn: parent 12 | source: 'flags/' + styleData.value 13 | } 14 | } 15 | role: 'flag' 16 | title: "Flag" 17 | width: 40 18 | } 19 | ``` 20 | 21 | 它将JS数据模型中暴露的flag属性作为```styleData.value```交给代理。代理调整图片路径,并在路径前面加上```'flags/'```并显示它。 22 | 23 | 对于移除,我们使用相似的技巧来显示一个移除按钮。 24 | 25 | ``` 26 | TableViewColumn { 27 | delegate: Button { 28 | iconSource: "remove.png" 29 | onClicked: { 30 | var data = view.model 31 | data.splice(styleData.row, 1) 32 | view.model = data 33 | } 34 | } 35 | width: 40 36 | } 37 | ``` 38 | 39 | 数据移除操作,我们坚持从视图模型上获取数据,然后使用JS的```splice```函数移除一个条目。这个方法提供给我们的模型来自一个JS数组。```splice```方法通过移除已有元素,添加新的元素来改变数组内容。 40 | 41 | 一个JS数组不如一个Qt模型智能,例如```QAbstractItemModel```,它无法通知视图行更新或者数据更新。由于视图无法接收到任何更新的通知,它无法更新数据显示。只有在我们将数据重新设置回视图时,视图才会知道有新的数据需要刷新视图内容。使用```view.model = data```再次设置数据模型可以让视图知道有数据更新。 42 | 43 | ![](http://qmlbook.github.io/_images/cityui_populated.png) 44 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /other/assets.md: -------------------------------------------------------------------------------- 1 | # 示例源码 2 | 3 | [Chapter 01 examples (ch01-assets.tgz)](http://qmlbook.github.io/assets/ch01-assets.tgz) 4 | 5 | [Chapter 04 examples (ch04-assets.tgz)](http://qmlbook.github.io/assets/ch04-assets.tgz) 6 | 7 | [Chapter 05 examples (ch05-assets.tgz)](http://qmlbook.github.io/assets/ch05-assets.tgz) 8 | 9 | [Chapter 06 examples (ch06-assets.tgz)](http://qmlbook.github.io/assets/ch06-assets.tgz) 10 | 11 | [Chapter 07 examples (ch07-assets.tgz)](http://qmlbook.github.io/assets/ch07-assets.tgz) 12 | 13 | [Chapter 08 examples (ch08-assets.tgz)](http://qmlbook.github.io/assets/ch08-assets.tgz) 14 | 15 | [Chapter 09 examples (ch09-assets.tgz)](http://qmlbook.github.io/assets/ch09-assets.tgz) 16 | 17 | [Chapter 10 examples (ch10-assets.tgz)](http://qmlbook.github.io/assets/ch10-assets.tgz) 18 | 19 | [Chapter 11 examples (ch11-assets.tgz)](http://qmlbook.github.io/assets/ch11-assets.tgz) 20 | 21 | [Chapter 12 examples (ch12-assets.tgz)](http://qmlbook.github.io/assets/ch12-assets.tgz) 22 | 23 | [Chapter 13 examples (ch13-assets.tgz)](http://qmlbook.github.io/assets/ch13-assets.tgz) 24 | 25 | [Chapter 14 examples (ch14-assets.tgz)](http://qmlbook.github.io/assets/ch14-assets.tgz) 26 | 27 | [Chapter 15 examples (ch15-assets.tgz)](http://qmlbook.github.io/assets/ch15-assets.tgz) 28 | 29 | [Chapter 16 examples (ch16-assets.tgz)](http://qmlbook.github.io/assets/ch16-assets.tgz) 30 | -------------------------------------------------------------------------------- /qt_and_c++/file_io.md: -------------------------------------------------------------------------------- 1 | # 文件IO(File IO) 2 | 3 | 通常我们都需要从读写文件。```QFile```是一个```QObject```对象,但是大多数情况下它被创建在栈上。```QFile```包含了通知用户数据可读取信号。它可以异步读取大段的数据,直到整个文件读取完成。为了方便它允许使用阻塞的方式读取数据。这种方法通常用于读取小段数据或者小型文件。幸运的是我们在这些例子中都只使用了小型数据。 4 | 5 | 除了读取文件内容到内存中可以使用```QByteArray```,你也可以根据读取数据类型使用```QDataStream```或者使用```QTextStream```读取unicode字符串。我们现在来看看如何使用。 6 | 7 | ``` 8 | QStringList data({"a", "b", "c"}); 9 | { // write binary files 10 | QFile file("out.bin"); 11 | if(file.open(QIODevice::WriteOnly)) { 12 | QDataStream stream(&file); 13 | stream << data; 14 | } 15 | } 16 | { // read binary file 17 | QFile file("out.bin"); 18 | if(file.open(QIODevice::ReadOnly)) { 19 | QDataStream stream(&file); 20 | QStringList data2; 21 | stream >> data2; 22 | QCOMPARE(data, data2); 23 | } 24 | } 25 | { // write text file 26 | QFile file("out.txt"); 27 | if(file.open(QIODevice::WriteOnly)) { 28 | QTextStream stream(&file); 29 | QString sdata = data.join(","); 30 | stream << sdata; 31 | } 32 | } 33 | { // read text file 34 | QFile file("out.txt"); 35 | if(file.open(QIODevice::ReadOnly)) { 36 | QTextStream stream(&file); 37 | QStringList data2; 38 | QString sdata; 39 | stream >> sdata; 40 | data2 = sdata.split(","); 41 | QCOMPARE(data, data2); 42 | } 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /extending_qml_with_c++/fileio_implementation.md: -------------------------------------------------------------------------------- 1 | # FileIO实现(FileIO Implementation) 2 | 3 | 类```FileIO```实现很简单。记住编程接口我们想要创建的像这样。 4 | 5 | ``` 6 | class FileIO : public QObject { 7 | ... 8 | Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) 9 | Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) 10 | ... 11 | public: 12 | Q_INVOKABLE void read(); 13 | Q_INVOKABLE void write(); 14 | ... 15 | } 16 | ``` 17 | 18 | 我们将保留属性,因为它们是简单的设置者和获取者。 19 | 20 | 读取方法在读取模式下打开一个文件并且使用一个文本流读取数据。 21 | 22 | ``` 23 | void FileIO::read() 24 | { 25 | if(m_source.isEmpty()) { 26 | return; 27 | } 28 | QFile file(m_source.toLocalFile()); 29 | if(!file.exists()) { 30 | qWarning() << "Does not exits: " << m_source.toLocalFile(); 31 | return; 32 | } 33 | if(file.open(QIODevice::ReadOnly)) { 34 | QTextStream stream(&file); 35 | m_text = stream.readAll(); 36 | emit textChanged(m_text); 37 | } 38 | } 39 | ``` 40 | 41 | 当文本变化时,使用```emit textChanged(m_text)```需要通知其它对象这个变化。否则属性绑定无法工作。 42 | 43 | 写入方法相同,但是在写入模式下打开文件,使用文本流写入内容。 44 | 45 | ``` 46 | void FileIO::write() 47 | { 48 | if(m_source.isEmpty()) { 49 | return; 50 | } 51 | QFile file(m_source.toLocalFile()); 52 | if(file.open(QIODevice::WriteOnly)) { 53 | QTextStream stream(&file); 54 | stream << m_text; 55 | } 56 | } 57 | ``` 58 | 59 | 最后不要忘记调用make install。否则你的插件文件不会拷贝到qml文件夹,qml引擎无法定位模块。 60 | 61 | 由于读取和写入会阻塞程序运行,你只能使用FileIO处理小型文本,否则会阻塞Qt的UI线程运行。这里一定要注意! 62 | -------------------------------------------------------------------------------- /extending_qml_with_c++/plugin_content.md: -------------------------------------------------------------------------------- 1 | # 插件内容(Plugin Content) 2 | 3 | 插件是一个已定义接口的库,它只在需要时才被加载。这与一个库在程序启动时被链接和加载不同。在QML场景下,这个接口叫做```QQmlExtensionPlugin```。我们关心其中的两个方法```initializeEngine()```和```registerTypes()```。当插件被加载时,首先会调用```initializeEngine()```,它允许我们访问引擎将插件对象暴露给根上下文。大多数时候你只会使用到```registerTypes()```方法。它允许你注册你自定义的QML类型到引擎提供的地址上。 4 | 5 | 我们稍微退一步考虑一个潜在的文件IO类型,它允许我们在QML中读取/写入一个小型文本文件。第一次的迭代可能看起来像在嘲笑QML的实现。 6 | 7 | ``` 8 | // FileIO.qml (good) 9 | QtObject { 10 | function write(path, text) {}; 11 | function read(path) { return "TEXT"} 12 | } 13 | ``` 14 | 15 | 这是一个纯粹的qml可能的实现,C++基于QML编程接口来探索一些编程接口。我们看到我们有一个读取和写入函数。写入函数需要一个路径和一个文本,读取函数需要一个路径,返回一个文本。路径和文本看起来是公共参数,或许我们可以将它们提取作为属性。 16 | 17 | ``` 18 | // FileIO.qml (better) 19 | QtObject { 20 | property url source 21 | property string text 22 | function write() { // open file and write text }; 23 | function read() { // read file and assign to text }; 24 | } 25 | ``` 26 | 27 | 当然这看起来更像一个QML编程接口。我们使用属性让我们的环境能够绑定我们的属性并且响应变化。 28 | 29 | 在C++中创建这个编程接口我们需要创建类似的一个接口。 30 | 31 | ``` 32 | class FileIO : public QObject { 33 | ... 34 | Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) 35 | Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) 36 | ... 37 | public: 38 | Q_INVOKABLE void read(); 39 | Q_INVOKABLE void write(); 40 | ... 41 | } 42 | ``` 43 | 44 | QML引擎需要注册```FileIO```类型。我们想要在```org.example.io```模块中使用它。 45 | 46 | ``` 47 | import org.example.io 1.0 48 | 49 | FileIO { 50 | } 51 | ``` 52 | 53 | 一个插件可以在相同的模块中暴露若干个类型。但是不能从一个插件中暴露若干个模块。所以模块与插件之间的关系是一对一的。这个关系由模块标识符表示。 54 | -------------------------------------------------------------------------------- /javascript/README.md: -------------------------------------------------------------------------------- 1 | # JavaScript 2 | 3 | JavaScript是web客户端开发的通用语言。基于node js它也开始引导web服务器的开发。因此它使非常适合在声明式QML语言上添加的命令性语言。QML本身作为一个申明式语言用于表达用户界面层次,但是这仅限于表达操作。有时你需要一个方法表达业务,使用JavaScript来完成。 4 | 5 | **注意** 6 | 7 | **在Qt社区有一个开放性的问题是在目前Qt程序中关于混合使用QML/JS/QtC++的正确性。通常建议的混合方式是在你的应用程序中将JS部分限制在最小,将你的业务逻辑部分放在QtC++中,UI逻辑放在QML/JS中。** 8 | 9 | **这本书趋向这种边界的划分,通常对于一个产品的开发这不一定是正确的混合方式,不是对于所有人都适用。最重要的是根据你的团队技能和个人品味而定。在接受推荐的时候保持你的怀疑。** 10 | 11 | 下面有一个简短的例子是关于如何在QML中混合适用JS: 12 | 13 | ``` 14 | Button { 15 | width: 200 16 | height: 300 17 | property bool toggle: false 18 | text: "Click twice to exit" 19 | 20 | // JS function 21 | function doToggle() { 22 | toggle = !toggle 23 | } 24 | 25 | onTriggered: { 26 | // this is JavaScript 27 | doToggle(); 28 | if(toggle) { 29 | Qt.quit() 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | 因此在QML中JavaScript作为一个单独的JS函数,作为一个JS模块可以在很多地方使用,它可以与每一个右边的属性绑定。 36 | 37 | ``` 38 | import "util.js" as Util // import a pure JS module 39 | 40 | Button { 41 | width: 200 42 | height: width*2 // JS on the right side of property binding 43 | 44 | // standalone function (not really useful) 45 | function log(msg) { 46 | console.log("Button> " + msg); 47 | } 48 | 49 | onTriggered: { 50 | // this is JavaScript 51 | log(); 52 | Qt.quit(); 53 | } 54 | } 55 | ``` 56 | 57 | 在使用QML定义用户界面时,使用JavaScript完成功能。那么你需要写多少的JavaScript呢?这取决于你的风格和你对JS开发的熟悉程度。JS是一种松散型语言,这使得你很难发现类型缺陷。函数参数接受不同类型的变量值,会导致非常难发现严重的Bug。发现缺陷的方法是严格的单元测试或者验收测试。因此如果你在JS中开发真正的逻辑(不是粘贴代码)你应该使用测试优先的方法。通常使用这种混合开发非常成功的团队(Qt/C++与QML/JS),他们都会最小化前段逻辑中使用的JS,在后端Qt C++中完成更加复杂的工作。后端遵循严格的单元测试,这样前段的开发者可以信任这些代码并且专注于用户界面的需求。 58 | 59 | **注意** 60 | 61 | **通常:后端开发者由功能驱动,前端开发者由用户场景驱动。** 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /storage/settings.md: -------------------------------------------------------------------------------- 1 | # Settings 2 | 3 | Qt自身就提供了基于系统方式的应用程序配置(又名选项,偏好)C++类 QSettings。它使用基于当前操作系统的方式存储配置。此外,它支持通用的INI文件格式用来操作跨平台的配置文件。 4 | 5 | 在Qt5.2中,配置(Settings)被加入到QML中。编程接口仍然在实验模块中,这意味着接口可能在未来会改变。这里需要注意。 6 | 7 | 这里有一个小例子,对一个矩形框配置颜色。每次用户点击窗口生成一个新的随机颜色。应用程序关闭后重启你将会看到你最后看到的颜色。 8 | 默认的颜色是用来初始化根矩形框的颜色。 9 | 10 | ``` 11 | import QtQuick 2.0 12 | import Qt.labs.settings 1.0 13 | 14 | Rectangle { 15 | id: root 16 | width: 320; height: 240 17 | color: '#000000' 18 | Settings { 19 | id: settings 20 | property alias color: root.color 21 | } 22 | MousArea { 23 | anchors.fill: parent 24 | onClicked: root.color = Qt.hsla(Math.random(), 0.5, 0.5, 1.0); 25 | } 26 | } 27 | ``` 28 | 29 | 每次颜色值的变化都被存储在配置中。这可能不是我们需要的。只有在要求使用标准属性的时候才存储配置。 30 | 31 | ``` 32 | Rectangle { 33 | id: root 34 | color: settings.color 35 | Settings { 36 | id: settings 37 | property color color: '#000000' 38 | } 39 | function storeSettings() { // executed maybe on destruction 40 | settings.color = root.color 41 | } 42 | } 43 | ``` 44 | 45 | 可以使用category属性存储不同种类的配置。 46 | 47 | ``` 48 | Settings { 49 | category: 'window' 50 | property alias x: window.x 51 | property alias y: window.x 52 | property alias width: window.width 53 | property alias height: window.height 54 | } 55 | ``` 56 | 57 | 配置同城根据你的应用程序名称,组织和域存储。这些信息通常在你的C++ main函数中设置。 58 | 59 | ``` 60 | int main(int argc, char** argv) { 61 | ... 62 | QCoreApplication::setApplicationName("Awesome Application"); 63 | QCoreApplication::setOrganizationName("Awesome Company"); 64 | QCoreApplication::setOrganizationDomain("org.awesome"); 65 | ... 66 | } 67 | ``` 68 | 69 | 70 | -------------------------------------------------------------------------------- /qt_and_c++/a_simple_model.md: -------------------------------------------------------------------------------- 1 | # 一个简单的模型(A simple model) 2 | 3 | 一个典型的QML C++模型继承自```QAbstractListModel``` 4 | ,并且最少需要实现```data```和```rowCount```函数。在这个例子中我们将使用由```QColor```类提供的一系列SVG颜色名称并且使用我们的模型展示它们。数据被存储在```QList```数据容器中。 5 | 6 | 我们的```DataEntryModel```基础自```QAbstractListModel```并且实现了需要强制实现的函数。我们可以在```rowCount```中忽略父对象索引,这只在树模型中使用。```QModelIndex```类提供了视图检索数据需要的单元格行和列的信息,视图基于行列和数据角色从模型中拉取数据。```QAbstractListModel```在```QtCore```中定义,但是```QColor```被定义在```QtGui```中。我们需要附加```QtGui```依赖。对于QML应用程序,它可以依赖```QtGui```,但是它通常不依赖```QtWidgets```。 7 | 8 | ``` 9 | #ifndef DATAENTRYMODEL_H 10 | #define DATAENTRYMODEL_H 11 | 12 | #include 13 | #include 14 | 15 | class DataEntryModel : public QAbstractListModel 16 | { 17 | Q_OBJECT 18 | public: 19 | explicit DataEntryModel(QObject *parent = 0); 20 | ~DataEntryModel(); 21 | 22 | public: // QAbstractItemModel interface 23 | virtual int rowCount(const QModelIndex &parent) const; 24 | virtual QVariant data(const QModelIndex &index, int role) const; 25 | private: 26 | QList m_data; 27 | }; 28 | 29 | #endif // DATAENTRYMODEL_H 30 | ``` 31 | 32 | 现在你可以使用QML导入命令```import org.example 1.0```来访问```DataEntryModel```,和其它QML项使用的方法一样```DataEntryModel {}```。 33 | 34 | 我们在这个例子中使用它来显示一个简单的颜色条目列表。 35 | 36 | ``` 37 | import org.example 1.0 38 | 39 | ListView { 40 | id: view 41 | anchors.fill: parent 42 | model: DataEntryModel {} 43 | delegate: ListDelegate { 44 | // use the defined model role "display" 45 | text: model.display 46 | } 47 | highlight: ListHighlight { } 48 | } 49 | ``` 50 | 51 | ListDelegate是自定义用来显示文本的代理。ListHighlight是一个矩形框。保持例子的整洁在代码提取时进行了保留。 52 | 53 | 视图现在可以使用C++模型来显示字符串列表,并且显示模型的属性。它仍然非常简单,但是已经可以在QML中使用。通常数据由外部的模型提供,这里的模型只是扮演了视图的一个接口。 54 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /qt_and_c++/cmake.md: -------------------------------------------------------------------------------- 1 | # CMake 2 | 3 | CMake是由Kitware创造的工具。由于它们的3D可视化软件VTK使得Kitware家喻户晓,当然这也有CMake这个跨平台makefile生成器的功劳。它使用一系列的```CMakeLists.txt```文件来生成平台指定的makefile。CMake被KDE项目所使用,它与Qt社区有一种特殊的关系。 4 | 5 | ```CMakeLists.txt```文件存储了项目配置。一个简单的hello world使用QtCore的项目如下: 6 | 7 | ``` 8 | // ensure cmake version is at least 3.0 9 | cmake_minimum_required(VERSION 3.0) 10 | // adds the source and build location to the include path 11 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 12 | // Qt's MOC tool shall be automatically invoked 13 | set(CMAKE_AUTOMOC ON) 14 | // using the Qt5Core module 15 | find_package(Qt5Core) 16 | // create excutable helloworld using main.cpp 17 | add_executable(helloworld main.cpp) 18 | // helloworld links against Qt5Core 19 | target_link_libraries(helloworld Qt5::Core) 20 | ``` 21 | 22 | 这将使用main.cpp编译一个可执行的helloworld应用程序,并与额外的Qt5Core库链接。编译文件通常会被修改: 23 | 24 | ``` 25 | // sets the PROJECT_NAME variable 26 | project(helloworld) 27 | cmake_minimum_required(VERSION 3.0) 28 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 29 | set(CMAKE_AUTOMOC ON) 30 | find_package(Qt5Core) 31 | 32 | // creates a SRC_LIST variable with main.cpp as single entry 33 | set(SRC_LIST main.cpp) 34 | // add an executable based on the project name and source list 35 | add_executable(${PROJECT_NAME} ${SRC_LIST}) 36 | // links Qt5Core to the project executable 37 | target_link_libraries(${PROJECT_NAME} Qt5::Core) 38 | ``` 39 | 40 | CMake十分强大。需要一些时间来适应语法。通常CMake更加适合大型和复杂的项目。 41 | 42 | **引用** 43 | 44 | [CMake Help](http://www.cmake.org/documentation/) - CMake在线帮助文档 45 | 46 | [Running CMake](http://www.cmake.org/runningcmake/) 47 | 48 | [KDE CMake Tutorial](https://techbase.kde.org/Development/Tutorials/CMake) 49 | 50 | [CMake Book](http://www.kitware.com/products/books/CMakeBook.html) 51 | 52 | [CMake and Qt](http://www.cmake.org/cmake/help/v3.0/manual/cmake-qt.7.html) 53 | 54 | 55 | -------------------------------------------------------------------------------- /extending_qml_with_c++/creating_the_plugin.md: -------------------------------------------------------------------------------- 1 | # 创建插件(Creating the plugin) 2 | 3 | Qt Creator包含了一个创建```QtQuick 2 QML Extension 4 | Plugin```向导,我们使用它来创建一个叫做```fileio``` 5 | 的插件,这个插件包含了一个从```org.example.io```中启动的```FileIO```对象。 6 | 7 | 插件类源于```QQmlExtensionPlugin```,并且实现了```registerTypes()``` 8 | 函数。```Q_PLUGIN_METADATA```是强制标识这个插件作为一个qml扩展插件。除此之外没有其它特殊的地方了。 9 | 10 | ``` 11 | #ifndef FILEIO_PLUGIN_H 12 | #define FILEIO_PLUGIN_H 13 | 14 | #include 15 | 16 | class FileioPlugin : public QQmlExtensionPlugin 17 | { 18 | Q_OBJECT 19 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") 20 | 21 | public: 22 | void registerTypes(const char *uri); 23 | }; 24 | 25 | #endif // FILEIO_PLUGIN_H 26 | ``` 27 | 28 | 在实现```registerTypes```中我们使用```qmlRegisterType```函数注册了我们的FileIO类。 29 | 30 | ``` 31 | #include "fileio_plugin.h" 32 | #include "fileio.h" 33 | 34 | #include 35 | 36 | void FileioPlugin::registerTypes(const char *uri) 37 | { 38 | // @uri org.example.io 39 | qmlRegisterType(uri, 1, 0, "FileIO"); 40 | } 41 | ``` 42 | 43 | 有趣的是我们不能在这里看到模块统一资源标识符(例如```org.example.io```)。这似乎是从外面设置的。 44 | 45 | 看你查找你的项目文件夹是,你会发现一个qmldir文件。这个文件指定了你的qml插件内容或者最好是你插件中关于QML的部分。它看起来应该像这样。 46 | 47 | ``` 48 | module org.example.io 49 | plugin fileio 50 | ``` 51 | 52 | 模块是统一资源标识符,在统一标识符下插件能够被其它插件获取,并且插件行必须与插件文件名完全相同(在mac下,它将是```libfileio_debug.dylib```存在于文件系统上,```fileio```在```qmldir```中)。这些文件由Qt Creator基于给定的信息创建。模块的标识符在```.pro```文件中同样可用。用来构建安装文件夹。 53 | 54 | 当你在构建文件夹中调用```make install```时,将会拷贝库文件到Qt```qml``` 55 | 文件夹中(在Qt5.4之后mac上这将在```~/Qt/5.4/clang_64/qml```文件夹中。这个路径依赖Qt按住那个位置,并且使用系统上的编译器)。你将会在```org/example/io```文件夹中发现库文件。目前包含两个文件。 56 | 57 | ``` 58 | libfileio_debug.dylib 59 | qmldir 60 | ``` 61 | 62 | 当导入一个叫做```org.example.io```的模块时,qml引擎将会在导入路径下查找一个可用模块并且尝试使用qmldir文件定位```org/example/io```路径。qmldir会告诉引擎使用哪个模块标识符加在哪个库作为qml扩展插件。两个模块使用相同的统一标识符将会相互覆盖。 63 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /qt_and_c++/associative_containers.md: -------------------------------------------------------------------------------- 1 | # 组合容器(Associative Containers) 2 | 3 | 映射,字典或者集合是组合容器的例子。它们使用一个键来保存一个值。它们可以快速的查询它们的元素。我们将展示使用最多的组合容器```QHash```,同时也会展示一些C++11新的特性。 4 | 5 | ``` 6 | QHash hash({{"b",2},{"c",3},{"a",1}}); 7 | qDebug() << hash.keys(); // a,b,c - unordered 8 | qDebug() << hash.values(); // 1,2,3 - unordered but same as order as keys 9 | 10 | QVERIFY(hash["a"] == 1); 11 | QVERIFY(hash.value("a") == 1); 12 | QVERIFY(hash.contains("c") == true); 13 | 14 | { // JAVA iterator 15 | int sum =0; 16 | QHashIterator i(hash); 17 | while (i.hasNext()) { 18 | i.next(); 19 | sum+= i.value(); 20 | qDebug() << i.key() << " = " << i.value(); 21 | } 22 | QVERIFY(sum == 6); 23 | } 24 | 25 | { // STL iterator 26 | int sum = 0; 27 | QHash::const_iterator i = hash.constBegin(); 28 | while (i != hash.constEnd()) { 29 | sum += i.value(); 30 | qDebug() << i.key() << " = " << i.value(); 31 | i++; 32 | } 33 | QVERIFY(sum == 6); 34 | } 35 | 36 | hash.insert("d", 4); 37 | QVERIFY(hash.contains("d") == true); 38 | hash.remove("d"); 39 | QVERIFY(hash.contains("d") == false); 40 | 41 | { // hash find not successfull 42 | QHash::const_iterator i = hash.find("e"); 43 | QVERIFY(i == hash.end()); 44 | } 45 | 46 | { // hash find successfull 47 | QHash::const_iterator i = hash.find("c"); 48 | while (i != hash.end()) { 49 | qDebug() << i.value() << " = " << i.key(); 50 | i++; 51 | } 52 | } 53 | 54 | // QMap 55 | QMap map({{"b",2},{"c",2},{"a",1}}); 56 | qDebug() << map.keys(); // a,b,c - ordered ascending 57 | 58 | QVERIFY(map["a"] == 1); 59 | QVERIFY(map.value("a") == 1); 60 | QVERIFY(map.contains("c") == true); 61 | 62 | // JAVA and STL iterator work same as QHash 63 | ``` 64 | 65 | -------------------------------------------------------------------------------- /javascript/js_objects.md: -------------------------------------------------------------------------------- 1 | # JS对象(JS Objects) 2 | 3 | 在使用JS工作时,有一些对象和方法会被频繁的使用。下面是它们的一个小的集合。 4 | 5 | * Math.floor(v),Math.ceil(v),Math.round(v) - 从浮点数获取最大,最小和随机整数 6 | * Math.random() - 创建一个在0到1之间的随机数 7 | * Object.keys(o) - 获取对象的索引值(包括QObject) 8 | * JSON.parse(s), JSON.stringify(o) - 转换在JS对象和JSON字符串 9 | * Number.toFixed(p) - 修正浮点数精度 10 | * Date - 日期时间操作 11 | 12 | 你可以可以在这里找到它们的使用方法:JavaScript reference 13 | 14 | You can find them also at: [JavaScript reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference) 15 | 16 | 下面有一些简单的例子演示了如何在QML中使用JS。会给你一些如何在QML中使用JS一些启发。 17 | 18 | **打印所有项的键(Print all keys from QML Item)** 19 | 20 | ``` 21 | Item { 22 | id: root 23 | Component.onCompleted: { 24 | var keys = Object.keys(root); 25 | for(var i=0; i 'value' 47 | } 48 | } 49 | ``` 50 | 51 | **当前时间(Current Date)** 52 | 53 | ``` 54 | Item { 55 | Timer { 56 | id: timeUpdater 57 | interval: 100 58 | running: true 59 | repeat: true 60 | onTriggered: { 61 | var d = new Date(); 62 | console.log(d.getSeconds()); 63 | } 64 | } 65 | } 66 | ``` 67 | 68 | **使用名称调用函数(Call a function by name)** 69 | 70 | ``` 71 | Item { 72 | id: root 73 | 74 | function doIt() { 75 | console.log("doIt()") 76 | } 77 | 78 | Component.onCompleted: { 79 | // Call using function execution 80 | root["doIt"](); 81 | var fn = root["doIt"]; 82 | // Call using JS call method (could pass in a custom this object and arguments) 83 | fn.call() 84 | } 85 | } 86 | ``` 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /javascript/the_language.md: -------------------------------------------------------------------------------- 1 | # JavaScript语法(The Language) 2 | 3 | 这章不会对JavaScript作详细的介绍。有其它的书可以参考,请访问[Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript) 4 | 5 | JavaScript表面上是一种非常通用的语言,与许多其它语言没有什么不同: 6 | 7 | ``` 8 | function countDown() { 9 | for(var i=0; i<10; i++) { 10 | console.log('index: ' + i) 11 | } 12 | } 13 | 14 | function countDown2() { 15 | var i=10; 16 | while( i>0 ) { 17 | i--; 18 | } 19 | } 20 | ``` 21 | 22 | 但是注意JS有函数作用域,没有C++中的块作用域(查看[Functions and function scope](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Functions_and_function_scope))。 23 | 24 | 语句if ... else,break,continue也可以使用。switch相对于C++中只可以切换整数值,JavaScript可以切换其它类型的值: 25 | 26 | ``` 27 | function getAge(name) { 28 | // switch over a string 29 | switch(name) { 30 | case "father": 31 | return 58; 32 | case "mother": 33 | return 56; 34 | } 35 | return unknown; 36 | } 37 | ``` 38 | 39 | JS可以将几种值认为是false,如false,0,“”,undefined,null。例如一个函数范围默认值undefined。使用‘===’操作符验证是否为false。‘==’等于操作将会对类型转换做验证。如果条件允许,直接使用等于操作符‘===’可以更快更好的验证一致性(查看[Comparison operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators))。 40 | 41 | 在JavaScript底层有它自己的实现方式,例如数组: 42 | 43 | ``` 44 | function doIt() { 45 | var a = [] // empty arrays 46 | a.push(10) // addend number on arrays 47 | a.push("Monkey") // append string on arrays 48 | console.log(a.length) // prints 2 49 | a[0] // returns 10 50 | a[1] // returns Monkey 51 | a[2] // returns undefined 52 | a[99] = "String" // a valid assignment 53 | console.log(a.length) // prints 100 54 | a[98] // contains the value undefined 55 | } 56 | ``` 57 | 58 | 当然对比C++或者Java这种OO语言,JS的工作方式是不同的。JS不是一种纯粹的OO语言,它也可以称作基于原型语言。每个对象都有一个原型对象。对象是基于这个原型对象创建的。请阅读这本关于JavaScript的书了解更多[Javascript the Good Parts by Douglas Crockford ](http://javascript.crockford.com/)。 59 | 60 | 可以使用在线JS控制台或者小片断的QML代码来测试小的JS片段代码: 61 | 62 | ``` 63 | import QtQuick 2.0 64 | 65 | Item { 66 | function runJS() { 67 | console.log("Your JS code goes here"); 68 | } 69 | Component.onCompleted: { 70 | runJS(); 71 | } 72 | } 73 | ``` 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /dynamic_qml/dynamically_loading_and_instantiating_items.md: -------------------------------------------------------------------------------- 1 | # (动态加载和实例化项)Dynamically Loading and Instantiating Items 2 | 3 | 加载一块QML代码时,它首先会被解释执行为一个组件。这一步包含了加载依赖和验证代码。QML的来源可以是本地文件,Qt资源文件,或者一个指定的URL网络地址。这意味着加载时间不确定。例如一个不需要加载任何依赖位于内存(RAM)中的Qt资源文件加载非常快,或者一个需要加载多种依赖位于一个缓慢的服务器中加载需要很长的时间。 4 | 5 | 创建一个组件的状态可以用来跟踪它的状态属性。可以使用的状态值包括组件为空(Component.NULL)、组件加载中(Component.Loading)、组件可用(Component.Ready)和组件错误(Component.Error)。从空(NULL)状态到加载中(Loading)再到可用(Ready)通常是一个工作流。在任何一个阶段状态都可以变为错误(Error)。在这种情况下,组件无法被用来创建新的对象实例。Component.errorString()函数用来检索用户可读的错误描述。 6 | 7 | 当加载连接缓慢的组件时,可以使用进度(progress)属性。它的范围从0.0意味着为加载任何东西,到1.0表明加载已完成。当组件的状态改变为可用(Ready)时,组件可以用实例化对象。下面的代码演示了如何实现这样的方法,考虑组件变为可用或者创建失败,同时组件加载时间可能会比较慢。 8 | 9 | ``` 10 | var component; 11 | 12 | function createImageObject() { 13 | component = Qt.createComponent("dynamic-image.qml"); 14 | if (component.status === Component.Ready || component.status === Component.Error) 15 | finishCreation(); 16 | else 17 | component.statusChanged.connect(finishCreation); 18 | } 19 | 20 | function finishCreation() { 21 | if (component.status === Component.Ready) 22 | { 23 | var image = component.createObject(root, {"x": 100, "y": 100}); 24 | if (image == null) 25 | console.log("Error creating image"); 26 | } 27 | else if (component.status === Component.Error) 28 | console.log("Error loading component:", component.errorString()); 29 | } 30 | ``` 31 | 32 | 上面的代码是源文件中的JavaScript代码,来自main QML文件。 33 | 34 | ``` 35 | import QtQuick 2.0 36 | import "create-component.js" as ImageCreator 37 | 38 | Item { 39 | id: root 40 | 41 | width: 1024 42 | height: 600 43 | 44 | Component.onCompleted: ImageCreator.createImageObject(); 45 | } 46 | ``` 47 | 48 | 一个组件的创建对象(createObject)对象函数用于创建一个实例化对象,如上所示。这不仅仅用于动态动态加载组件,也用语言QML代码中的组件内联。这样产生的对象可以像其它的对象一样用于QML场景中。唯一的不同是这些对象没有id。 49 | 50 | 创建对象(createObject)函数接受两个参数。第一个参数是父对象。第二个参数是按照格式{"name": value, "name": value}组成的一串属性和值。下面的例子演示了这种用法。注意,属性参数是可选的。 51 | 52 | ``` 53 | var image = component.createObject(root, {"x": 100, "y": 100}); 54 | ``` 55 | 56 | **注意** 57 | 58 | **一个动态创建的组件实例不同于一个内联组件元素(in-line Component element)。内联组件元素也提供了函数用来动态实例化对象。** 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /dynamic_qml/loading_components_dynamically.md: -------------------------------------------------------------------------------- 1 | # 动态加载组件(Loading Components Dynamically) 2 | 3 | 动态加载QML不同组成部分最简单的方法是使用加载元素项(Loader element)。它作为一个占位符项用来加载项。项的加载通过资源属性(source property)或者资源组件(sourceCompontent)属性控制。加载元素项通过给定的URL链接加载项,然后实例化一个组件。 4 | 5 | 加载元素项(loader)作为一个占位符用于被加载项的加载。它的大小基于被加载项的大小而定,反之亦然。如果加载元素定义了大小,或者通过锚定(anchoring)定义了宽度和高度,被加载项将会被设置为加载元素项的大小。如果加载元素项没有设置大小,它将会根据被加载项的大小而定。 6 | 7 | 下面例子演示了使用加载元素项(Loader Element)将两个分离的用户界面部分加载到一个相同的空间。这个主意是我们有一个快速拨号界面,可以是数字界面或模拟界面。如下面插图所示。表盘周围的数字不受被加载项影响。 8 | 9 | ![](http://qmlbook.github.io/_images/loader-analog.png) 10 | 11 | ![](http://qmlbook.github.io/_images/loader-digital.png) 12 | 13 | 首先,在应用程序中声明一个加载元素项(Loader element)。注意,资源属性(source property)已经被忽略。这是因为资源取决于当前用户界面状态。 14 | 15 | ``` 16 | Loader { 17 | id: dialLoader 18 | 19 | anchors.fill: parent 20 | } 21 | ``` 22 | 23 | 拨号加载器(dialLoader)的父项的状态属性改变元素驱动根据不同状态加载不同的QML文件。在这个例子中,资源属性(source property)是一个相对文件路径,但是它可以是一个完整的URL链接,通过网络获取加载项。 24 | 25 | ``` 26 | states: [ 27 | State { 28 | name: "analog" 29 | PropertyChanges { target: analogButton; color: "green"; } 30 | PropertyChanges { target: dialLoader; source: "Analog.qml"; } 31 | }, 32 | State { 33 | name: "digital" 34 | PropertyChanges { target: digitalButton; color: "green"; } 35 | PropertyChanges { target: dialLoader; source: "Digital.qml"; } 36 | } 37 | ] 38 | ``` 39 | 40 | 为了使被加载项更加生动,它的速度属性必须根项的速度属性绑定。不能够使用直接绑定来绑定属性,因为项不是总在加载,并且这会随着时间而改变。需要使用一个东西来替换绑定元素(Binding Element)。绑定目标属性(target property),每次加载元素项改变时会触发已加载完成(onLoaded)信号。 41 | 42 | ``` 43 | Loader { 44 | id: dialLoader 45 | 46 | anchors.left: parent.left 47 | anchors.right: parent.right 48 | anchors.top: parent.top 49 | anchors.bottom: analogButton.top 50 | 51 | onLoaded: { 52 | binder.target = dialLoader.item; 53 | } 54 | } 55 | Binding { 56 | id: binder 57 | 58 | property: "speed" 59 | value: speed 60 | } 61 | ``` 62 | 63 | 当被加载项加载完成后,加载完成信号(onLoaded)会触发加载QML的动作。类似的,QML完成加载以来与组建构建完成(Component.onCompleted)信号。所有组建都可以使用这个信号,无论它们何时加载。例如,当整个用户界面完成加载后,整个应用程序的根组建可以使用它来启动自己。 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /qt_and_c++/sequential_containers.md: -------------------------------------------------------------------------------- 1 | # 顺序容器(Sequential Containers) 2 | 3 | 链表,队列,数组都是顺序容器。最常用的顺序容器是```QList```类。它是一个模板类,需要一个类型才能被初始化。它也是隐式共享的,数据存放在堆中。所有的容器类应该被创建在栈上。正常情况下你不需要使用```new QList()```这样的语句,千万不要使用new来初始化一个容器。 4 | 5 | 类```QList```与类```QString```一样强大,提供了方便的接口来查询数据。下面一个简单的示例展示了如何使用和遍历链表,这里面也使用到了一些C++11的新特性。 6 | 7 | ``` 8 | // Create a simple list of ints using the new C++11 initialization 9 | // for this you need to add "CONFIG += c++11" to your pro file. 10 | QList list{1,2}; 11 | 12 | // append another int 13 | list << 3; 14 | 15 | // We are using scopes to avoid variable name clashes 16 | 17 | { // iterate through list using Qt for each 18 | int sum(0); 19 | foreach (int v, list) { 20 | sum += v; 21 | } 22 | QVERIFY(sum == 6); 23 | } 24 | { // iterate through list using C++ 11 range based loop 25 | int sum = 0; 26 | for(int v : list) { 27 | sum+= v; 28 | } 29 | QVERIFY(sum == 6); 30 | } 31 | 32 | { // iterate through list using JAVA style iterators 33 | int sum = 0; 34 | QListIterator i(list); 35 | 36 | while (i.hasNext()) { 37 | sum += i.next(); 38 | } 39 | QVERIFY(sum == 6); 40 | } 41 | 42 | { // iterate through list using STL style iterator 43 | int sum = 0; 44 | QList::iterator i; 45 | for (i = list.begin(); i != list.end(); ++i) { 46 | sum += *i; 47 | } 48 | QVERIFY(sum == 6); 49 | } 50 | 51 | 52 | // using std::sort with mutable iterator using C++11 53 | // list will be sorted in descending order 54 | std::sort(list.begin(), list.end(), [](int a, int b) { return a > b; }); 55 | QVERIFY(list == QList({3,2,1})); 56 | 57 | 58 | int value = 3; 59 | { // using std::find with const iterator 60 | QList::const_iterator result = std::find(list.constBegin(), list.constEnd(), value); 61 | QVERIFY(*result == value); 62 | } 63 | 64 | { // using std::find using C++ lambda and C++ 11 auto variable 65 | auto result = std::find_if(list.constBegin(), list.constBegin(), [value](int v) { return v == value; }); 66 | QVERIFY(*result == value); 67 | } 68 | ``` 69 | 70 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /qt_and_c++/qmake.md: -------------------------------------------------------------------------------- 1 | # QMake 2 | QMake是用来读取项目文件并生成编译文件的工具。项目文件记录了你的项目配置,扩展依赖库和源代码文件。最简单包含一个源代码文件的项目可能像这样: 3 | 4 | ``` 5 | // myproject.pro 6 | 7 | SOURCES += main.cpp 8 | ``` 9 | 10 | 我们编译了一个基于项目文件名称```myproject```的可执行程序。这个编译将只包含```main.cpp```源文件。默认情况下我们会为项目添加QtCore和QtGui模块。如果我们的项目是一个QML应用程序,我们需要添加QtQuick和QtQml到这个链表中: 11 | 12 | ``` 13 | // myproject.pro 14 | 15 | QT += qml quick 16 | 17 | SOURCES += main.cpp 18 | ``` 19 | 20 | 现在编译文件知道与Qt的QtQml和QtQuick模块链接。QMake使用```=```,```+=` and ``-=```来指定,添加和移除选项链表中的元素。例如一个只有控制台的编译将不会依赖UI,你需要移除QtGui模块: 21 | 22 | ``` 23 | // myproject.pro 24 | 25 | QT -= gui 26 | 27 | SOURCES += main.cpp 28 | ``` 29 | 30 | 当你期望编译一个库来替代一个应用程序时,你需要改变编译模板: 31 | 32 | ``` 33 | // myproject.pro 34 | TEMPLATE = lib 35 | 36 | QT -= gui 37 | 38 | HEADERS += utils.h 39 | SOURCES += utils.cpp 40 | ``` 41 | 42 | 现在项目将使用```utils.h```头文件和```utils.cpp```文件编译为一个没有UI依赖的库。库的格式依赖于你当前编译项目所用的操作系统。 43 | 44 | 通常将会有更加复杂的配置并且需要编译个项目配置。qmake提供了```subdirs```模板。假设我们有一个mylib和一个myapp项目。我们的配置可能如下: 45 | 46 | ``` 47 | my.pro 48 | mylib/mylib.pro 49 | mylib/utils.h 50 | mylib/utils.cpp 51 | myapp/myapp.pro 52 | myapp/main.cpp 53 | ``` 54 | 55 | 我们已经知道了如何使用Mylib.pro和myapp.pro。my.pro作为一个包含项目文件配置如下: 56 | 57 | ``` 58 | // my.pro 59 | TEMPLATE = subdirs 60 | 61 | subdirs = mylib \ 62 | myapp 63 | 64 | myapp.depends = mylib 65 | ``` 66 | 67 | 在项目文件中声明包含两个子项目```mylib```和```myapp```,```myapp```依赖于```mylib```。当你使用qmake为这个项目文件生成编译文件时,将会在每个项目文件对应的文件夹下生成一个编译文件。当你使用```my.pro```文件的makefile编译时,所有的子项目也会编译。 68 | 69 | 有时你需要基于你的配置在不同的平台上做不同的事情。qmake推荐使用域的概念来处理它。当一个配置选项设置为true时使一个域生效。 70 | 71 | 例如使用unix指定utils的实现可以像这样: 72 | 73 | ``` 74 | unix { 75 | SOURCES += utils_unix.cpp 76 | } else { 77 | SOURCES += utils.cpp 78 | } 79 | ``` 80 | 81 | 这表示如果CONFIG变量包含了unix配置将使用对应域下的文件路径,否则使用其它的文件路径。一个典型的例子,移除mac下的应用绑定: 82 | 83 | ``` 84 | macx { 85 | CONFIG -= app_bundle 86 | } 87 | ``` 88 | 89 | 这将在mac下创建的应用程序不再是一个```.app```文件夹而是创建一个应用程序替换它。 90 | 91 | 基于QMake的项目通常在你开始编写Qt应用程序最好的选择。但是也有一些其它的选择。它们各有优势。我们将在下一小节中简短的讨论其它的选择。 92 | 93 | **引用** 94 | 95 | [QMake Manual](http://doc.qt.io/qt-5//qmake-manual.html) - qmake手册目录。 96 | 97 | [QMake Language](http://doc.qt.io/qt-5//qmake-language.html) - 赋值,域和相关语法 98 | 99 | [QMake Variables](http://doc.qt.io/qt-5//qmake-variable-reference.html) - TEMPLATE,CONFIG,QT等变量解释 100 | 101 | 102 | -------------------------------------------------------------------------------- /meet_qt_5/qt_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 | -------------------------------------------------------------------------------- /qt_and_c++/the_qobject.md: -------------------------------------------------------------------------------- 1 | # QObject对象(The QObject) 2 | 3 | 正如介绍中描述的,```QObject```是Qt的内省机制。在Qt中它几乎是所有类的基类。值类型除外,例如```QColor```,```QString```和```QList```。 4 | 5 | Qt对象是一个标准的C++对象,但是它具有更多的功能。可以从两个方向来深入探讨:内省和内存管理。内省意味着Qt对象知道它的类名,它与其它类的关系,以及它的方法和属性。内存管理意味着每个Qt对象都可以成为是其它子对象的父对象。父对象拥有子对象,当父对象销毁时,它也会负责销毁它的子对象。 6 | 7 | 理解```QObject```的能力如何影响一个类最好的方法是使用Qt的类来替换一个典型的C++类。如下所示的代表一个普通的类。 8 | 9 | 类```Person```是一个数据类,包含了一个名字和性别属性。```Person```使用Qt的对象系统来添加一个元信息到c++类中。它允许使用```Person```对象的用户连接槽函数并且当属性变化时获得通知。 10 | 11 | ``` 12 | class Person : public QObject 13 | { 14 | Q_OBJECT // enabled meta object abilities 15 | 16 | // property declarations required for QML 17 | Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) 18 | Q_PROPERTY(Gender gender READ gender WRITE setGender NOTIFY genderChanged) 19 | 20 | // enables enum introspections 21 | Q_ENUMS(Gender) 22 | 23 | public: 24 | // standard Qt constructor with parent for memory management 25 | Person(QObject *parent = 0); 26 | 27 | enum Gender { Unknown, Male, Female, Other }; 28 | 29 | QString name() const; 30 | Gender gender() const; 31 | 32 | public slots: // slots can be connected to signals 33 | void setName(const QString &); 34 | void setGender(Gender); 35 | 36 | signals: // signals can be emitted 37 | void nameChanged(const QString &name); 38 | void genderChanged(Gender gender); 39 | 40 | private: 41 | // data members 42 | QString m_name; 43 | Gender m_gender; 44 | }; 45 | ``` 46 | 47 | 构造函数传入父对象到超类中并且初始化成员变量。Qt的值类型类会自动初始化。在这个例子中```QString``` 48 | 将会初始化为一个空字符串(```QString::isNull()```)并且性别成员变量会明确的初始化为未知性别。 49 | 50 | ``` 51 | Person::Person(QObject *parent) 52 | : QObject(parent) 53 | , m_gender(Person::Unknown) 54 | { 55 | } 56 | ``` 57 | 58 | 获取函数在命名在属性后并且是一个简单的```const```函数。使用设置属性函数当属性被改变时会发送改变信号。为此我们插入一个保护用来比较当前值与新值。只有在值不同时我们指定给成员变量的值才会生效,并且发送改变信号。 59 | 60 | ``` 61 | QString Person::name() const 62 | { 63 | return m_name; 64 | } 65 | 66 | void Person::setName(const QString &name) 67 | { 68 | if (m_name != name) // guard 69 | { 70 | m_name = name; 71 | emit nameChanged(m_name); 72 | } 73 | } 74 | ``` 75 | 76 | 类通过继承```QObject```,我们获得了元对象能力,我们可以尝试使用```metaObject()```的方法。例如从对象中检索类名。 77 | 78 | ``` 79 | Person* person = new Person(); 80 | person->metaObject()->className(); // "Person" 81 | Person::staticMetaObject.className(); // "Person" 82 | ``` 83 | 84 | QObject基类和元对象还有其它很多功能。详情请查看```QMetaObject```文档获取更多信息。 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /other/collaboration_correction.md: -------------------------------------------------------------------------------- 1 | # 协作校正 2 | 3 | 很多热心的爱好者想要知道如何帮忙校对,这里我再增加一个详细的教程帮助大家。 4 | 5 | **注册github账号,下载markdown编辑工具** 6 | 7 | 首先在github上注册一个账号,然后下载markdown编辑工具,我使用的是GitBook Editor,[点这里下](https://github.com/GitbookIO/editor/releases)。 8 | 9 | 当然你也可以使用其它的markdown编辑工具。 10 | 11 | **创建自己的工作分支** 12 | 13 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/1.PNG?raw=true) 14 | 15 | 登录你的github账号后,进入[我们项目的页面](https://github.com/cwc1987/Qt5-Cadaques-In-Chinese),点击上图右上角的fork,创建自己的工作分支。 16 | 17 | 下图是我测试号的工作分支。 18 | 19 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/2.PNG?raw=true) 20 | 21 | **下载TortoiseGit,克隆你的工作分支到本地** 22 | 23 | 下载TortoiseGit工具,[点我下载](https://code.google.com/p/tortoisegit/wiki/Download),你也可以使用其它的工具来克隆你的工作分支。 24 | 25 | 下图是使用TortoiseGit工具克隆工作分支的界面截图。 26 | 27 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/3.PNG?raw=true) 28 | 29 | **使用gitbook客户端打开项目文件夹,开始校对** 30 | 31 | 使用gitbook客户端打开项目文件夹,就能开始编译校对,下图是打开项目文件夹的截图。 32 | 33 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/4.PNG?raw=true) 34 | 35 | **校对完成后上传到你在github上的工作分支** 36 | 37 | 校对完成后,首先使用Git Commit->master上传到本地库,然后使用pull上传到github上。 38 | 39 | 下图是上传信息的名字与联系方式的补充。 40 | 41 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/5.PNG?raw=true) 42 | 43 | 下图是上传到本地库的界面截图。 44 | 45 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/6.PNG?raw=true) 46 | 47 | 上传本地库完成后,点击pull上传到你在github上的工作分支。 48 | 49 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/7.PNG?raw=true) 50 | 51 | 确定上传工作分支地址,就是你在github上的工作分支地址。 52 | 53 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/8.PNG?raw=true) 54 | 55 | **提交pull rqeuset到我的项目** 56 | 57 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/9.PNG?raw=true) 58 | 59 | 再次进入你的github工作分支页,点击右边的pull request进入。 60 | 61 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/10.PNG?raw=true) 62 | 63 | 点击上图的New pull request绿色按键,进入修改提交。 64 | 65 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/11.PNG?raw=true) 66 | 67 | 系统会检测你的工作分支与我们项目的差别,确认提交内容,点击Create pull rqeust绿色按键添加修改内容描述。 68 | 69 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/12.PNG?raw=true) 70 | 71 | 完成描述后点击上图的Create pull request绿色按键确认提交。 72 | 73 | 下图为我的项目收到新的pull request的请求,我会确认提交内容后合并。 74 | 75 | ![](https://github.com/cwc1987/QmlBook-In-Chinese/blob/master/other/process_images/13.PNG?raw=true) 76 | -------------------------------------------------------------------------------- /dynamic_qml/connecting_indirectly.md: -------------------------------------------------------------------------------- 1 | # 间接连接(Connecting Indirectly) 2 | 3 | 动态创建QML元素时,无法使用onSignalName静态配置来连接信号。必须使用连接元素(Connection element)来完成连接信号。它可以连接一个目标元素任意数量的信号。 4 | 5 | 通过设置连接元素(Connection element)的目标属性,信号可以像正常的方法连接。也就是使用onSignalName方法。不管怎样,通过改变目标属性可以在不同的时间监控不同的元素。 6 | 7 | ![](http://qmlbook.github.io/_images/connections.png) 8 | 9 | 在上面这个例子中,用户界面由两个可点击区域组成。当其中一个区域点击后,会使用一个闪烁的动画。左边区域的代码段如下所示。在鼠标区域(MouseArea)中,左点击动画(leftClickedAnimation)被触发,导致区域闪烁。 10 | 11 | ``` 12 | Rectangle { 13 | id: leftRectangle 14 | 15 | width: 290 16 | height: 200 17 | 18 | color: "green" 19 | 20 | MouseArea { 21 | id: leftMouseArea 22 | anchors.fill: parent 23 | onClicked: leftClickedAnimation.start(); 24 | } 25 | 26 | Text { 27 | anchors.centerIn: parent 28 | font.pixelSize: 30 29 | color: "white" 30 | text: "Click me!" 31 | } 32 | } 33 | ``` 34 | 35 | 除了两个可点击区域,还使用了一个连接元素(Connection element)。当状态为激活时会触发第三个动画,即元素的目标。 36 | 37 | ``` 38 | Connections { 39 | id: connections 40 | onClicked: activeClickedAnimation.start(); 41 | } 42 | ``` 43 | 为了确定鼠标区域的目标,定义了两种状态。注意我们无法使用属性改变元素(PropertyChanges element)来设置目标属性,因为它已经包含了一个目标属性。利用状态改变脚本(StateChangeScript)来完成。 44 | 45 | ``` 46 | states: [ 47 | State { 48 | name: "left" 49 | StateChangeScript { 50 | script: connections.target = leftMouseArea 51 | } 52 | }, 53 | State { 54 | name: "right" 55 | StateChangeScript { 56 | script: connections.target = rightMouseArea 57 | } 58 | } 59 | ] 60 | ``` 61 | 62 | 当尝试运行这个例子时,需要注意当多个信号被处理调用所有操作时,执行的顺序是未定义的。 63 | 64 | 当创建一个连接元素(Connection element)未指定目标属性时,默认的属性是父对象。这意味着需要显式的设置NULL来避免捕获来自父对象的信号,直到目标被设置。这种行为使得基于连接元素(Connection element)创建自定义信号处理组件成为可能。使用这种方式可以将信号的处理代码封装和再使用。 65 | 66 | 在下面这个例子中,闪烁组件能够被放在任何的鼠标区域(MouseArea)中.点击后会触发动画,导致父对象闪烁。在同一个鼠标区域(MouseArea)的实际任务被触发时也可以被调用。这从实际的动作中,分离了标准的用户反馈,闪烁。 67 | 68 | ``` 69 | import QtQuick 2.0 70 | 71 | Connections { 72 | onClicked: { 73 | // Automatically targets the parent 74 | } 75 | } 76 | ``` 77 | 78 | 只需要简单的在每个鼠标区域(MouseArea)实例化一个闪烁组件来实现闪烁。 79 | 80 | ``` 81 | import QtQuick 2.0 82 | 83 | Item { 84 | // A background flasher that flashes the background of any parent MouseArea 85 | } 86 | ``` 87 | 88 | 当你使用一个连接元素(Connection element)来监控不同类型的目标元素的信号时,你可能会发现在在某些场景下会有来自不同目标的可用信号。这将导致连接元素(Connections element)由于丢失信号输出运行错误(run-time errors)。为了避免这个问题,设置忽略未知信号(ignoreUnknownSignal)属性为true,可以忽略这些错误。 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /dynamic_qml/tracking_dynamic_objects.md: -------------------------------------------------------------------------------- 1 | # 跟踪动态对象(Tracking Dynamic Objects) 2 | 3 | 处理动态对象时,通常需要跟踪已创建的对象。另一个常见的功能是能够存储和恢复动态对象的状态。在我们动态填充时,使用链表模型(ListModel)可以非常方便的处理这些问题。 4 | 5 | 在下面的例子中包含了两种元素,火箭和飞机,能够被用户创建和移动。为了控制整个场景动态创建元素,我们使用一个模型来跟踪项。 6 | 7 | **待完成** 8 | 9 | **插图** 10 | 11 | 模型是一个链表模型(ListModel),用已创建的项进行填充。实例化时跟踪对象引用的资源URL。后者不是需要严格跟踪的对象,但是以后会派上用场。 12 | 13 | ``` 14 | import QtQuick 2.0 15 | import "create-object.js" as CreateObject 16 | 17 | Item { 18 | id: root 19 | 20 | ListModel { 21 | id: objectsModel 22 | } 23 | 24 | function addPlanet() { 25 | CreateObject.create("planet.qml", root, itemAdded); 26 | } 27 | 28 | function addRocket() { 29 | CreateObject.create("rocket.qml", root, itemAdded); 30 | } 31 | 32 | function itemAdded(obj, source) { 33 | objectsModel.append({"obj": obj, "source": source}) 34 | } 35 | ``` 36 | 37 | 你可以从上面的例子中看到,create-object.js是一种使得JavaScript引进更加简单的、普遍的方法。创建方法使用了三个参数:一个资源URL,一个根元素和一个完成的回调函数。回调需要两个参数:一个新创建的对象引用和一个资源URL。 38 | 39 | 这意味着每一次调用addPlanet或者addRocket时,当新建对象被创建完成后悔调用itemAdded函数。后者会将对象的引用和资源URL添加到objectsModel模型中。 40 | 41 | 可以在很多方面使用objectsModel。在示例中,clearItems函数依赖它。这个函数证明了两个事情。首先,如何遍历模型和执行一个任务,即调用析构函数来移除每一个项。其次,它强调了模型不会更新已经销毁的对象。此外移除模型项已连接的对象问题,模型项的对象属性设置为null,为了补救这个问题,代码显式的清除了已移除对象的模型项。 42 | 43 | ``` 44 | function clearItems() { 45 | while(objectsModel.count > 0) { 46 | objectsModel.get(0).obj.destroy(); 47 | objectsModel.remove(0); 48 | } 49 | } 50 | ``` 51 | 52 | 通过设置XmlListModel模型的xml属性可以处理XML文档字符串。代码如下,模型展示了反序列化函数。反序列化函数通过设置dsIndex引用模型的第一个项来启动反序列化,然后调用项的创建。然后回调dsItemAdded设置新创建对象的x,y属性,然后更新索引创建下一个对象。 53 | 54 | ``` 55 | XmlListModel { 56 | id: xmlModel 57 | query: "/scene/item" 58 | XmlRole { name: "source"; query: "source/string()" } 59 | XmlRole { name: "x"; query: "x/string()" } 60 | XmlRole { name: "y"; query: "y/string()" } 61 | } 62 | 63 | function deserialize() { 64 | dsIndex = 0; 65 | CreateObject.create(xmlModel.get(dsIndex).source, root, dsItemAdded); 66 | } 67 | 68 | function dsItemAdded(obj, source) { 69 | itemAdded(obj, source); 70 | obj.x = xmlModel.get(dsIndex).x; 71 | obj.y = xmlModel.get(dsIndex).y; 72 | 73 | dsIndex ++; 74 | 75 | if (dsIndex < xmlModel.count) 76 | CreateObject.create(xmlModel.get(dsIndex).source, root, dsItemAdded); 77 | } 78 | 79 | property int dsIndex 80 | ``` 81 | 82 | 这个例子演示了如何使用模型跟踪已创建的模型项,和基于信息对模型项序列化和反序列化。这可以用于存储一个动态填充场景,例如窗口部件。在这个例子中,模型被用于跟踪每一个模型项。 83 | 84 | 另一种解决方案是用于一个场景根项下的子项属性来跟踪子项。然后,这要求项自己知道资源URL用于创建它们自己。这也要求场景只能动态创建子项,以避免序列化或者反序列化静态分配的对象。 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /qt_and_c++/qstring.md: -------------------------------------------------------------------------------- 1 | # QString 2 | 3 | 通常在Qt中文本操作是基于unicode完成的。你需要使用```QString```类来完成这个事情。它包含了很多好用的功能函数,这与其它流行的框架类似。对于8位的数据你通常需要使用```QByteArray```类,对于ASCII校验最好使用```QLatin1String```来暂存。对于一个字符串链你可以使用```QList```或者```QStringList```类(派生自```QList```)。 4 | 5 | 这里有一些例子介绍了如何使用```QString```类。QString可以在栈上创建,但是它的数据存储在堆上。分配一个字符串数据到另一个上,不会产生拷贝操作,只是创建了数据的引用。这个操作非常廉价让开发者更专注于代码而不是内存操作。```QString```使用引用计数的方式来确定何时可以安全的删除数据。这个功能叫做[隐式共享](http://doc.qt.io/qt-5//implicit-sharing.html),在Qt的很多类中都用到了它。 6 | 7 | ``` 8 | QString data("A,B,C,D"); // create a simple string 9 | // split it into parts 10 | QStringList list = data.split(","); 11 | // create a new string out of the parts 12 | QString out = list.join(","); 13 | // verify both are the same 14 | QVERIFY(data == out); 15 | // change the first character to upper case 16 | QVERIFY(QString("A") == out[0].toUpper()); 17 | ``` 18 | 19 | 这里我们将展示如何将一个字符串转换为数字,将一个数字转换为字符串。也有一些方便的函数用于float或者double和其它类型的转换。只需要在Qt帮助文档中就可以找到这些使用方法。 20 | 21 | ``` 22 | // create some variables 23 | int v = 10; 24 | int base = 10; 25 | // convert an int to a string 26 | QString a = QString::number(v, base); 27 | // and back using and sets ok to true on success 28 | bool ok(false); 29 | int v2 = a.toInt(&ok, base); 30 | // verify our results 31 | QVERIFY(ok == true); 32 | QVERIFY(v = v2); 33 | ``` 34 | 35 | 通常你需要参数化文本。例如使用```QString("Hello" + name)``` 36 | ,一个更加灵活的方法是使用```arg```标记目标,这样即使在翻译时也可以保证目标的变化。 37 | 38 | ``` 39 | // create a name 40 | QString name("Joe"); 41 | // get the day of the week as string 42 | QString weekday = QDate::currentDate().toString("dddd"); 43 | // format a text using paramters (%1, %2) 44 | QString hello = QString("Hello %1. Today is %2.").arg(name).arg(weekday); 45 | // This worked on Monday. Promise! 46 | if(Qt::Monday == QDate::currentDate().dayOfWeek()) { 47 | QCOMPARE(QString("Hello Joe. Today is Monday."), hello); 48 | } else { 49 | QVERIFY(QString("Hello Joe. Today is Monday.") != hello); 50 | } 51 | ``` 52 | 53 | 有时你需要在你的代码中直接使用unicode字符。你需要记住如何在使用```QChar```和```QString```类来标记它们。 54 | 55 | ``` 56 | // Create a unicode character using the unicode for smile :-) 57 | QChar smile(0x263A); 58 | // you should see a :-) on you console 59 | qDebug() << smile; 60 | // Use a unicode in a string 61 | QChar smile2 = QString("\u263A").at(0); 62 | QVERIFY(smile == smile2); 63 | // Create 12 smiles in a vector 64 | QVector smilies(12); 65 | smilies.fill(smile); 66 | // Can you see the smiles 67 | qDebug() << smilies; 68 | ``` 69 | 70 | 上面这些示例展示了在Qt中如何轻松的处理unicode文本。对于非unicode文本,QByteArray类同样有很多方便的函数可以使用。阅读Qt帮助文档中QString部分,它有一些很好的示例。 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /extending_qml_with_c++/understanding_the_qml_run-time.md: -------------------------------------------------------------------------------- 1 | # 理解QML运行环境(Understanding the QML Run-time) 2 | 3 | 当运行QML时,它在一个运行时环境下执行。这个运行时环境是由```QtQml```模块下的C++代码实现的。它由一个负责执行QML的引擎,持有访问每个组件属性的上下文和实例化的QML元素组件构成。 4 | 5 | ``` 6 | #include 7 | #include 8 | 9 | int main(int argc, char **argv) 10 | { 11 | QGuiApplication app(argc, argv); 12 | QUrl source(QStringLiteral("qrc:/main.qml")); 13 | QQmlApplicationEngine engine; 14 | engine.load(source); 15 | return app.exec(); 16 | } 17 | ``` 18 | 19 | 在这个例子中,```QGuiApplication```封装了所有与应用程序引用相关的属性(例如应用程序名称,命令行参数,和事件循环管理)。```QQmlApplicationEngine```分层管理上下文和组件的顺序。它需要加载一个典型的qml文件作为应用程序的开始点。在这个例子中,```main.qml```包含了以一个窗口和一个文本。 20 | 21 | **注意** 22 | 23 | **通过```QmlApplicationEngine```加载一个使用简单项作为根类型的```main.qml```不会在你的屏幕上显示任何东西,它需要一个窗口来管理一个平面的渲染。引擎可以加载不包含任何用户界面的qml代码(例如一个纯粹的对象)。由于它不会默认为你创建一个窗口。```qmlcene```或者新的qml运行环境将会在内部首先检查```main.qml```文件是否包含一个窗口作为根项,如果没有包含将会为你创建一个并且设置根项作为新创建窗口的子项。** 24 | 25 | ``` 26 | import QtQuick 2.4 27 | import QtQuick.Window 2.0 28 | 29 | Window { 30 | visible: true 31 | width: 512 32 | height: 300 33 | 34 | Text { 35 | anchors.centerIn: parent 36 | text: "Hello World!" 37 | } 38 | } 39 | ``` 40 | 41 | 在QML文件中我们定义我们的依赖是```QtQuick```和```QtQuick.Window```。这些定义将会触发在导入的路径中查找这些模块,并在加载成功后由引擎加载需要的插件。新加载的类型将会倍qmldir控制在qml文件中可用。 42 | 43 | 当然也可以使用快速创建插件直接向引擎添加我们的自定义类型。这里我们假设我们有一个基于```QObject```的```CurrentTime```类。 44 | 45 | ``` 46 | QQmlApplicationEngine engine; 47 | 48 | qmlRegisterType("org.example", 1, 0, "CurrentTime"); 49 | 50 | engine.load(source); 51 | ``` 52 | 53 | 现在我们也可可以在我们的qml文件中使用```CurrentTime```类型。 54 | 55 | ``` 56 | import org.example 1.0 57 | 58 | CurrentTime { 59 | // access properties, functions, signals 60 | } 61 | ``` 62 | 63 | 一种更偷懒的方式是通过上下文属性直接设置。 64 | 65 | ``` 66 | QScopedPointer current(new CurrentTime()); 67 | 68 | QQmlApplicationEngine engine; 69 | 70 | engine.rootContext().setContextProperty("current", current.value()) 71 | 72 | engine.load(source); 73 | ``` 74 | 75 | **注意** 76 | 77 | **不要混淆```setContextProperty()```和setProperty()。```setContextProperty()```是设置一个qml上下文的属性,```setProperty()```是设置一个QObject的动态属性值,这对你没什么帮助。** 78 | 79 | 现在你可以在你的应用程序的任何地方使用这个属性了。感谢上下文继承这一特性。 80 | 81 | ``` 82 | import QtQuick 2.4 83 | import QtQuick.Window 2.0 84 | 85 | Window { 86 | visible: true 87 | width: 512 88 | height: 300 89 | 90 | Component.onCompleted: { 91 | console.log('current: ' + current) 92 | } 93 | } 94 | ``` 95 | 96 | 通常有以下几种不同的方式扩展QML: 97 | 98 | * 上下文属性 - setContextProperty() 99 | 100 | * 引擎注册类型 - 在main.cpp中调用qmlRegisterType 101 | 102 | * QML扩展插件 - 后面会讨论 103 | 104 | 上下文属性使用对于小型的应用程序使用非常方便。它们不需要你做太多的事情就可以将系统编程接口暴露为友好的全局对象。它有助于确保不会出现命名冲突(例如使用($)这种特殊符号,例如$.currentTime)。在JS变量中$是一个有效的字符。 105 | 106 | 注册QML类型允许用户从QML中控制一个c++对象的生命周期。上下文属性无法完成这间事情。它也不会破坏全局命名空间。所有的类型都需要先注册,并且在程序启动时会链接所有库,这在大多数情况下都不是一个问题。 107 | 108 | QML扩展插件提供了最灵活的扩展系统。它允许你在插件中注册类型,在第一个QML文件调用导入鉴定时会加载这个插件。由于使用了一个QML单例这也不会再破坏全局命名空间。插件允许你跨项目重用模块,这在你使用Qt包含多个项目时非常方便。 109 | 110 | 这章的其余部分将会集中在qml扩展插件上讨论。它们提供了最好的灵活性和可重用性。 111 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /httpuiserving_ui_via_http.md: -------------------------------------------------------------------------------- 1 | # 通过HTTP服务用户界面(Serving UI via HTTP) 2 | 3 | 通过HTTP加载一个简单的用户界面,我们需要一个web服务器,它为用户界面文档提供服务。但是首先我们需要有用户界面,我们在项目里创建一个创建了红色矩形框的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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /meet_qt_5/qt5_introduction.md: -------------------------------------------------------------------------------- 1 | # 1.2 Qt5介绍 2 | 3 | ## 1.2.1 Qt Quick 4 | 5 | Qt Quick是Qt5界面开发技术的统称,是以下几种技术的集合: 6 | 7 | * QML - 界面标记语言 8 | 9 | * JavaScript - 动态脚本语言 10 | 11 | * Qt C++ - 跨平台C++封装库 12 | 13 | ![](http://qmlbook.org/_images/qt5_overview.png) 14 | 15 | QML是与HTML类似的一种标记语言。在QtQuick中将由标签组成的元素封装在大括号中`Item{}`。这样的设计重新定义了界面的创建方式,对于开发者而言更加简单易读。可以使用JavaScript开发界面功能,也可以使用本地Qt C++函数接口扩展界面功能。简单来说,声明式的UI被称作前端,本地C++部分称作后端,将复杂的计算过程与本地设备操作从界面开发中分离。 16 | 17 | 在一个典型的Qt5项目中,前端采用QML/JaveScript开发界面,后端采用Qt C++与系统交互并完成复杂的运算逻辑,将侧重设计的界面开发与功能开发的工作内容分离。通常后端开发者可以使用Qt的单元测试框架完成单元测试后将函数接口提供给前端开发者使用。 18 | 19 | ## 1.2.2 Qt5用户界面开发示例 20 | 21 | 我们将使用QtQuick创建一个简单的界面,这个例子展示了QML语言的一些特性,在例子完成后我们将获得一个可以旋转的风车。![](http://qmlbook.github.io/_images/scene.png) 22 | 23 | 我们开始创建一个空的`main.qml`文档。QML文件采用`.qml`作为文件格式后缀。作为一种标记语言(类似HTML)一个QML文档有且只有一个根元素,在这个例子中使用`Image`元素作为根元素,这个元素的宽度、高度与`"images/background.png"`图像相同。 24 | 25 | 26 | import QtQuick 2.5 27 | 28 | Image { 29 | id: root 30 | source: "images/background.png" 31 | } 32 | 33 | 34 | QML中不限制根元素类型,在上面这段代码中我们设置了`Image`元素的`source`属性作为我们的背景图像,它也是我们的根元素。 35 | 36 | ![](http://qmlbook.org/_images/background.png) 37 | 38 | **注意** 39 | 40 | **每个元素都有属性,比如**`Image`**有**`width`**和**`height`**,也会有其它的属性如**`source`**。**`Image`**元素的尺寸会自动与**`source`**设置的图像匹配。想要自定义**`Image`**元素的尺寸必须显式的定义**`width`**和**`height`**的值。** 41 | 42 | **大多数标准元素都在**`QtQuick`**模块中,通常我们在导入声明中首先包含这个模块。** 43 | 44 | `id`**是个特殊的属性,它可以作为一个标识符在当前文档内引用对应的元素。注意:`id`被定义后无法再改变,在程序执行期间也无法被赋值。使用`root`作为根元素id仅仅是作者的习惯,这样可以在复杂的QML文档中快速引用最顶层元素。** 45 | 46 | 我们使用分离的风车竿和风车的图片作为前景元素。 47 | 48 | ![](http://qmlbook.github.io/_images/pole.png) 49 | 50 | ![](http://qmlbook.org/_images/pinwheel.png) 51 | 52 | 风车竿需要放置在背景的水平居中位置,并且竿的底部与背景底部平行。风车需要放置在背景中央位置。 53 | 54 | 通常用户界面由不同的类型的元素组成,而不是像这个示例只有图像。 55 | 56 | Image { 57 | id: root 58 | ... 59 | Image { 60 | id: pole 61 | anchors.horizontalCenter: parent.horizontalCenter 62 | anchors.bottom: parent.bottom 63 | source: "images/pole.png" 64 | } 65 | 66 | Image { 67 | id: wheel 68 | anchors.centerIn: parent 69 | source: "images/pinwheel.png" 70 | } 71 | ... 72 | } 73 | 74 | 为了将风车放在中央,我们需要使用一个较复杂的属性`anchor`。锚点可以指定对象与父对象和同级对象的相对几何位置,比如定义元素放在另一个元素的中央`(anchors.centerIn: parent)`。锚点定义时两端都可以定义元素的左侧(left)、右侧(right)、顶部(top)、底部(bottom)、居中(centerIn)、填充(fill)、垂直居中(verticalCenter )和水平居中(horizontalCenter )相对位置。当然它们必须是可以匹配,无法定义一个元素的左侧与另一个元素的顶部对齐。 75 | 76 | 这样我就能设置风车的位置在我们背景元素的中央了。 77 | 78 | ---here---- 79 | 80 | **注意** 81 | 82 | **有时你需要进行一些微小的调整。使用anchors.horizontalCenterOffset或者anchors.verticalCenterOffset可以帮你实现这个功能。类似的调整属性也可以用于其他所有的锚。查阅Qt的帮助文档可以知道完整的锚属性列表。** 83 | 84 | **注意** 85 | 86 | **将一个图像作为根矩形元素的子元素放置展示了一种声明式语言的重要概念。你描述了用户界面的层和分组的顺序,最顶部的一层(根矩形框)先绘制,然后子层按照包含它的元素局部坐标绘制在包含它的元素上。** 87 | 88 | 为了让我们的展示更加有趣一点,我们应该让程序有一些交互功能。当用户点击场景上某个位置时,让我们的风车转动起来。 89 | 90 | 我们使用mouseArea元素,并且让它与我们的根元素大小一样。 91 | 92 | ``` 93 | Image { 94 | id: root 95 | ... 96 | MouseArea { 97 | anchors.fill: parent 98 | onClicked: wheel.rotation += 90 99 | } 100 | ... 101 | } 102 | ``` 103 | 104 | 当用户点击覆盖区域时,鼠标区域会发出一个信号。你可以重写onClicked函数来链接这个信号。在这个案例中引用了风车的图像并且让他旋转增加90度。 105 | 106 | **注意** 107 | 108 | **对于每个工作的信号,命名方式都是on + SignalName的标题。当属性的值发生改变时也会发出一个信号。它们的命名方式是:on + PropertyName + Chagned。 109 | 如果一个宽度(width)属性改变了,你可以使用onWidthChanged: print\(width\)来得到这个监控这个新的宽度值。** 110 | 111 | 现在风车将会旋转,但是还不够流畅。风车的旋转角度属性被直接改变了。我们应该怎样让90度的旋转可以持续一段时间呢。现在是动画效果发挥作用的时候了。一个动画定义了一个属性的在一段时间内的变化过程。为了实现这个效果,我们使用一个动画类型叫做属性行为。这个行为指定了一个动画来定义属性的每一次改变并赋值给属性。每次属性改变,动画都会运行。这是QML中声明动画的几种方式中的一种方式。 112 | 113 | ``` 114 | Image { 115 | id: root 116 | Image { 117 | id: wheel 118 | Behavior on rotation { 119 | NumberAnimation { 120 | duration: 250 121 | } 122 | } 123 | } 124 | } 125 | ``` 126 | 127 | 现在每当风车旋转角度发生改变时都会使用NumberAnimation来实现250毫秒的旋转动画效果。每一次90度的转变都需要花费250ms。 128 | 129 | 现在风车看起来好多了,我希望以上这些能够让你能够对Qt Quick编程有一些了解。 130 | 131 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /qt_and_c++/more_complex_data.md: -------------------------------------------------------------------------------- 1 | # 更复杂的数据(More Complex Data) 2 | 3 | 实际工作中使用的模型数据通常比较复杂。所以需要自定义一些角色枚举方便视图通过属性查找数据。例如模型提供颜色数据不仅只是16进制字符串,在QML中也可以是来自HSV颜色模型的色调,饱和度和亮度,以“model.hue”,“model.saturation”和“model.brightness”作为参数。 4 | 5 | ``` 6 | #ifndef ROLEENTRYMODEL_H 7 | #define ROLEENTRYMODEL_H 8 | 9 | #include 10 | #include 11 | 12 | class RoleEntryModel : public QAbstractListModel 13 | { 14 | Q_OBJECT 15 | public: 16 | // Define the role names to be used 17 | enum RoleNames { 18 | NameRole = Qt::UserRole, 19 | HueRole = Qt::UserRole+2, 20 | SaturationRole = Qt::UserRole+3, 21 | BrightnessRole = Qt::UserRole+4 22 | }; 23 | 24 | explicit RoleEntryModel(QObject *parent = 0); 25 | ~RoleEntryModel(); 26 | 27 | // QAbstractItemModel interface 28 | public: 29 | virtual int rowCount(const QModelIndex &parent) const override; 30 | virtual QVariant data(const QModelIndex &index, int role) const override; 31 | protected: 32 | // return the roles mapping to be used by QML 33 | virtual QHash roleNames() const override; 34 | private: 35 | QList m_data; 36 | QHash m_roleNames; 37 | }; 38 | 39 | #endif // ROLEENTRYMODEL_H 40 | ``` 41 | 42 | 在头文件中,我们为QML添加了数据角色枚举的映射。当QML尝试访问一个模型中的属性时(例如“model.name”),链表视图将会在映射中查询“name”然后向模型申请使用```NameRole```角色枚举的数据。用户在定义角色枚举时应该从```Qt::UserRole```开始,并且对于每个模型需要保证唯一。 43 | 44 | ``` 45 | #include "roleentrymodel.h" 46 | 47 | RoleEntryModel::RoleEntryModel(QObject *parent) 48 | : QAbstractListModel(parent) 49 | { 50 | // Set names to the role name hash container (QHash) 51 | // model.name, model.hue, model.saturation, model.brightness 52 | m_roleNames[NameRole] = "name"; 53 | m_roleNames[HueRole] = "hue"; 54 | m_roleNames[SaturationRole] = "saturation"; 55 | m_roleNames[BrightnessRole] = "brightness"; 56 | 57 | // Append the color names as QColor to the data list (QList) 58 | for(const QString& name : QColor::colorNames()) { 59 | m_data.append(QColor(name)); 60 | } 61 | 62 | } 63 | 64 | RoleEntryModel::~RoleEntryModel() 65 | { 66 | } 67 | 68 | int RoleEntryModel::rowCount(const QModelIndex &parent) const 69 | { 70 | Q_UNUSED(parent); 71 | return m_data.count(); 72 | } 73 | 74 | QVariant RoleEntryModel::data(const QModelIndex &index, int role) const 75 | { 76 | int row = index.row(); 77 | if(row < 0 || row >= m_data.count()) { 78 | return QVariant(); 79 | } 80 | const QColor& color = m_data.at(row); 81 | qDebug() << row << role << color; 82 | switch(role) { 83 | case NameRole: 84 | // return the color name as hex string (model.name) 85 | return color.name(); 86 | case HueRole: 87 | // return the hue of the color (model.hue) 88 | return color.hueF(); 89 | case SaturationRole: 90 | // return the saturation of the color (model.saturation) 91 | return color.saturationF(); 92 | case BrightnessRole: 93 | // return the brightness of the color (model.brightness) 94 | return color.lightnessF(); 95 | } 96 | return QVariant(); 97 | } 98 | 99 | QHash RoleEntryModel::roleNames() const 100 | { 101 | return m_roleNames; 102 | } 103 | ``` 104 | 105 | 现在实现只是改变了两个地方。首先是初始化。我们使用```QColor```数据类型初始化数据链表。此外我们还定义了我们自己的角色名称映射实现QML的访问。这个映射将在后面的```::roleNames```函数中返回。 106 | 107 | 第二个变化是在```::data```函数中。我们确保能够覆盖到其它的角色枚举(例如色调,饱和度,亮度)。没有可以从颜色中获取SVG名称的方法,由于一个颜色可以替代任何颜色,但SVG名称是受限的。所以我们忽略掉这点。我们需要创建一个结构体```{ QColor, QString }```来存储名称,这样可以鉴别已被命名的颜色。 108 | 109 | 在注册类型完成后,我们可以使用模型了,可以将它的条目显示在我们的用户界面中。 110 | 111 | ``` 112 | ListView { 113 | id: view 114 | anchors.fill: parent 115 | model: RoleEntryModel {} 116 | focus: true 117 | delegate: ListDelegate { 118 | text: 'hsv(' + 119 | Number(model.hue).toFixed(2) + ',' + 120 | Number(model.saturation).toFixed() + ',' + 121 | Number(model.brightness).toFixed() + ')' 122 | color: model.name 123 | } 124 | highlight: ListHighlight { } 125 | } 126 | ``` 127 | 128 | 我们将返回的类型转换为JS数字类型,这样可以使用定点标记来格式化数字。代码中应当避免直接调用数字(例如```model.saturation.toFixed(2)```)。选择哪种格式取决于你的输入数据。 129 | 130 | -------------------------------------------------------------------------------- /javascript/creating_a_js_console.md: -------------------------------------------------------------------------------- 1 | # 创建JS控制台(Creating a JS Console) 2 | 3 | 下面这个小的例子我们将创建一个JS控制台。我们需要一个输入区域允许用户输入表达式,和一个结果输出列表。由于这更像一个桌面应用程序,我们使用QtQuick控制模块。 4 | 5 | **注意** 6 | 7 | **在你下一个项目中包含一个JS控制台可以更好的用来测试。增加Quake-Terminal效果也有助于对你的客户留下更好的印象。为了更好的使用它,你需要评估JS控制台的控制范围,例如当前可见屏幕,核心数据模型,一个单例对象或者其它的东西。** 8 | 9 | ![](http://qmlbook.github.io/_images/jsconsole.png) 10 | 11 | 我们在Qt Creator中使用QtQuick controls创建一个Qt Quick UI项目。把这个项目取名为JSConsole。完成引导后,我们已经有了一个基础的应用程序框架,这个框架包含一个应用程序窗口和一个菜单。 12 | 13 | 我们使用一个TextFiled来输入文本,使用一个Button来对输入求值。表达式求值结果使用一个机遇链表模型(ListModel)的链表视图(ListView)显示,每一个链表项使用两个标签显示表达式和求值结果。 14 | 15 | ``` 16 | // part of JSConsole.qml 17 | ApplicationWindow { 18 | id: root 19 | 20 | ... 21 | 22 | ColumnLayout { 23 | anchors.fill: parent 24 | anchors.margins: 9 25 | RowLayout { 26 | Layout.fillWidth: true 27 | TextField { 28 | id: input 29 | Layout.fillWidth: true 30 | focus: true 31 | onAccepted: { 32 | // call our evaluation function on root 33 | root.jsCall(input.text) 34 | } 35 | } 36 | Button { 37 | text: qsTr("Send") 38 | onClicked: { 39 | // call our evaluation function on root 40 | root.jsCall(input.text) 41 | } 42 | } 43 | } 44 | Item { 45 | Layout.fillWidth: true 46 | Layout.fillHeight: true 47 | Rectangle { 48 | anchors.fill: parent 49 | color: '#333' 50 | border.color: Qt.darker(color) 51 | opacity: 0.2 52 | radius: 2 53 | } 54 | 55 | ScrollView { 56 | id: scrollView 57 | anchors.fill: parent 58 | anchors.margins: 9 59 | ListView { 60 | id: resultView 61 | model: ListModel { 62 | id: outputModel 63 | } 64 | delegate: ColumnLayout { 65 | width: ListView.view.width 66 | Label { 67 | Layout.fillWidth: true 68 | color: 'green' 69 | text: "> " + model.expression 70 | } 71 | Label { 72 | Layout.fillWidth: true 73 | color: 'blue' 74 | text: "" + model.result 75 | } 76 | Rectangle { 77 | height: 1 78 | Layout.fillWidth: true 79 | color: '#333' 80 | opacity: 0.2 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | 求值函数jsCall不会做求值操作,而是将它的内容移动到JS模块(jsconsole.js)完成清晰的分离。 91 | 92 | ``` 93 | // part of JSConsole.qml 94 | 95 | import "jsconsole.js" as Util 96 | 97 | ... 98 | 99 | ApplicationWindow { 100 | id: root 101 | 102 | ... 103 | 104 | function jsCall(exp) { 105 | var data = Util.call(exp); 106 | // insert the result at the beginning of the list 107 | outputModel.insert(0, data) 108 | } 109 | } 110 | ``` 111 | 112 | 为了安全我们不在JS中调用eval函数,这可能导致用户修改局部作用域。我们使用constructor函数在运行时创建JS函数,并在我们的作用域中传入变量值。由于函数可能随时都在创建,它不能扮演关闭和存储它私有作用域的角色,我们需要使用 this.a = 10 来存储值10在这个函数的作用域。这个作用域由脚本设置到变量作用域。 113 | 114 | ``` 115 | // jsconsole.js 116 | .pragma library 117 | 118 | var scope = { 119 | // our custom scope injected into our function evaluation 120 | } 121 | 122 | function call(msg) { 123 | var exp = msg.toString(); 124 | console.log(exp) 125 | var data = { 126 | expression : msg 127 | } 128 | try { 129 | var fun = new Function('return (' + exp + ');'); 130 | data.result = JSON.stringify(fun.call(scope), null, 2) 131 | console.log('scope: ' + JSON.stringify(scope, null, 2) + 'result: ' + result) 132 | } catch(e) { 133 | console.log(e.toString()) 134 | data.error = e.toString(); 135 | } 136 | return data; 137 | } 138 | ``` 139 | 140 | 调用函数返回的数据是一个JS对象,包含了一个结果,表达式和错误属性:data: { expression: {}, result: {}, error: {} }。我们可以直接在链表模型(ListModel)中使用这个JS对象,并且可以通过代理(delegate)访问它,例如model.expression会得到输入的表达式。为了让例子更加简单,我们忽略了错误的结果。 141 | 142 | 143 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 《QmlBook》In Chinese 2 | 3 | 中文版《QmlBook》,原作地址[QmlBook](http://qmlbook.github.io/index.html)。 4 | 5 | QML的中文资料一直比较少,希望大家能喜欢。 6 | 7 | # 在线阅读 8 | 9 | 使用Gitbook制作,可以直接[在线阅读](http://cwc1987.gitbooks.io/qmlbook-in-chinese/content/)。 10 | 11 | # PDF下载 12 | 13 | [点我下载](https://www.gitbook.com/download/pdf/book/cwc1987/qmlbook-in-chinese) 14 | 15 | [百度网盘-中文字体修正](http://pan.baidu.com/s/1dDnqMXV) 16 | 17 | # 当前阶段 18 | 19 | [QmlBook](http://qmlbook.github.io/index.html)上发布的课程已完成所有章节的翻译,进入第一次校正阶段,还有很多不通顺或者翻译很生硬的地方。 20 | 21 | 很多术语可能不准确,如果有什么错误希望广大Qt爱好者谅解,并及时指出。 22 | 23 | # 校对贡献 24 | 25 | 排名不分先后 26 | 27 | [**DreamerCorey**](https://github.com/DreamerCorey) 28 | 29 | [**Jakes Lee**](https://github.com/jakeslee) 30 | 31 | [**itviewer**](https://github.com/itviewer) 32 | 33 | # 课程目录 34 | 35 | 1. 初识Qt5(Meet Qt5) 36 | * 序(Preface\) 37 | * Qt5介绍(Qt5 Introduction) 38 | * Qt构建模块(Qt Building Blocks) 39 | * Qt项目(Qt Project) 40 | 2. 开始学习(Get Start) 41 | * 安装Qt5软件工具包(Installing Qt5 SDK) 42 | * 你好世界(Hello World) 43 | * 应用程序类型(Application Types) 44 | * 总结(Summary) 45 | 3. Qt Creator集成开发环境(Qt Creator IDE) 46 | * 用户界面(The User Interface) 47 | * 注册你的Qt工具箱(Registering your Qt Kit) 48 | * 使用编辑器(Managing Projects) 49 | * 定位器(Locator) 50 | * 调试(Debugging) 51 | * 快捷键(Shortcuts) 52 | 4. QML快速入门(Quick Starter) 53 | * QML语法(QML Syntax) 54 | * 基本元素(Basic Elements) 55 | * 组件(Compontents) 56 | * 简单的转换(Simple Transformations) 57 | * 定位元素(Positioning Element) 58 | * 布局元素(Layout items) 59 | * 输入元素(Input Element) 60 | * 高级用法(Advanced Techniques) 61 | 5. 动态元素(Fluid Elements) 62 | * 动画(Animations\) 63 | * 状态与过渡(States and Transitions) 64 | * 高级用法(Advanced Techniques) 65 | 6. 模型-视图-代理(Model-View-Delegate) 66 | * 概念(Concept) 67 | * 基础模型(Basic Model) 68 | * 动态视图(Dynamic Views) 69 | * 代理(Delegate) 70 | * 高级用法(Advanced Techniques) 71 | * 总结(Summary) 72 | 7. 画布元素(Canvas Element) 73 | * 便捷的接口(Convenient API) 74 | * 渐变(Gradients) 75 | * 阴影(Shadows) 76 | * 图片(Images) 77 | * 转换(Transformation) 78 | * 组合模式(Composition Mode) 79 | * 像素缓冲(Pixels Buffer) 80 | * 画布绘制(Canvas Paint) 81 | * HTML5画布移植(Porting from HTML5 Canvas) 82 | 8. 粒子模拟(Particle Simulations) 83 | * 概念(Concept) 84 | * 简单的模拟(Simple Simulation) 85 | * 粒子参数(Particle Parameters) 86 | * 粒子方向(Directed Particle) 87 | * 粒子画笔(Particle Painter) 88 | * 粒子控制(Affecting Particles) 89 | * 粒子组(Particle Group) 90 | * 总结(Summary) 91 | 9. 着色器效果(Shader Effect) 92 | * OpenGL着色器(OpenGL Shader) 93 | * 着色器元素(Shader Elements) 94 | * 片段着色器(Fragment Shader) 95 | * 波浪效果(Wave Effect) 96 | * 顶点着色器(Vertex Shader) 97 | * 剧幕效果(Curtain Effect) 98 | * Qt图像效果库(Qt GraphicsEffect Library) 99 | 10. 多媒体(Multimedia) 100 | * 媒体播放(Playing Media) 101 | * 声音效果(Sounds Effects) 102 | * 视频流(Video Streams) 103 | * 捕捉图像(Capturing Images) 104 | * 高级用法(Advanced Techniques) 105 | * 总结(Summary) 106 | 11. 网络(Networking) 107 | * 通过HTTP服务用户界面(Serving UI via HTTP) 108 | * 模板(Templating) 109 | * HTTP请求(HTTP Requests) 110 | * 本地文件(Local files) 111 | * REST接口(REST API) 112 | * 云服务(Engine IO) 113 | * Web Sockets 114 | * 总结(Summary) 115 | 12. 存储(Stgorage) 116 | * 配置(Settings) 117 | * 本地存储-SQL(Local Storage - SQL) 118 | * 其它存储接口(Other Storage APIs) 119 | 13. 动态QML(Dynamic QML) 120 | * 动态加载组件(Loading Components Dynamically) 121 | * 创建与销毁对象(Creating and Destorying Objects) 122 | * 跟踪动态对象(Tracking Dynamic Objects) 123 | * 总结(Summary) 124 | 14. JavaScript 125 | * 浏览器/HTML与QtQuick/QML对比(Browser/HTML vs QtQuick/QML) 126 | * JavaScript语法(The Language) 127 | * JS对象(JS Objects) 128 | * 创建JS控制台(Creating a JS Console) 129 | 15. Qt and C++ 130 | * 演示程序(A Boilerplate Application) 131 | * Qt对象(The QObject) 132 | * 编译系统(Build Systems) 133 | * Qt通用类(Common Qt Classes) 134 | * C++数据模型(Models in C++) 135 | 16. C++扩展QML(Extending QML with C++) 136 | * 理解QML运行环境(Understanding the QML Run-time) 137 | * 插件内容(Plugin Content) 138 | * 创建插件(Creating the plugin) 139 | * FileIO实现(FileIO Implementation) 140 | * 使用FileIO(Using FileIO) 141 | * 总结(Summary) 142 | 17. 其它(Other) 143 | * 示例源码 144 | * 术语英汉对照表 145 | * 格式定义 146 | * 协作校正 147 | 148 | # 原作者 149 | 150 | 感谢原作者Juergen Bocklage-Ryannel和Johan Thelin的分享。 151 | 152 | # 开源协议 153 | 154 | [Creative Commons Attribution Non Commercial Share Alike 4.0](http://creativecommons.org/licenses/by-nc/4.0) 155 | 156 | # 问题与建议 157 | 158 | 有任何建议可以在项目issue中提出,或者email我:cwc1987@163.com 159 | 160 | -------------------------------------------------------------------------------- /qt_and_c++/a_boilerplate_application.md: -------------------------------------------------------------------------------- 1 | # 演示程序(A Boilerplate Application) 2 | 3 | 理解Qt最好的方法是从一个小的应用程序开始。这个简单的例子叫做“Hello World!”,使用unicode编码将字符串写入到一个文件中。 4 | 5 | ``` 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | int main(int argc, char *argv[]) 15 | { 16 | QCoreApplication app(argc, argv); 17 | 18 | // prepare the message 19 | QString message("Hello World!"); 20 | 21 | // prepare a file in the users home directory named out.txt 22 | QFile file(QDir::home().absoluteFilePath("out.txt")); 23 | // try to open the file in write mode 24 | if(!file.open(QIODevice::WriteOnly)) { 25 | qWarning() << "Can not open file with write access"; 26 | return -1; 27 | } 28 | // as we handle text we need to use proper text codecs 29 | QTextStream stream(&file); 30 | // write message to file via the text stream 31 | stream << message; 32 | 33 | // do not start the eventloop as this would wait for external IO 34 | // app.exec(); 35 | 36 | // no need to close file, closes automatically when scope ends 37 | return 0; 38 | } 39 | ``` 40 | 41 | 这个简单的例子演示了文件访问的使用和通过文本流使用文本编码将文本正确的写入到文件中。二进制数据的操作有一个跨平台的二进制流类叫做QDataStream。我们使用的不同类需要使用它们的类名包含。另一种是使用模块名和类名 42 | 例如```#include ```。对于比较懒的人,有一个更加简单的方法是包含整个模块,使用```#include ```。例如在QtCore中你可以在应用程序中使用很多通用的类,这没有UI依赖。查看[QtCore class list](http://doc.qt.io/qt-5/qtcore-module.html)或者[QtCore overview](http://doc.qt.io/qt-5/qtcore-index.html)获取更多的信息。 43 | 44 | 使用qmake和make来构建程序。QMake读取项目文件(project file)并生成一个Makefile供make使用。项目文件(project file)独立于平台,qmake会根据特定平台的设置应用一些规则来生成Makefile。在有特殊需求的项目中,项目文件也可以包含特定平台规则的平台作用域。下面是一个简单的项目文件(project file)例子。 45 | 46 | ``` 47 | # build an application 48 | TEMPLATE = app 49 | 50 | # use the core module and do not use the gui module 51 | QT += core 52 | QT -= gui 53 | 54 | # name of the executable 55 | TARGET = CoreApp 56 | 57 | # allow console output 58 | CONFIG += console 59 | 60 | # for mac remove the application bundling 61 | macx { 62 | CONFIG -= app_bundle 63 | } 64 | 65 | # sources to be build 66 | SOURCES += main.cpp 67 | ``` 68 | 69 | 我们不会再继续深入这个话题,只需要记住Qt项目会使用特定的项目文件(project file),qmake会根据这些项目文件和指定平台生成Makefile。 70 | 71 | 上面简单的例子只是在应用程序中写入文本。一个命令行工具这是不够的。对于一个用户界面我们需要一个事件循环来等待用户的输入并安排刷新绘制操作。下面这个相同的例子使用一个桌面按钮来触发写入。 72 | 73 | 令人惊奇的是我们的main.cpp依然很小。我们将代码移入到我们的类中,并使用信号槽(signal/slots)来连接用用户的输入,例如按钮点击。信号槽(signal/slot)机制通常需要一个对象,你很快就会看到。 74 | 75 | ``` 76 | #include 77 | #include 78 | #include 79 | #include "mainwindow.h" 80 | 81 | 82 | int main(int argc, char** argv) 83 | { 84 | QApplication app(argc, argv); 85 | 86 | MainWindow win; 87 | win.resize(320, 240); 88 | win.setVisible(true); 89 | 90 | return app.exec(); 91 | } 92 | ``` 93 | 94 | 在main函数中我们简单的创建了一个应用程序对象,并使用exec()开始事件循环。现在应用程序放在了事件循环中,并等待用户输入。 95 | 96 | ``` 97 | int main(int argc, char** argv) 98 | { 99 | QApplication app(argc, argv); // init application 100 | 101 | // create the ui 102 | 103 | return app.exec(); // execute event loop 104 | } 105 | ``` 106 | 107 | Qt提供了几种UI技术。这个例子中我们使用纯Qt C++的桌面窗口用户界面库。我们需要创建一个主窗口来放置一个触发功能的按钮,同事由主窗口来实现我们的核心功能,正如我们在上面例子上看到的。 108 | 109 | ![](http://qmlbook.github.io/_images/storecontent.png) 110 | 111 | 主窗口本身也是一个窗口,它是一个没有父对象的窗口。这与Qt如何定义用户界面为一个界面元素树类似。在这个例子中,主窗口是我们的根元素,按钮是主窗口上的一个子元素。 112 | 113 | ``` 114 | #ifndef MAINWINDOW_H 115 | #define MAINWINDOW_H 116 | 117 | #include 118 | 119 | class MainWindow : public QMainWindow 120 | { 121 | public: 122 | MainWindow(QWidget* parent=0); 123 | ~MainWindow(); 124 | public slots: 125 | void storeContent(); 126 | private: 127 | QPushButton *m_button; 128 | }; 129 | 130 | #endif // MAINWINDOW_H 131 | ``` 132 | 133 | 此外我们定义了一个公有槽函数```storeContent()```,当点击按钮时会调用这个函数。槽函数是一个C++方法,这个方法被注册到Qt的源对象系统中,可以被动态调用。 134 | 135 | ``` 136 | #include "mainwindow.h" 137 | 138 | MainWindow::MainWindow(QWidget *parent) 139 | : QMainWindow(parent) 140 | { 141 | m_button = new QPushButton("Store Content", this); 142 | 143 | setCentralWidget(m_button); 144 | connect(m_button, &QPushButton::clicked, this, &MainWindow::storeContent); 145 | } 146 | 147 | MainWindow::~MainWindow() 148 | { 149 | 150 | } 151 | 152 | void MainWindow::storeContent() 153 | { 154 | qDebug() << "... store content"; 155 | QString message("Hello World!"); 156 | QFile file(QDir::home().absoluteFilePath("out.txt")); 157 | if(!file.open(QIODevice::WriteOnly)) { 158 | qWarning() << "Can not open file with write access"; 159 | return; 160 | } 161 | QTextStream stream(&file); 162 | stream << message; 163 | } 164 | ``` 165 | 166 | 在主窗口中我们首先创建了一个按钮,并将```clicked()```信号与```storeContent()```槽连接起来。每点击信号发送时都会触发调用槽函数```storeContent()```。就是这么简单,通过信号与槽的机制实现了松耦合的对象通信。 167 | -------------------------------------------------------------------------------- /storage/local_storage_-_sql.md: -------------------------------------------------------------------------------- 1 | # 本地存储 - SQL(Local Storage - SQL) 2 | 3 | Qt Quick支持一个与浏览器由区别的本地存储编程接口。需要使用"import QtQuick.LocalStorage 2.0"语句来导入后才能使用这个编程接口。 4 | 5 | 通常使用基于给定的数据库名称和版本号使用系统特定位置的唯一文件ID号来存储数据到一个SQLITE数据库中。无法列出或者删除已有的数据库。你可以使用QQmlEngine::offlineStoragePate()来寻找本地存储。 6 | 7 | 使用这个编程接口你首选需要创建一个数据库对象,然后在这个数据库上创建数据库事务。每个事务可以包含一个或多个SQL查询。当一个SQL查询在事务中失败后,事务会回滚。 8 | 9 | 例如你可以使用本地存储从一个简单的注释表中读取一个文本列: 10 | 11 | ``` 12 | import QtQuick 2.2 13 | import QtQuick.LocalStorage 2.0 14 | 15 | Item { 16 | Component.onCompleted: { 17 | var db = LocalStorage.openDatabaseSync("MyExample", "1.0", "Example database", 10000); 18 | db.transaction( function(tx) { 19 | var result = tx.executeSql('select * from notes'); 20 | for(var i = 0; i < result.rows.length; i++) { 21 | print(result.rows[i].text); 22 | } 23 | } 24 | }); 25 | } 26 | } 27 | ``` 28 | 29 | ## 疯狂的矩形框(Crazy Rectangle) 30 | 31 | 假设我们想要存储一个矩形在场景中的位置。 32 | 33 | ![](http://qmlbook.github.io/_images/crazy_rect.png) 34 | 35 | 下面是我们的基础代码。 36 | 37 | ``` 38 | import QtQuick 2.2 39 | 40 | Item { 41 | width: 400 42 | height: 400 43 | 44 | Rectangle { 45 | id: crazy 46 | objectName: 'crazy' 47 | width: 100 48 | height: 100 49 | x: 50 50 | y: 50 51 | color: "#53d769" 52 | border.color: Qt.lighter(color, 1.1) 53 | Text { 54 | anchors.centerIn: parent 55 | text: Math.round(parent.x) + '/' + Math.round(parent.y) 56 | } 57 | MouseArea { 58 | anchors.fill: parent 59 | drag.target: parent 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | 可以自由的拖动这个矩形。当关闭这个应用程序并再次打开时,这个矩形框仍然在相同的位置。 66 | 67 | 现在我们将添加矩形的x/y坐标值存储到SQL数据库中。首先我们需要添加一个初始化、读取和保存数据库功能。这些功能在组件构造和组件销毁时被调用。 68 | 69 | ``` 70 | import QtQuick 2.2 71 | import QtQuick.LocalStorage 2.0 72 | 73 | Item { 74 | // reference to the database object 75 | property var db; 76 | 77 | function initDatabase() { 78 | // initialize the database object 79 | } 80 | 81 | function storeData() { 82 | // stores data to DB 83 | } 84 | 85 | function readData() { 86 | // reads and applies data from DB 87 | } 88 | 89 | 90 | Component.onCompleted: { 91 | initDatabase(); 92 | readData(); 93 | } 94 | 95 | Component.onDestruction: { 96 | storeData(); 97 | } 98 | } 99 | ``` 100 | 101 | 你也可以调用已有的JS库提取相关数据库代码来完成所有的逻辑。如果这个逻辑变得更加复杂这将是最好的解决方案。 102 | 103 | 在数据库初始化函数中,我们创建一个数据库对象并且确保SQL表已经被创建。 104 | 105 | ``` 106 | function initDatabase() { 107 | print('initDatabase()') 108 | db = LocalStorage.openDatabaseSync("CrazyBox", "1.0", "A box who remembers its position", 100000); 109 | db.transaction( function(tx) { 110 | print('... create table') 111 | tx.executeSql('CREATE TABLE IF NOT EXISTS data(name TEXT, value TEXT)'); 112 | }); 113 | } 114 | ``` 115 | 116 | 应用程序下一步调用读取函数来读取数据库中已有的数据。这里我们需要区分数据库表中是否已有数据。我们观察有多少条语句返回来检查是否已有数据。 117 | 118 | ``` 119 | function readData() { 120 | print('readData()') 121 | if(!db) { return; } 122 | db.transaction( function(tx) { 123 | print('... read crazy object') 124 | var result = tx.executeSql('select * from data where name="crazy"'); 125 | if(result.rows.length === 1) { 126 | print('... update crazy geometry') 127 | // get the value column 128 | var value = result.rows[0].value; 129 | // convert to JS object 130 | var obj = JSON.parse(value) 131 | // apply to object 132 | crazy.x = obj.x; 133 | crazy.y = obj.y; 134 | } 135 | }); 136 | } 137 | ``` 138 | 139 | 我们希望在将数据作为一个JSON字符串存储在一列值中。这与典型的SQL不同,但是可以很好的与JS代码结合。所以我们将它存储为一个JS对象的可以使用JSON stringif/parse方法的数据替代使用x,y作为属性值放在数据库表中。最后我们获取一个包含x,y属性有效的JS对象,我们可以将它应用在我们疯狂的矩形中。 140 | 141 | 为了保存数据,我们需要区分更新和插入的情况。当一个记录已经存在时我们使用更新,如果没有记录则将它插入在“crazy”下。 142 | 143 | ``` 144 | function storeData() { 145 | print('storeData()') 146 | if(!db) { return; } 147 | db.transaction( function(tx) { 148 | print('... check if a crazy object exists') 149 | var result = tx.executeSql('SELECT * from data where name = "crazy"'); 150 | // prepare object to be stored as JSON 151 | var obj = { x: crazy.x, y: crazy.y }; 152 | if(result.rows.length === 1) {// use update 153 | print('... crazy exists, update it') 154 | result = tx.executeSql('UPDATE data set value=? where name="crazy"', [JSON.stringify(obj)]); 155 | } else { // use insert 156 | print('... crazy does not exists, create it') 157 | result = tx.executeSql('INSERT INTO data VALUES (?,?)', ['crazy', JSON.stringify(obj)]); 158 | } 159 | }); 160 | } 161 | ``` 162 | 163 | 替代选择所有记录的设置,我们也可以使用SQLITE计数函数:SELECT COUNT(*) from data where name = "crazy",将返回使用行选择查询的结果。否则这将是一个通用的SQL代码。所谓额外的特性,我们在查询中使用?绑定SQL值。 164 | 165 | 现在你可与拖动这个矩形框当你退出应用程序时会将x/y坐标值存储到数据库,下次启动应用程序时矩形框将使用存储的x/y坐标值定位。 166 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------