Turn Jos' blog posts into a mdbook-based tutorial

Summary:
How to create the tutorial:
$ cargo install mdbook
$ cd tutorial
$ mdbook build

Then open build/tutorial/index.html in a web browser.

See also http://rust-lang-nursery.github.io/mdBook/

Reviewers: vandenoever

Reviewed By: vandenoever

Differential Revision: https://phabricator.kde.org/D9368
master
Friedrich W. H. Kossebau 2017-12-17 12:45:20 +01:00
parent 6bfe86d904
commit a3e02d39ec
11 changed files with 642 additions and 0 deletions

3
.gitignore vendored
View File

@ -3,3 +3,6 @@
/docker_home/
/tests/rust_*/Cargo.lock
/tests/rust_*/target/
# tutorial build
/build/tutorial/

5
tutorial/SUMMARY.md Normal file
View File

@ -0,0 +1,5 @@
# Summary
[Rust Qt Binding Generator](./rust_qt_binding_generator.md)
- [Rust and QML: a timely example](./time_for_rust_and_qml.md)

8
tutorial/book.toml Normal file
View File

@ -0,0 +1,8 @@
[book]
title = "The official Rust Qt Binding Generator Guide"
author = "Jos van den Oever"
description = "Giving Rust a cute wrapping!"
src = "."
[build]
build-dir = "../build/tutorial"

BIN
tutorial/demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
tutorial/demo2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
tutorial/demo3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
tutorial/happy_time.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,177 @@
# Rust Qt Binding Generator
![Rust Qt Binding Generator (Logo by Alessandro Longo)](rust_qt_binding_generator.svg)
This code generator gets you started quickly to use Rust code from Qt and QML. In other words, it helps to create a Qt based GUI on top of Rust code.
Qt is a mature cross-platform graphical user interface library. Rust is a new programming language with strong compile time checks and a modern syntax.
## Getting started
There are two template projects that help you to get started quickly. One for [Qt Widgets](https://commits.kde.org/rust-qt-binding-generator?path=templates/qt_widgets) and one for [Qt Quick](https://commits.kde.org/rust-qt-binding-generator?path=templates/qt_quick). Just copy these folders as new project and start coding.
Here is a small schematic of how files made by the generator are related:
<table>
<tr>
<td style="background:#676767;color:white;padding:1ex;">Qt Widgets (main.cpp) / Qt Quick (main.qml)</td>
<td>&#10229; UI code, written by hand</td>
</tr>
<tr>
<td style="background:#3daefd;padding:1ex;">src/Binding.h</td>
<td rowspan="3" style="vertical-align:middle;">&#10229; generated from<br/>binding.json</td>
</tr>
<tr>
<td style="background:#3daefd;padding:1ex;">src/Binding.cpp</td>
</tr>
<tr>
<td style="background:#3daefd;padding:1ex;">rust/src/interface.rs</td>
</tr>
<tr>
<td style="background:#676767;color:white;padding:1ex">rust/src/implementation.rs</td>
<td>&#10229; Rust code, written by hand</td>
</tr>
</table>
To combine Qt and Rust, write an interface in a JSON file. From that, the generator creates Qt code and Rust code. The Qt code can be used directly. The Rust code has two files: interface and implementation. The interface can be used directly.
```json
{
"cppFile": "src/Binding.cpp",
"rust": {
"dir": "rust",
"interfaceModule": "interface",
"implementationModule": "implementation"
},
"objects": {
"Greeting": {
"type": "Object",
"properties": {
"message": {
"type": "QString",
"write": true
}
}
}
}
}
```
This file describes an binding with one object, `Greeting`. `Greeting` has one property: `message`. It is a writable property.
The Rust Qt Binding Generator will create binding source code from this description:
```
$ rust_qt_binding_generator binding.json
```
This will create four files:
* *src/Binding.h*
* *src/Binding.cpp*
* *rust/src/interface.rs*
* rust/src/implementation.rs
Only `implementation.rs` should be changed. The other files are the binding. `implementation.rs` is initialy created with a simple implementation that is shown here with some comments.
```rust
// rust/src/implementation.rs
use interface::*;
/// A Greeting
pub struct Greeting {
/// Emit signals to the Qt code.
emit: GreetingEmitter,
/// The message of the greeting.
message: String,
}
/// Implementation of the binding
/// GreetingTrait is defined in interface.rs
impl GreetingTrait for Greeting {
/// Create a new greeting with default data.
fn new(emit: GreetingEmitter) -> Greeting {
Greeting {
emit: emit,
message: "Hello World!".into(),
}
}
/// The emitter can emit signals to the Qt code.
fn emit(&self) -> &GreetingEmitter {
&self.emit
}
/// Get the message of the Greeting
fn message(&self) -> &str {
&self.message
}
/// Set the message of the Greeting
fn set_message(&mut self, value: String) {
self.message = value;
self.emit.message_changed();
}
}
```
The building block of Qt and QML projects are QObject and the Model View classes. `rust_qt_binding_generator` reads a JSON file to generate QObject or QAbstractItemModel classes that call into generated Rust files. For each type from the JSON file, a Rust trait is generated that should be implemented.
This way, Rust code can be called from Qt and QML projects.
### Qt Widgets with Rust
This C++ code uses the Rust code written above.
```cpp
#include "Binding.h"
#include <QDebug>
int main() {
Greeting greeting;
qDebug() << greeting.message();
return 0;
}
```
### Qt Quick with Rust
This Qt Quick (QML) code uses the Rust code written above.
```qml
Rectangle {
Greeting {
id: rust
}
Text {
text: rust.message
}
}
```
## Demo application
The project comes with a demo application that show a Qt user interface based on Rust. It uses all of the features of Object, List and Tree. Reading the demo code is a good way to get started.
<figure>
<img src="demo.png" alt="Qt Widgets UI with Rust logic"/>
<figcaption><a href="https://commits.kde.org/rust-qt-binding-generator?path=demo/src/main.cpp">Qt Widgets UI</a> with <a href="https://commits.kde.org/rust-qt-binding-generator?path=demo/rust/src/implementation/file_system_tree.rs">Rust logic</a></figcaption>
</figure>
<figure>
<img src="demo2.png" alt="Qt Quick Controls UI with Rust logic"/>
<figcaption><a href="https://commits.kde.org/rust-qt-binding-generator?path=demo/qml/DataAndChart.qml">Qt Quick Controls UI</a> with <a href="https://commits.kde.org/rust-qt-binding-generator?path=demo/rust/src/implementation/time_series.rs">Rust logic</a></figcaption>
</figure>
<figure>
<img src="demo3.png" alt="Qt Quick Controls 2 UI with Rust logic"/>
<figcaption><a href="https://commits.kde.org/rust-qt-binding-generator?path=demo/qml/FileTreeView2.qml">Qt Quick Controls 2 UI</a> with <a href="https://commits.kde.org/rust-qt-binding-generator?path=demo/rust/src/implementation/file_system_tree.rs">Rust logic</a></figcaption>
</figure>
## Docker development environment
To get started quickly, the project comes with a `Dockerfile`. You can start a docker session with the required dependencies with `./docker/docker-bash-session.sh`.
## More information
* [Rust Qt Binding Generator](https://cgit.kde.org/rust-qt-binding-generator.git/about)
* [Qt](http://doc.qt.io/)
* [Qt Examples and tutorials](http://doc.qt.io/qt-5/qtexamplesandtutorials.html)
* [The QML Book](https://qmlbook.github.io/)

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
width="55.309204mm"
height="54.466091mm"
viewBox="0 0 195.97749 192.99009"
version="1.1">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Rust Qt Binding Generator</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(-41.773428,-1.8383977)">
<path style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#676767;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 189.73686,108.82684 0,0.002 -4.79574,0.17474 c -0.20222,0.008 -0.42562,0.19613 -0.46723,0.38936 l -1.48526,6.99514 c -0.2499,0.0749 -0.4867,0.19274 -0.73313,0.28109 l 0,-0.0361 c -0.87315,0.2957 -1.69397,0.59808 -2.49569,1.01993 l -0.0969,0 -6.43294,-4.07021 c -0.0875,-0.0535 -0.19078,-0.10069 -0.28869,-0.0912 l 0,-0.004 c -0.0977,0 -0.21501,0.0522 -0.28869,0.0931 l -3.35988,3.33138 c -0.14313,0.1447 -0.1999,0.386 -0.095,0.5546 l 2.67422,4.27343 -0.22412,0.0627 c 0.10783,-0.0574 0.22486,-0.007 0.29249,0.0855 0,0 0.13408,0.17982 0.13865,0.18613 l 1.05411,1.68278 c -0.70677,1.14474 -1.22667,2.39687 -1.6315,3.69795 l -7.29713,1.48336 c -0.20348,0.0535 -0.38556,0.17059 -0.38556,0.37226 l 0,4.62481 c 0,0.19502 0.18974,0.41404 0.38556,0.46153 l 7.1034,1.66569 c 0.37837,1.50839 0.95479,2.9414 1.73026,4.25444 l -4.12908,6.01319 c -0.11457,0.16982 -0.0563,0.41461 0.095,0.5565 l 3.36366,3.23641 c 0.14313,0.13215 0.3989,0.20196 0.57359,0.095 l 6.43294,-3.79482 c 1.26503,0.70309 2.59217,1.29075 4.03413,1.6657 l 1.53653,6.94005 c 0.0439,0.20134 0.17646,0.37037 0.38367,0.37037 l 4.80144,0 c 0.20235,0 0.43214,-0.17883 0.48052,-0.37037 l 1.72647,-6.94005 c 0.86115,-0.22398 1.69714,-0.54586 2.50898,-0.90787 0.60279,-0.24323 1.18981,-0.49758 1.75686,-0.8205 l 6.29998,4.04363 c 0.17447,0.10694 0.42528,0.0528 0.5736,-0.095 l 0.0684,-0.0665 3.37696,-3.33138 0.19563,-0.20702 c 0.13801,-0.1499 0.18673,-0.39564 0.076,-0.56029 l -4.15377,-6.14236 c 0.66604,-1.16987 1.14166,-2.43986 1.50044,-3.75492 l 7.24015,-1.74736 c 0.2015,-0.0609 0.37934,-0.18405 0.37227,-0.38556 l -0.16335,-4.62481 c -0.008,-0.19491 -0.20339,-0.40415 -0.40075,-0.44444 l -7.15848,-1.40737 c -0.43119,-1.49359 -1.05723,-2.90225 -1.87841,-4.18607 l 3.91637,-6.16325 c 0.10872,-0.17395 0.0402,-0.41458 -0.11586,-0.5508 l -3.47383,-3.11295 c -0.14769,-0.12669 -0.41037,-0.18356 -0.58119,-0.0703 l -6.29429,4.02463 c -1.28893,-0.65634 -2.63495,-1.19558 -4.0892,-1.51755 l -1.77965,-6.87548 c -0.0511,-0.1996 -0.18987,-0.36657 -0.39695,-0.35896 z m -2.23738,10.0853 c 0.22279,-0.008 0.44034,0.006 0.66096,0.008 l -2.73121,10.53504 a 3.6892254,3.6892254 0 0 0 -0.5261,0.63817 l -3.2725,-9.17744 -2.70461,1.25733 c 2.30542,-1.92882 5.28506,-3.1407 8.57346,-3.2611 z m 6.36076,1.23644 c 4.52582,2.02548 7.74095,6.33492 7.92201,11.4775 0.0619,1.76001 -0.25405,3.44454 -0.85659,5.00087 l -9.65796,-6.19743 a 3.6892254,3.6892254 0 0 0 -0.44254,-0.66666 l 3.03508,-9.61428 z m -17.32544,4.62671 7.12618,19.98258 c -5.54023,-1.71508 -9.56869,-6.6762 -9.56869,-12.581 0,-2.02836 0.47587,-3.95019 1.32382,-5.66941 0.0285,0.0375 0.0245,0.0286 0.0532,0.0665 0.30541,-0.62882 0.66537,-1.22807 1.06552,-1.79865 z m 11.4509,6.32468 a 1.014537,1.014537 0 0 1 1.01423,1.01423 1.014537,1.014537 0 0 1 -1.01423,1.01423 1.014537,1.014537 0 0 1 -1.01423,-1.01423 1.014537,1.014537 0 0 1 1.01423,-1.01423 z m -1.10729,4.53174 a 3.6892254,3.6892254 0 0 0 1.10729,0.17094 3.6892254,3.6892254 0 0 0 0.99144,-0.13675 l 7.73586,5.76249 3.29719,-2.91163 c -0.19496,0.33839 -0.39898,0.67136 -0.62107,0.99144 -0.1236,0.17557 -0.24988,0.34895 -0.38176,0.5185 -0.22128,0.28786 -0.45467,0.56469 -0.69894,0.8338 -0.20319,0.22199 -0.41842,0.43328 -0.63627,0.64196 -0.21715,0.20905 -0.43541,0.41501 -0.66665,0.60968 -0.2649,0.2224 -0.54228,0.42956 -0.82431,0.63247 -0.22414,0.16119 -0.4474,0.32476 -0.68184,0.47292 -0.28816,0.1823 -0.59011,0.34346 -0.89268,0.50522 -1.98433,1.05695 -4.254,1.68278 -6.68555,1.68278 -0.87748,0 -1.73307,-0.0878 -2.56596,-0.23741 l 4.05691,-2.43111 -2.53366,-7.1053 z"
/>
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3daefd;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 74.916016,27.875 c -30.483373,3.42e-4 -55.194972,24.711944 -55.195313,55.195312 -6.39e-4,26.399248 18.537675,48.457208 43.302735,53.898438 l 75.003902,21.48242 0.0371,-0.0586 c 2.6883,0.81145 5.53728,1.25384 8.49024,1.2539 16.20697,-3.5e-4 29.34535,-13.13866 29.3457,-29.3457 -1.6e-4,-7.49845 -2.81581,-14.33601 -7.44336,-19.52344 l 0.0273,-0.0449 -5.31055,-6.52148 0.082,-0.11328 c -0.0846,-0.0305 -0.16747,-0.0649 -0.25195,-0.0957 L 119.68164,50.798828 c -1.27746,-1.768834 -2.65174,-3.46366 -4.125,-5.066406 l -0.66406,-0.814453 -0.0449,0.07227 C 104.79564,34.452609 90.628793,27.875175 74.916016,27.875 Z m 2.18164,4.960938 0.962891,0.01172 c 2.03161,0.122268 4.054771,0.369045 6.056641,0.736328 l 0.953124,0.173828 0.546876,7.800782 c 1.503994,0.370794 2.986663,0.82329 4.441406,1.355468 l 5.023437,-5.919921 0.863281,0.40039 c 1.83118,0.850207 3.61034,1.809109 5.326168,2.873047 l 0.82032,0.509766 -2.228519,7.214844 c 1.338359,0.945495 2.621089,1.967375 3.841799,3.060546 l 6.69726,-3.636718 0.66016,0.693359 c 1.37523,1.443945 2.66316,2.969546 3.85742,4.566406 l 0.57617,0.769531 -3.71679,5.791016 2.55859,3.5625 6.5918,-0.99414 5.2832,6.4375 -4.08789,5.445312 3.14062,3.728516 6.97657,-1.517578 3.2207,3.884765 -3.40039,5.419922 3.41797,4.095703 6.19726,-2.238281 3.59375,4.576172 -3.36718,4.798828 3.55273,4.345701 5.72461,-1.57617 3.25,3.89453 -2.10937,5.38672 4.21484,2.98437 4.79883,-2.31836 1.0039,0.9668 c 0.11667,0.0611 0.2323,0.12511 0.34375,0.2207 l 0.01,0.008 3.56836,3.19922 c 0.57048,0.49799 0.87231,1.35116 0.35937,2.17187 l -0.002,0.004 -3.67969,5.78711 c 0.5578,0.97995 1.02145,2.00816 1.38477,3.08398 l 6.74609,1.32617 0.006,0 c 0.43873,0.0896 0.71945,0.29744 0.95117,0.55078 0.23172,0.25335 0.44084,0.57926 0.46094,1.08203 l 0,0.004 0.16602,4.75781 c 0.0168,0.4777 -0.23931,0.96182 -0.50977,1.21289 -0.27047,0.25108 -0.52946,0.35423 -0.75976,0.42383 l -0.0332,0.0117 -6.86328,1.6543 c -0.27835,0.89809 -0.63103,1.77688 -1.05469,2.63281 l 3.91016,5.78125 c 0.53045,0.78855 0.31034,1.57187 -0.19531,2.12109 l -0.006,0.008 -0.22266,0.23437 -3.49023,3.44336 -0.0664,0.0625 c -0.54551,0.54143 -1.33021,0.76141 -2.12305,0.27539 l -0.01,-0.008 -5.91211,-3.79297 c -0.40748,0.19669 -0.81787,0.4079 -1.21484,0.56836 -0.63744,0.28379 -1.33045,0.50697 -2.02149,0.72852 l -1.61523,6.49804 -0.002,0.002 c -0.11759,0.4655 -0.36161,0.73709 -0.63281,0.94726 -0.2712,0.21017 -0.59734,0.37891 -1.07226,0.37891 l -4.9375,0 c -0.44902,0 -0.89781,-0.22405 -1.16211,-0.5 -0.2643,-0.27596 -0.39178,-0.5766 -0.45508,-0.86719 l -1.43946,-6.50781 c -1.04018,-0.33008 -2.02654,-0.73787 -2.95898,-1.21875 l -6.07031,3.58008 c -0.42481,0.26019 -0.86392,0.27012 -1.20313,0.20312 l -3.79882,-1.91602 -0.26172,-5.83398 -5.20899,-1.78906 -4.12695,5.16601 -3.11997,-0.82092 -0.13242,-6.96256 -5.08745,-1.53454 -4.45982,5.97171 -4.01701,-1.08946 -0.570311,-6.8125 -4.873049,-1.2539 -4.291015,4.93359 -3.167706,-0.89821 -0.521485,-6.75 -4.802734,-1.51954 -4.244141,5.38672 -0.36914,0.66407 -0.740235,-0.18164 c -1.95137,-0.47835 -2.694398,-0.61883 -4.574218,-1.32813 l -0.625,-6.58789 -4.689454,-1.14063 -4.246093,5.84571 -0.929688,-0.24805 c -1.92329,-0.51434 -3.815216,-1.14239 -5.664062,-1.88086 l -0.888672,-0.35547 0.970703,-8.4082 c -1.271912,-0.64835 -2.510452,-1.36018 -3.710938,-2.13281 l -6.55664,5.32617 -0.779297,-0.57617 c -1.619784,-1.1951 -3.165804,-2.48481 -4.630859,-3.86524 l -0.691407,-0.65234 4.007813,-7.61524 c -0.902052,-1.00445 -1.755879,-2.05122 -2.558594,-3.13671 l -8.234375,2.66211 -0.517578,-0.82227 c -1.079763,-1.71692 -2.055148,-3.4974 -2.919922,-5.33203 l -0.402344,-0.85547 6.533203,-5.691404 c -0.468896,-1.219976 -0.881443,-2.46087 -1.236328,-3.71875 l -8.613281,-0.488282 -0.195312,-0.9375 c -0.41899,-2.001708 -0.716249,-4.028595 -0.888672,-6.066406 l -0.08008,-0.953125 8.183593,-2.960937 c -0.0014,-0.04232 -0.0027,-0.08463 -0.0039,-0.126953 0.0087,-1.237162 0.07127,-2.473359 0.1875,-3.705079 l -7.865234,-3.574218 0.152344,-0.94336 c 0.3308,-2.030884 0.786451,-4.038528 1.363281,-6.013672 l 0.269531,-0.921874 8.660156,0.193359 c 0.44852,-1.193118 0.950329,-2.36552 1.503906,-3.513672 L 31.0625,58.285156 31.550781,57.458984 c 1.051739,-1.777872 2.212442,-3.487839 3.47461,-5.123046 l 0.583984,-0.757813 7.802734,3.228516 c 0.867397,-0.994308 1.780489,-1.947809 2.736329,-2.857422 l -3.333985,-7.775391 0.757813,-0.59375 c 1.61997,-1.266857 3.315386,-2.43296 5.078125,-3.492187 l 0.816406,-0.490235 5.966797,5.689453 c 1.220707,-0.647672 2.472185,-1.235605 3.75,-1.761718 l -0.289063,-8.19336 0.902344,-0.287109 c 1.95328,-0.623891 3.942251,-1.128721 5.957031,-1.509766 l 0.951172,-0.179687 3.431641,7.298828 c 1.450035,-0.1921 2.908998,-0.309443 4.371093,-0.351563 l 2.056641,-5.923828 z"
transform="translate(41.77342,1.8383977)"
/>
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#676767;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 119.89976,36.275898 -2.87305,8.277344 a 40.191879,40.191879 0 0 0 -0.16601,-0.0059 40.191879,40.191879 0 0 0 -5.08789,0.363282 l -3.74805,-7.972657 a 48.53965,48.53965 0 0 0 -5.74805,1.455079 l 0.3125,8.824218 a 40.191879,40.191879 0 0 0 -4.718746,2.115235 l -6.361328,-6.06836 a 48.53965,48.53965 0 0 0 -4.902344,3.371094 l 3.5,8.166016 a 40.191879,40.191879 0 0 0 -3.583984,3.652343 l -8.154297,-3.375 a 48.53965,48.53965 0 0 0 -3.353516,4.945313 l 6.144532,6.279297 a 40.191879,40.191879 0 0 0 -2.041016,4.722656 l -8.841797,-0.197266 a 48.53965,48.53965 0 0 0 -1.316406,5.802735 l 8.007812,3.638672 a 40.191879,40.191879 0 0 0 -0.298828,4.46875 40.191879,40.191879 0 0 0 0.03711,0.722656 l -8.302735,3.001953 a 48.53965,48.53965 0 0 0 0.855469,5.851562 l 8.744141,0.498047 a 40.191879,40.191879 0 0 0 1.644531,5.042969 l -6.605469,5.753894 a 48.53965,48.53965 0 0 0 2.816406,5.14649 l 8.314454,-2.6875 a 40.191879,40.191879 0 0 0 3.460937,4.1875 l -4.0625,7.7207 a 48.53965,48.53965 0 0 0 4.466797,3.73242 l 6.699219,-5.44336 a 40.191879,40.191879 0 0 0 4.873047,2.73047 l -0.998047,8.64063 a 48.53965,48.53965 0 0 0 5.464848,1.81445 l 4.27539,-7.48242 a 40.191879,40.191879 0 0 0 5.66211,0.85937 l 2.17187,8.3418 a 48.53965,48.53965 0 0 0 0.58984,0.0293 48.53965,48.53965 0 0 0 5.10743,-0.32617 l 1.27734,-8.49024 a 40.191879,40.191879 0 0 0 5.6875,-1.28906 l 5.02344,6.9707 a 48.53965,48.53965 0 0 0 5.17578,-2.29297 l -1.88086,-8.39843 a 40.191879,40.191879 0 0 0 4.85156,-3.33594 l 7.17969,4.67187 a 48.53965,48.53965 0 0 0 3.98633,-4.00976 l -4.75391,-7.10156 a 40.191879,40.191879 0 0 0 3.27148,-4.92579 l 8.39258,1.76758 a 48.53965,48.53965 0 0 0 2.23828,-5.20117 l -7.02148,-4.921866 a 40.191879,40.191879 0 0 0 1.27539,-5.726563 l 8.50391,-1.394531 a 48.53965,48.53965 0 0 0 0.22656,-4.535156 48.53965,48.53965 0 0 0 -0.0391,-1.13086 L 156.888,81.461405 a 40.191879,40.191879 0 0 0 -0.89258,-5.712891 l 7.44922,-4.384765 a 48.53965,48.53965 0 0 0 -1.91406,-5.386719 l -8.61914,1.109375 a 40.191879,40.191879 0 0 0 -2.80664,-4.923828 l 5.35156,-6.759766 a 48.53965,48.53965 0 0 0 -3.72266,-4.408203 l -7.70312,4.181641 a 40.191879,40.191879 0 0 0 -4.30859,-3.4375 l 2.5664,-8.304688 a 48.53965,48.53965 0 0 0 -5.13867,-2.771484 l -5.64453,6.652343 a 40.191879,40.191879 0 0 0 -5.14844,-1.583984 l -0.61328,-8.746094 a 48.53965,48.53965 0 0 0 -5.84375,-0.708984 z m -3.04101,15.199219 a 33.263943,33.263943 0 0 1 33.26562,33.261719 33.263943,33.263943 0 0 1 -7.75391,21.343744 c -0.78455,0.25317 -1.99028,0.48593 -2.6621,0.39453 -3.09393,-0.80702 -3.86965,-2.8554 -4.3711,-5.38867 -0.28811,-5.695707 -2.1749,-10.84697 -4.1875,-13.886714 3.89469,-1.454145 10.59461,1.004658 13.11133,-7.65039 1.91358,-7.817579 -2.2951,-12.595602 -8.57422,-14.289063 -11.78273,-3.089078 -23.53934,-6.289164 -35.20312,-9.427734 a 33.263943,33.263943 0 0 1 16.375,-4.357422 z m -22.382817,8.707031 -0.824219,3.076172 10.244146,2.74414 -7.718755,28.808594 -10.244141,-2.74414 -0.734375,2.742187 A 33.263943,33.263943 0 0 1 83.595073,84.736836 33.263943,33.263943 0 0 1 94.475933,60.182148 Z m 17.103517,7.878906 14.67773,3.933594 c 2.0436,0.547578 4.28242,2.794174 3.50977,5.677734 -0.64159,2.394425 -3.07103,3.702384 -6.19531,2.865235 l -14.31055,-3.832031 2.31836,-8.644532 z m 5.28125,15.357422 a 1.3176933,1.3176933 0 0 1 1.31836,1.31836 1.3176933,1.3176933 0 0 1 -1.31836,1.318359 1.3176933,1.3176933 0 0 1 -1.31836,-1.318359 1.3176933,1.3176933 0 0 1 1.31836,-1.31836 z m -9.91211,1.925781 5.59375,1.464844 a 4.7916117,4.7916117 0 0 0 4.31836,2.720703 4.7916117,4.7916117 0 0 0 2.74609,-0.873047 l 0.24219,0.06445 c 0.76964,0.200741 3.08395,2.134145 4.1875,7.142579 0.43345,2.715418 1.40174,6.652834 1.9043,8.546874 0.64774,2.01804 1.29577,7.9852 3.46484,8.5664 1.47531,0.38315 2.37476,0.6284 3.41406,0.90625 a 33.263943,33.263943 0 0 1 -15.96093,4.11914 33.263943,33.263943 0 0 1 -28.42188,-16.04883 c 4.513807,1.22275 15.09443,4.09221 23.35156,6.30469 l 2.31446,-8.644524 -10.24219,-2.744141 3.08789,-11.525391 z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

BIN
tutorial/time.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,415 @@
# Time for QML
If you are new to QML, I can recommend the [QML book](https://qmlbook.github.io/). It walks you through many wonderful examples. In addition, Qt Creator comes with [many more](https://doc.qt.io/qt-5/qtexamples.html).
It is a tradition in KDE to use clocks as examples. I will follow this tradition and create a widget that shows the time.
We'll start without any Rust at all. The first code is only QML. It uses the Rust logo as SVG image for the background, [`rust-logo-blk.svg`](https://www.rust-lang.org/logos/rust-logo-blk.svg). So download this file (or pick another SVG image and store it with that name).
The syntax of QML is declarative. The rotation of the Rust logo is given by the statement `angle: time.second * 6`. This is a binding. The value of `angle` updates automatically whenever `time.second` changes. The rotation of the logo changes every second because of this declarative binding.
Another example is `anchors.fill: parent` on the `Image` item. This means that the image takes up the same rectangular space as the parent item. If that item is resized, the image will scale along with it.
In this file, we added a temporary `QtObject` with properties `hour`, `minute` and `second`. The values in this object are updated every second by the `Timer` item. The JavaScript code between `{}` runs every second and updates the values in the `QtObject`. This object has `id: time` and the logo and hands are bound to this object.
The `QtObject` is a functional placeholder for the Rust code that we are going to write later.
```qml
// just_qml.qml
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
width: 512
height: 512
visible: true
// A mock-up of the time object that we will
// implement in Rust
QtObject {
id: time
property int hour
property int minute
property int second
}
// This timer will also become Rust code
Timer {
interval: 1000; running: true; repeat: true
onTriggered: {
var date = new Date()
time.hour = date.getHours()
time.minute = date.getMinutes()
time.second = date.getSeconds()
}
}
// the clock face
Image {
anchors.fill: parent
source: "rust-logo-blk.svg"
sourceSize: Qt.size(width, height) // ensure rendered SVG canvas matches used size
fillMode: Image.PreserveAspectFit
transform: Rotation {
origin.x: width / 2
origin.y: height / 2
angle: time.second * 6 // convert seconds to degrees
}
}
// the minute hand
Rectangle {
id: minute
x: (parent.width - width) / 2
y: 0
width: parent.width / 100
height: parent.height / 1.8
radius: width
color: "#3daefd"
transform: Rotation {
origin.x: hour.width / 2
origin.y: height / 2
// convert minutes to degrees
angle: time.minute * 6
}
}
// the hour hand
Rectangle {
id: hour
x: (parent.width - width) / 2
y: parent.height / 6
width: parent.width / 50
height: parent.height / 2.8
radius: width
color: "#3daefd"
transform: Rotation {
origin.x: hour.width / 2
origin.y: height / 3
// convert hours to degrees
angle: time.hour * 30 + time.minute / 2
}
}
}
```
You can run the plain QML file with the tool `qmlscene`.
```
$ qmlscene just_qml.qml
```
<figure>
<img src="time.png" alt="Time for Rust and QML"/>
<figcaption>Time for Rust and QML</figcaption>
</figure>
`qmlscene` can run any plain QML files. If you have QML plugins installed, these can be used too. You can make plugins that are implemented in Rust, but we'll not go into that now.
## Set up a QML project with Rust
Before we can replace the `QtObject`, we have to set up a project. Rust Qt Binding Generator comes with a template project for QML in the folder [`templates/qt_quick`](https://commits.kde.org/rust-qt-binding-generator?path=templates/qt_quick).
You can get set up like so. You will need to have Qt, Rust and CMake installed.
First build `rust_qt_binding_generator`.
```
$ git clone git://anongit.kde.org/rust-qt-binding-generator
$ mkdir build
$ cd rust-qt-binding-generator/build
$ cmake ..
$ make rust_qt_binding_generator
$ export PATH=$PATH:$PWD/src
```
Now build and run the template project.
```
$ mkdir ../templates/qt_quick/build
$ cd ../templates/qt_quick/build
$ cmake ..
$ make
$ ./MyExe
```
You will be greeted with a 'Hello World' application.
## Starting from a template
So what just happened? The template project is based on CMake. CMake is the build system that most KDE projects use. A template in CMake is an example of how to add Rust code to KDE programs. It is possible to use another build system.
CMake performs four steps as instructed by the `CMakeLists.txt` file. It
1) generates Rust and C++ from `bindings.json` by calling `rust_qt_binding_generator`,
2) compiles the Rust code in `rust/` into a static library by calling `cargo`,
3) compiles the C++ code,
4) links the C++ objects, the QML files, and the Rust library into an executable.
If you prefer to use only `cargo`, you'll have to tell it to perform steps 1, 3 and 4 in a `build.js` file.
## Adding some Rust
Now let's turn this clock into the [Antikythera mechanism](https://en.wikipedia.org/wiki/Antikythera_mechanism) by adding some Rust.
We want the Rust code to have a Time object that indicates the hour, the minute and the second. We write this interface into `bindings.json`.
```json
{
"cppFile": "src/Bindings.cpp",
"rust": {
"dir": "rust",
"interfaceModule": "interface",
"implementationModule": "implementation"
},
"objects": {
"Time": {
"type": "Object",
"properties": {
"hour": {
"type": "quint32"
},
"minute": {
"type": "quint32"
},
"second": {
"type": "quint32"
}
}
}
}
}
```
Now if we run `make` again, three files will be updated: `src/Bindings.h`, `src/Bindings.cpp`, and `rust/src/interface.rs`. And then we'll get a compile error from `cargo`.
That is because we have to adapt `rust/src/implementation.rs` to the new `interface.rs`. `interface.rs` specifies a trait that must be implemented in `implementation.rs`.
This is the generated trait:
```rust
// rust/src/interface.rs
pub trait TimeTrait {
fn new(emit: TimeEmitter) -> Self;
fn emit(&self) -> &TimeEmitter;
fn hour(&self) -> u32;
fn minute(&self) -> u32;
fn second(&self) -> u32;
}
```
Note that the trait has getters, but no setters. With `"write": true`, you can add setters on properties.
For now, we implement a fixed time in our new `implementation.rs`.
```rust
// rust/src/implementation.rs
use interface::*;
pub struct Time {
emit: TimeEmitter
}
impl TimeTrait for Time {
fn new(emit: TimeEmitter) -> Self {
Time {
emit
}
}
fn emit(&self) -> &TimeEmitter {
&self.emit
}
fn hour(&self) -> u32 {
1
}
fn minute(&self) -> u32 {
52
}
fn second(&self) -> u32 {
0
}
}
```
Now whenever the QML application wants to know the time, it can ask the Rust code. Well, almost. We have to change three more files and one of them is a C++ file. It is a very simple change and it is needed to tell the QML code about the Rust QObject. In `src/main.cpp`, change this line:
```c++
// src/main.cpp
qmlRegisterType<Simple>("RustCode", 1, 0, "Simple");
```
to this
```c++
// src/main.cpp
qmlRegisterType<Time>("RustCode", 1, 0, "Time");
```
Next we add the Rust logo to the program, by copying the file `rust-logo-blk.svg` into the toplevel dir of the template and noting it as resource in `qml.qrc`. That file lists files that should be compiled into the executable.
```xml
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>rust-logo-blk.svg</file>
</qresource>
</RCC>
```
Now create the file `main.qml`. The line `import RustCode 1.0` imports our Rust object into the application. Our mockup `QtObject` and the `Timer` have been replaced with `Time { id: time }`.
This `Time` still has the properties `hour`, `minute`, and `second`. Whenever these change, the user interface is updated.
```qml
// main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import RustCode 1.0
Window {
width: 512
height: 512
visible: true
// here is our Rust time
Time {
id: time
}
// the clock face
Image {
anchors.fill: parent
source: "rust-logo-blk.svg"
sourceSize: Qt.size(width, height) // ensure rendered SVG canvas matches used size
fillMode: Image.PreserveAspectFit
transform: Rotation {
origin.x: width / 2
origin.y: height / 2
angle: time.second * 6 // convert seconds to degrees
}
}
// the minute hand
Rectangle {
id: minute
x: (parent.width - width) / 2
y: 0
width: parent.width / 100
height: parent.height / 1.8
radius: width
color: "#3daefd"
transform: Rotation {
origin.x: hour.width / 2
origin.y: height / 2
// convert minutes to degrees
angle: time.minute * 6
}
}
// the hour hand
Rectangle {
id: hour
x: (parent.width - width) / 2
y: parent.height / 6
width: parent.width / 50
height: parent.height / 2.8
radius: width
color: "#3daefd"
transform: Rotation {
origin.x: hour.width / 2
origin.y: height / 3
// convert hours to degrees
angle: time.hour * 30 + time.minute / 2
}
}
}
```
## Start the time
<figure>
<img src="happy_time.png" alt="The time is now"/>
<figcaption>A happy clock</figcaption>
</figure>
Are you still here? That was quite a few instructions to follow for a simple example. The good news is that this setup does not get harder when you add more interfaces.
Anyway, now the part you've been waiting for. We will let Rust update the time and send it to the user interface. The crate `chrono` is used to get the time. Add it to `lib.rs` and `Cargo.toml`.
```toml
# rust/Cargo.toml
...
[dependencies]
chrono = "*"
...
```
```rust
// rust/src/lib.rs
...
extern crate chrono;
...
```
This code goes in `implementation.rs`. A thread wakes up every second and sends a signal to the user interface whenever a property changes.
```rust
// rust/src/implementation.rs
use interface::*;
use chrono::{Local, Timelike};
use std::thread;
use std::time::Duration;
pub struct Time {
emit: TimeEmitter,
}
fn emit_time(emit: TimeEmitter) {
thread::spawn(move || {
loop {
thread::sleep(Duration::from_secs(1));
emit.second_changed();
if Local::now().second() == 0 {
emit.minute_changed();
if Local::now().minute() == 0 {
emit.hour_changed();
}
}
}
});
}
impl TimeTrait for Time {
fn new(emit: TimeEmitter) -> Self {
emit_time(emit.clone());
Time {
emit
}
}
fn emit(&self) -> &TimeEmitter {
&self.emit
}
fn hour(&self) -> u32 {
Local::now().hour()
}
fn minute(&self) -> u32 {
Local::now().minute()
}
fn second(&self) -> u32 {
Local::now().second()
}
}
```
## Closing remarks
This was a pretty long tutorial with quite a few different parts. That was the point of the tutorial: to learn the parts that make up a binding between Qt and Rust.