2015-09-14 23:38:02 +02:00
:Authors:
Filippo Cucchetto < filippocucchetto @gmail .com >
Will Szumski < will @cowboycoders .org >
2019-10-01 23:56:29 +02:00
:Version: 0.7.7
:Date: 2019/10/01
2015-09-14 23:38:02 +02:00
Introduction
-----------
The NimQml module adds Qt Qml bindings to the Nim programming language
allowing you to create new modern UI by mixing the Qml declarative syntax
and the Nim imperative language.
2019-10-01 23:56:29 +02:00
You will need:
2015-09-14 23:38:02 +02:00
* The DOtherSide C++ shared library
* The NimQml Nim module
This first component implements the glue code necessary for
communicating with the Qt C++ library, the latter module wraps
the libDOtherSide exported symbols in Nim
2019-10-01 23:56:29 +02:00
Building the C++ DOtherSide bindings
2015-09-14 23:38:02 +02:00
--------
At the time of writing the DOtherSide C++ library must be compiled
and installed manually from source.
First clone the DOtherSide git repo
::
git clone https://github.com/filcuc/DOtherSide
than you can proceed with the common CMake build steps
::
mkdir build
cd build
cmake ..
make
2019-10-01 23:56:29 +02:00
make install
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
Installation of NimQml module
2015-09-14 23:38:02 +02:00
----------
The installation is not mandatory, in fact you could try
2019-10-01 23:56:29 +02:00
the built-in examples in the following way
2015-09-14 23:38:02 +02:00
::
2019-10-01 23:56:29 +02:00
cd path/to/repo/nimqml
cd examples/helloworld
2015-09-14 23:38:02 +02:00
export LD_LIBRARY_PATH=path/to/libDOtherSide.so
2019-10-01 23:56:29 +02:00
nim c -r main
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
Alternatively you can use the ``nimble` ` package manager
2015-09-14 23:38:02 +02:00
::
nimble install NimQml
or
::
cd to/build/dir/Nim/NimQml
nimble install
Example 1: HelloWorld
----------
As usual lets start with an HelloWorld example.
Most of the NimQml projects are made by one or more nim and qml
files. Usually the .nim files contains your app logic and data
layer. The qml files contain the presentation layer and expose
the data in your nim files.
2019-10-01 23:56:29 +02:00
``examples/helloworld/main.nim` `
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
:file: ../examples/helloworld/main.nim
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
``examples/helloworld/main.qml` `
2015-09-14 23:38:02 +02:00
.. code-block:: qml
2019-10-01 23:56:29 +02:00
:file: ../examples/helloworld/main.qml
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
The example shows the mandatory steps of each NimQml app
1. Create the ``QApplication` ` for initializing the Qt runtime
2. Create the `QQmlApplicationEngine` and load your main .qml file
3. Call the `exec` proc of the QApplication instance for starting the Qt event loop
2015-09-14 23:38:02 +02:00
Example 2: exposing data to Qml
------------------------------------
2019-10-01 23:56:29 +02:00
The previous example shown how to startup the Qt event loop
to create an application with a window.
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
It's time to explore how to pass data to Qml but lets see the
2015-09-14 23:38:02 +02:00
example code first:
2019-10-01 23:56:29 +02:00
``examples/simpledata/main.nim` `
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
:file: ../examples/simpledata/main.nim
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
``examples/simpledata/main.qml` `
2015-09-14 23:38:02 +02:00
.. code-block:: qml
2019-10-01 23:56:29 +02:00
:file: ../examples/simpledata/main.qml
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
The example shows how to expose simple values to Qml:
1. Create a `QVariant` and set its value.
2. Set a property in the Qml root context with a given name.
2015-09-14 23:38:02 +02:00
Once a property is set through the ``setContextProperty` ` proc, it's available
globally in all the Qml script loaded by the current engine (see the official Qt
documentation for more details about the engine and context objects)
At the time of writing the QVariant class support the following types:
* int
* string
* bool
* float
* QObject derived classes
Example 3: exposing complex data and procedures to Qml
----------------------------------------------------------
As seen by the second example, simple data is fine. However most
applications need to expose complex data, functions and
update the view when something changes in the data layer.
This is achieved by creating an object that derives from QObject.
A QObject is made of :
2019-10-01 23:56:29 +02:00
1. ``slots` `: functions that could be called from the qml engine and/or connected to Qt signals
2. ``signals` `: functions for sending events and to which slots connect
3. ``properties` `: properties allow the passing of data to the Qml view and make it aware of changes in the data layer
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
A QObject `property` is made of three things:
2015-09-14 23:38:02 +02:00
* a read slot: a method that returns the current value of the property
* a write slot: a method that sets the value of the property
* a notify signal: emitted when the current value of the property is changed
We'll start by looking at the main.nim file
2019-10-01 23:56:29 +02:00
``examples/slotsandproperties/main.nim` `
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
:file: ../examples/slotsandproperties/main.nim
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
We can see:
1. The creation of a Contact object
2. The injection of the Contact object to the Qml root context using the ``setContextProperty` ` as seen in the previous example
2015-09-14 23:38:02 +02:00
The Qml file is as follows:
2019-10-01 23:56:29 +02:00
``examples/slotsandproperties/main.qml` `
2015-09-14 23:38:02 +02:00
.. code-block:: qml
2019-10-01 23:56:29 +02:00
:file: ../examples/slotsandproperties/main.qml
2015-09-14 23:38:02 +02:00
The qml is made up of: a Label, a TextInput widget, and a button.
The label displays the contact name - this automatically updates when
the contact name changes.
When clicked, the button updates the contact name with the text from
the TextInput widget.
So where's the magic?
The magic is in the Contact.nim file
2019-10-01 23:56:29 +02:00
``examples/slotsandproperties/contact.nim` `
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
:file: ../examples/slotsandproperties/contact.nim
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
A Contact is a subtype derived from `QObject`
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
Defining a `QObject` is done using the nim `QtObject` macro
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
QtObject:
type Contact* = ref object of QObject
m_name: string
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
Inside the `QtObject` just define your subclass as your would normally do in Nim.
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
Since Nim doesn't support automatic invocation of base class constructors and destructors
you need to call manually the base class `setup` and `delete` functions.
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
.. code-block:: nim
proc delete*(self: Contact) =
self.QObject.delete
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
proc setup(self: Contact) =
self.QObject.setup
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
Don't forget to call the `setup` function and `delete` in your exported constructor
procedure
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
proc newContact*(): Contact =
new(result, delete)
result.m_name = "InitialName"
result.setup
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
The creation of a property is done in the following way:
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
QtProperty[string] name:
read = getName
write = setName
notify = nameChanged
A `QtProperty` is defined by a:
1. type, in this case `string`
2. name, in this case `name`
3. read slot, in this case `getName`
4. write slot, in this case `setName`
5. notify signal, in this case `nameChanged`
Looking at the ``getName` , `setName``, ` nameChanged` procs, show that slots and signals
are nothing more than standard procedures annotated with `{.slot.}` and `{.signal.}`
2015-09-14 23:38:02 +02:00
2019-10-01 23:56:29 +02:00
Example 4: ContactApp
2015-09-14 23:38:02 +02:00
-------------------------
The last example tries to show you all the stuff presented
in the previous chapters and gives you an introduction to how
to expose lists to qml.
2019-10-01 23:56:29 +02:00
Qt models are a huge topic and explaining in detail how they work is
2015-09-14 23:38:02 +02:00
out of scope. For further information please read the official
Qt documentation.
The main file follows the basic logic of creating a qml
engine and exposing a QObject derived object "ApplicationLogic"
through a global "logic" property
2019-10-01 23:56:29 +02:00
``examples/contactapp/main.nim` `
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
:file: ../examples/contactapp/main.nim
2015-09-14 23:38:02 +02:00
The qml file shows a simple app with a central tableview
2019-10-01 23:56:29 +02:00
``examples/contactapp/main.qml` `
2015-09-14 23:38:02 +02:00
.. code-block:: qml
2019-10-01 23:56:29 +02:00
:file: ../examples/contactapp/main.qml
2015-09-14 23:38:02 +02:00
The important things to notice are:
1. The menubar load, save and exit items handlers call the logic load, save and exit slots
2. The TableView model is retrieved by the logic.contactList property
3. The delete and add buttons call the del and add slots of the logic.contactList model
The ApplicationLogic object is as follows:
2019-10-01 23:56:29 +02:00
``examples/contactapp/applicationlogic.nim` `
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
:file: ../examples/contactapp/applicationlogic.nim
2015-09-14 23:38:02 +02:00
The ApplicationLogic object,
1. expose some slots for handling the qml menubar triggered signals
2. expose a contactList property that return a QAbstractListModel derived object that manage the list of contacts
The ContactList object is as follows:
2019-10-01 23:56:29 +02:00
``examples/contactapp/contactlist.nim` `
2015-09-14 23:38:02 +02:00
.. code-block:: nim
2019-10-01 23:56:29 +02:00
:file: ../examples/contactapp/contactlist.nim
2015-09-14 23:38:02 +02:00
The ContactList object:
1. overrides the ``rowCount` ` method for returning the number of rows stored in the model
2. overrides the ``data` ` method for returning the value for the exported roles
3. overrides the ``roleNames` ` method for returning the names of the roles of the model. This name are then available in the qml item delegates
4. defines two slots ``add`` and ``del`` that add or delete a Contact. During this operations the model execute the ``beginInsertRows`` and ``beginRemoveRows`` for notifing the view of an upcoming change. Once the add or delete operations are done the model execute the ``endInsertRows`` and ``endRemoveRows` `.