Initial work for writing a nice initial documentation and improving the examples

This commit is contained in:
Filippo Cucchetto 2015-01-03 15:46:39 +01:00
parent fbda8a0618
commit c6960df455
9 changed files with 186 additions and 33 deletions

87
Nim/Docs/NimQml.txt Normal file
View File

@ -0,0 +1,87 @@
:Authors:
Filippo Cucchetto <filippocucchetto@gmail.com>
:Version: 0.1.0
:Date: 2015/01/02
Introduction
-----------
The NimQml module add 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.
The NimQml is made by two components:
* 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
Building
--------
At the time of writing the DOtherSide C++ library must be compiled
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
If everything goes correctly you'll have build both
the DOtherSide C++ library and the Nim examples
Installation
----------
The installation is not mandatory, in fact you could try
the built Nim example in the following way
::
cd path/to/build/dir
cd Nim/Examples/HelloWorld
export LD_LIBRARY_PATH=path/to/libDOtherSide.so
./HelloWorld
Given this, you can procede with the installation of the C++ library
in the following way
::
cd to/build/dir
make install
or by manually copying the library in your system lib directory
::
sudo cp build/dir/path/DOtherSide/libDOtherSide.so /usr/lib
First example: HelloWorld
----------
As usual lets start with theHelloWorld 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 contains the presentation layer and expose
the data in your nim files.
Here's the ``main.nim`` file
.. code-block:: nim
:file: ../Examples/HelloWorld/main.nim
and here the ``main.qml`` file
.. code-block:: qml
:file: ../Examples/HelloWorld/main.qml
The following examples show the basic 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
Second example: exposing data to Qml
------------------------------------
Third example: databinding
--------------------------

View File

@ -1 +1,2 @@
add_subdirectory(HelloWorld)
add_subdirectory(Simple)

View File

@ -0,0 +1,2 @@
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
add_nim_executable(TARGET HelloWorld SOURCES main.nim PATHS ../../NimQml)

View File

@ -0,0 +1,19 @@
import NimQml
import macros
import typeinfo
proc mainProc() =
var app: QApplication
app.create()
defer: app.delete()
var engine: QQmlApplicationEngine
engine.create()
defer: engine.delete()
engine.load("main.qml")
app.exec()
when isMainModule:
mainProc()

View File

@ -0,0 +1,12 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
ApplicationWindow
{
width: 400
height: 300
title: "Hello World"
Component.onCompleted: visible = true
}

View File

@ -1,2 +1,2 @@
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
add_nim_executable(TARGET Main SOURCES main.nim PATHS ../../NimQml)
add_nim_executable(TARGET Simple SOURCES main.nim PATHS ../../NimQml)

View File

@ -1,13 +1,19 @@
import NimQmlTypes
import tables
## NimQml aims to provide binding to the QML for the Nim programming language
export QObject
export QApplication
export QVariant
export QQmlApplicationEngine
export QQmlContext
type QMetaType* {.pure.} = enum
type QMetaType* {.pure.} = enum ## \
## Qt metatypes values used for specifing the
## signals and slots argument and return types.
##
## This enum mimic the QMetaType::Type C++ enum
UnknownType = cint(0),
Bool = cint(1),
Int = cint(2),
@ -50,53 +56,65 @@ proc dos_qvariant_setString(variant: pointer, value: cstring) {.cdecl, dynlib:"l
proc dos_chararray_delete(rawCString: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc create*(variant: var QVariant) =
## Create a new QVariant
var data: pointer
dos_qvariant_create(data)
variant = QVariant(data)
proc create*(variant: var QVariant, value: cint) =
## Create a new QVariant given a cint value
var data: pointer
dos_qvariant_create_int(data, value)
variant = QVariant(data)
proc create*(variant: var QVariant, value: bool) =
## Create a new QVariant given a bool value
var data: pointer
dos_qvariant_create_bool(data, value)
variant = QVariant(data)
proc create*(variant: var QVariant, value: string) =
## Create a new QVariant given a string value
var data: pointer
dos_qvariant_create_string(data, value)
variant = QVariant(data)
proc create*(variant: var QVariant, value: QObject) =
## Create a new QVariant given a QObject
var data: pointer
dos_qvariant_create_qobject(data, value.data)
variant = QVariant(data)
proc delete*(variant: QVariant) =
## Delete a QVariant
debugMsg("QVariant", "delete")
dos_qvariant_delete(pointer(variant))
proc isNull*(variant: QVariant): bool =
## Return true if the QVariant value is null, false otherwise
dos_qvariant_isnull(pointer(variant), result)
proc intVal*(variant: QVariant): int =
## Return the QVariant value as int
var rawValue: cint
dos_qvariant_toInt(pointer(variant), rawValue)
result = cast[int](rawValue)
proc `intVal=`*(variant: QVariant, value: int) =
## Sets the QVariant value int value
var rawValue = cast[cint](value)
dos_qvariant_setInt(pointer(variant), rawValue)
proc boolVal*(variant: QVariant): bool =
## Return the QVariant value as bool
dos_qvariant_toBool(pointer(variant), result)
proc `boolVal=`*(variant: QVariant, value: bool) =
## Sets the QVariant bool value
dos_qvariant_setBool(pointer(variant), value)
proc stringVal*(variant: QVariant): string =
## Return the QVariant value as string
var rawCString: cstring
var rawCStringLength: cint
dos_qvariant_toString(pointer(variant), rawCString, rawCStringLength)
@ -104,6 +122,7 @@ proc stringVal*(variant: QVariant): string =
dos_chararray_delete(rawCString)
proc `stringVal=`*(variant: QVariant, value: string) =
## Sets the QVariant string value
dos_qvariant_setString(pointer(variant), value)
@ -114,19 +133,23 @@ proc dos_qqmlapplicationengine_context(engine: pointer, context: var pointer) {.
proc dos_qqmlapplicationengine_delete(engine: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc create*(engine: var QQmlApplicationEngine) =
## Create an new QQmlApplicationEngine
var temp: pointer
dos_qqmlapplicationengine_create(temp)
engine = QQmlApplicationEngine(temp)
proc load*(engine: QQmlApplicationEngine, filename: cstring) =
## Load the given Qml file
dos_qqmlapplicationengine_load(pointer(engine), filename)
proc rootContext*(engine: QQmlApplicationEngine): QQmlContext =
## Return the engine root context
var context: pointer
dos_qqmlapplicationengine_context(pointer(engine), context)
result = cast[QQmlContext](context)
proc delete*(engine: QQmlApplicationEngine) =
## Delete the given QQmlApplicationEngine
debugMsg("QQmlApplicationEngine", "delete")
dos_qqmlapplicationengine_delete(pointer(engine))
@ -134,6 +157,7 @@ proc delete*(engine: QQmlApplicationEngine) =
proc dos_qqmlcontext_setcontextproperty(context: pointer, propertyName: cstring, propertyValue: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc setContextProperty*(context: QQmlContext, propertyName: string, propertyValue: QVariant) =
## Sets a new property with the given value
dos_qqmlcontext_setcontextproperty(pointer(context), propertyName, pointer(propertyValue))
# QApplication
@ -142,21 +166,31 @@ proc dos_qguiapplication_exec() {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc dos_qguiapplication_delete() {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc create*(application: QApplication) =
debugMsg("QApplication", "create")
## Create a new QApplication
dos_qguiapplication_create()
proc exec*(application: QApplication) =
debugMsg("QApplication", "exec")
## Start the Qt event loop
dos_qguiapplication_exec()
proc delete*(application: QApplication) =
debugMsg("QApplication", "delete")
## Delete the given QApplication
dos_qguiapplication_delete()
# QObject
type QVariantArray {.unchecked.} = array[0..0, QVariant]
type QVariantArrayPtr = ptr QVariantArray
proc toVariantSeq(args: QVariantArrayPtr, numArgs: cint): seq[QVariant] =
result = @[]
for i in 0..numArgs-1:
result.add(args[i])
proc toCIntSeq(metaTypes: openarray[QMetaType]): seq[cint] =
result = @[]
for metaType in metaTypes:
result.add(cint(metaType))
proc dos_qobject_create(qobject: var pointer, nimobject: pointer, qobjectCallback: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc dos_qobject_delete(qobject: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc dos_qobject_slot_create(qobject: pointer, slotName: cstring, argumentsCount: cint, argumentsMetaTypes: ptr cint, slotIndex: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.}
@ -164,40 +198,31 @@ proc dos_qobject_signal_create(qobject: pointer, signalName: cstring, argumentsC
proc dos_qobject_signal_emit(qobject: pointer, signalName: cstring, argumentsCount: cint, arguments: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc dos_qobject_property_create(qobject: pointer, propertyName: cstring, propertyType: cint, readSlot: cstring, writeSlot: cstring, notifySignal: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
method onSlotCalled*(nimobject: QObject, slotName: string, args: openarray[QVariant]) =
debugMsg("QObject", "onSlotCalled", "begin")
debugMsg("QObject", "onSlotCalled", "end")
method onSlotCalled*(nimobject: QObject, slotName: string, args: openarray[QVariant]) =
## Called from the NimQml bridge when a slot is called from Qml.
## Subclasses can override the given method for handling the slot call
discard()
proc toVariantSeq(args: QVariantArrayPtr, numArgs: cint): seq[QVariant] =
result = @[]
for i in 0..numArgs-1:
result.add(args[i])
proc qobjectCallback(nimobject: pointer, slotName: QVariant, numArguments: cint, arguments: QVariantArrayPtr) {.exportc.} =
debugMsg("QObject", "qobjectCallback", "begin")
proc qobjectCallback(nimobject: pointer, slotName: QVariant, numArguments: cint, arguments: QVariantArrayPtr) {.exportc.} =
var nimQObjectCasted = cast[ptr QObject](nimobject)
# forward to the QObject subtype instance
nimQObjectCasted[].onSlotCalled(slotName.stringVal, arguments.toVariantSeq(numArguments))
debugMsg("QObject", "qobjectCallback", "end")
proc toCIntSeq(metaTypes: openarray[QMetaType]): seq[cint] =
result = @[]
for metaType in metaTypes:
result.add(cint(metaType))
proc create*(qobject: var QObject) =
## Create a new QObject
qobject.name = "QObject"
qobject.slots = initTable[string,cint]()
qobject.signals = initTable[string, cint]()
dos_qobject_create(qobject.data, addr(qobject), qobjectCallback)
proc delete*(qobject: QObject) =
debugMsg("QObject", "delete")
## Delete the given QObject
dos_qobject_delete(qobject.data)
proc registerSlot*(qobject: var QObject,
slotName: string,
metaTypes: openarray[QMetaType]) =
## Register a slot in the QObject with the given name and signature
# Copy the metatypes array
var copy = toCIntSeq(metatypes)
var index: cint
@ -207,6 +232,7 @@ proc registerSlot*(qobject: var QObject,
proc registerSignal*(qobject: var QObject,
signalName: string,
metatypes: openarray[QMetaType]) =
## Register a signal in the QObject with the given name and signature
var index: cint
if metatypes.len > 0:
var copy = toCIntSeq(metatypes)
@ -220,10 +246,12 @@ proc registerProperty*(qobject: var QObject,
propertyType: QMetaType,
readSlot: string,
writeSlot: string,
notifySignal: string) =
notifySignal: string) =
## Register a property in the QObject with the given name and type.
dos_qobject_property_create(qobject.data, propertyName, cast[cint](propertyType), readSlot, writeSlot, notifySignal)
proc emit*(qobject: QObject, signalName: string, args: openarray[QVariant] = []) =
## Emit the signal with the given name and values
if args.len > 0:
var copy: seq[QVariant]
for i in 0..args.len-1:
@ -239,23 +267,27 @@ proc dos_qquickview_show(view: pointer) {.cdecl, dynlib:"libDOtherSide.so", impo
proc dos_qquickview_source(view: pointer, filename: var cstring, length: var int) {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc dos_qquickview_set_source(view: pointer, filename: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.}
proc create(view: var QQuickView) =
proc create(view: var QQuickView) =
## Create a new QQuickView
var temp: pointer
dos_qquickview_create(temp)
view = QQuickView(temp)
proc source(view: QQuickView): cstring =
## Return the source Qml file loaded by the view
var length: int
dos_qquickview_source(pointer(view), result, length)
proc `source=`(view: QQuickView, filename: cstring) =
## Sets the source Qml file laoded by the view
dos_qquickview_set_source(pointer(view), filename)
proc show(view: QQuickView) =
## Sets the view visible
dos_qquickview_show(pointer(view))
proc delete(view: QQuickView) =
debugMsg("QQuickView", "delete")
proc delete(view: QQuickView) =
## Delete the given QQuickView
dos_qquickview_delete(pointer(view))

View File

@ -1,14 +1,14 @@
import tables
type
QVariant* = distinct pointer
QQmlApplicationEngine* = distinct pointer
QQmlContext* = distinct pointer
QApplication* = distinct pointer
QObject* {.inheritable.} = ref object of RootObj
QVariant* = distinct pointer ## A QVariant
QQmlApplicationEngine* = distinct pointer ## A QQmlApplicationEngine
QQmlContext* = distinct pointer ## A QQmlContext
QApplication* = distinct pointer ## A QApplication
QObject* {.inheritable.} = ref object of RootObj ## A QObject
name*: string
data*: pointer
slots*: Table[string, cint]
signals*: Table[string, cint]
properties*: Table[string, cint]
QQuickView* = distinct pointer
QQuickView* = distinct pointer ## A QQuickView

View File

@ -23,7 +23,7 @@ function(add_nim_executable )
# add target to trigger the nimrod compiler
add_custom_target(
nim ALL
${ARGS_TARGET} ALL
COMMAND
${NIM_EXECUTABLE} "c" ${in_paths} "--nimcache:" ${DIRECTORY} "--out:" ${nim_target} ${in_files}
DEPENDS