2) I need your help The current syntax for defining Signals/Slots/Properties is awkward and i'm already done work on custom macro that could increase the syntax in Nim. However hints and suggestions are wellcome.
Here's the basic example i put on github
import NimQml
import macros
import typeinfo
type MyQObject = ref object of QObject
m_name: string
method getName(myQObject: MyQObject): string =
result = myQObject.m_name
method setName(myQObject: MyQObject, name: string) =
if myQObject.m_name != name:
myQObject.m_name = name
myQObject.emit("nameChanged")
method onSlotCalled(myQObject: MyQObject, slotName: string, args: openarray[QVariant]) =
case slotName:
of "getName":
args[0].stringVal = myQObject.getName()
of "setName":
myQObject.setName(args[1].stringVal)
else:
discard()
proc mainProc() =
var app: QApplication
app.create()
finally: app.delete()
var myQObject = MyQObject()
myQObject.create()
myQObject.m_name = "InitialName"
finally: myQObject.delete()
myQObject.registerSlot("getName", [QMetaType.QString])
myQObject.registerSlot("setName", [QMetaType.Void, QMetaType.QString])
myQObject.registerSignal("nameChanged", [QMetaType.Void])
myQObject.registerProperty("name", QMetaType.QString, "getName", "setName", "nameChanged")
var engine: QQmlApplicationEngine
engine.create()
finally: engine.delete()
var variant: QVariant
variant.create(myQObject)
finally: variant.delete()
var rootContext: QQmlContext = engine.rootContext()
rootContext.setContextProperty("myQObject", variant)
engine.load("main.qml")
app.exec()
when isMainModule:
mainProc()
and corrisponding QML
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
ApplicationWindow
{
width: 400
height: 300
Component.onCompleted: visible = true
function temp()
{
console.log("temp called")
}
ColumnLayout
{
anchors.fill: parent
Label
{
text: "Current name is:" + myQObject.name
}
TextField
{
id: textField
}
Button
{
text: "Change Name"
onClicked: {
console.log("QML:", textField.text)
myQObject.name = textField.text
}
}
}
}
The repo is here: https://github.com/filcuc/DOtherSide
Given this i wish you a happy new year :)
Hi all, the next step for my project is to remove most (if not all) the tedius register Slot, Signal, Property stuff.
First let's talk what i would like to remove:
myQObject.registerSlot("getName", [QMetaType.QString])
myQObject.registerSlot("setName", [QMetaType.Void, QMetaType.QString])
myQObject.registerSignal("nameChanged", [QMetaType.Void])
myQObject.registerProperty("name", QMetaType.QString, "getName", "setName", "nameChanged")
method onSlotCalled(myQObject: MyQObject, slotName: string, args: openarray[QVariant]) =
case slotName:
of "getName":
args[0].stringVal = myQObject.getName()
of "setName":
myQObject.setName(args[1].stringVal)
else:
discard()
The idea should be something like this:
1. let the user annotate the methods that are slots and those that are signals. So:
method getName(myQObject: MyQObject): string {.slot.} =
result = myQObject.m_name
method setName(myQObject: MyQObject, name: string) {.slot.} =
if myQObject.m_name != name:
myQObject.m_name = name
myQObject.nameChanged()
method nameChanged(myQObject: MyQObject) {.signal.} = discard()
5. In the create method delegate to the QObject base class implementation to make the register calls. So:
#...inside the main proc
myQObject.create() # this calls the QObject base implementation
#...
method create(qobject: QObject):
# Pseudo code
for slot in globalSlots["myQObject"]: # we have to find the class name in some way...
registerSlot(...)
for signals in globalSignals["myQObject"]:
registerSignal(...)
for property in globalProperties["myQObject"]:
registerProperty(...)
so a syntax could be:
property("name"):
method getName(myQObject: MyQObject): string {.read.} =
result = myQObject.m_name
method setName(myQObject: MyQObject, name: string) {.write.} =
if myQObject.m_name != name:
myQObject.m_name = name
myQObject.nameChanged()
method nameChanged(myQObject: MyQObject) {.notify.} = discard()
All in all to summarize, here's the complete example i posted with this fictional syntax:
import NimQml
import macros
import typeinfo
type MyQObject = ref object of QObject
m_name: string
property("name"):
method getName(myQObject: MyQObject): string {.read.} =
result = myQObject.m_name
method setName(myQObject: MyQObject, name: string) {.write.} =
if myQObject.m_name != name:
myQObject.m_name = name
myQObject.emit("nameChanged")
method nameChanged(myQObjet: MyQObject) {.notify.} =
proc mainProc() =
var app: QApplication
app.create()
finally: app.delete()
var myQObject = MyQObject()
myQObject.create()
myQObject.m_name = "InitialName"
finally: myQObject.delete()
var engine: QQmlApplicationEngine
engine.create()
finally: engine.delete()
var variant: QVariant
variant.create(myQObject)
finally: variant.delete()
var rootContext: QQmlContext = engine.rootContext()
rootContext.setContextProperty("myQObject", variant)
engine.load("main.qml")
app.exec()
when isMainModule:
mainProc()
method name(myQObjet: MyQObject) {.notify.} =
setContextProperty(QQmlContext context, QVariant value)
myQObject.registerSlot("getName", [QMetaType.QString])
myQObject.registerSlot("setName", [QMetaType.Void, QMetaType.QString])
myQObject.registerSignal("nameChanged", [QMetaType.Void])
myQObject.registerProperty("name", QMetaType.QString, "getName", "setName", "nameChanged")
and then before loading the QML file
rootContext.setContextProperty("myQObject", variant)
At this point when the QML files is loaded you can use the myQObject identifier for calling slots, connecting to its signals or reading/writing its properties. i.e:
// QML file...
Label
{
text: "Current name is:" + myQObject.name
}
//...
Here the Label text value is AUTOMATICALLY updated by the Qt engine each time the nameChanged signal is emitted. So we basically the label text property is databound to the myQObject name property.
Talking about the implementation:
The problem with Qt is that for generiting properties, signals and slots it use a preprocessor called MOC. MOC is called by the build system of qt (qmake) before invoking the c++ compiler for generiting the .moc files (that contains the implementation of the signals and slots).
Given that we are in Nim and not C++ i cannot invoke moc on my Nim files so my solutions has been the creation of dynamic QObject: so a QObject that doesn't need to be processed by MOC and to which we can add slots/signals and properties dynamically (in the source files is called DynamicQObject). In the Nim side when we create a QObject in reality a DynamicQObject is created on the C++ side and after that the slots, signals and properties are registered throught the register*** functions.
Hope to have been clear
@seyko2 I tried to beautify your code because here it's didplayed badly(maybe you're not using the Syntax Cheatsheet. From what i understood you meant:
The following looks better:
type MyQObject = ref object of QObject
m_name: string
method name(myQObject: MyQObject): string {.read.} =
result = myQObject.m_name
method name(myQObject: MyQObject, name: string) {.write.} =
if myQObject.m_name != name:
myQObject.m_name = name
myQObject.emit("nameChanged")
method name(myQObjet: MyQObject) {.notify.} = discard()
This could be a nice syntax for defining a property. So basically you're proposing to use the method name as the property name and the macro read/write/notify. This is fine however keep in mind that those methods are not called only by QML but also from Nim Code. So it must be valid to call the notify signal of a property from Nim i.e:
myQObject.nameChanged()
Infact i made a mistake in the setName proc i posted. The {.signal.} macro and {.notify.} macro in reality must generated the following code
method nameChanged(myQObjet: MyQObject) {.signal.} = discard()
become (after macro invokation)
# Registration of signal in the global scope
globalSignalTable["MyQObject"] = #information of this signal
method nameChanged(myQQObject: MyQObject) {.signal.} =
myQObject.emit("nameChanged") # emit the signal
So the set method should be like this:
method name(myQObject: MyQObject, name: string) {.write.} =
if myQObject.m_name != name:
myQObject.m_name = name
myQObject.nameChanged()
So it could be ok to use the same proc name for both read,write, signal for getting the association but the proc must be renamed
I had a go at creating a macro, see:
https://gist.github.com/cowboy-coders/f99380b6e975b2b60f27
I have some problems with the create method (see gist), but it ends up looking like:
QtType:
type MyQObject = ref object of QObject
m_name: string
method getName(myQObject: MyQObject): string {.slot.} =
result = myQObject.m_name
method nameChanged(myQObject: MyQObject) {.signal.}
method setName(myQObject: MyQObject, name: string) {.slot.} =
if myQObject.m_name != name:
myQObject.m_name = name
myQObject.nameChanged()
type name = object of QtProperty[string]
read = getName
write = setName
notify = nameChanged
proc mainProc() =
var app: QApplication
app.create()
defer: app.delete()
var myQObject = MyQObject()
myQObject.create()
defer: myQObject.delete()
myQObject.m_name = "InitialName"
var engine: QQmlApplicationEngine
engine.create()
defer: engine.delete()
var variant: QVariant
variant.create(myQObject)
defer: variant.delete()
var rootContext: QQmlContext = engine.rootContext()
rootContext.setContextProperty("myQObject", variant)
engine.load("main.qml")
app.exec()
when isMainModule:
mainProc()
@will: First let me thank you! wow i'm impressed! :) i cannot lie to you: i need some time for understand all that macro and template stuff (i'm not at your level :( )
Your solution has a couple of advantages:
type MyQObject = ref object of MyOtherQObject
m_name: string
In this way the create method of MyQObject can call the MyOtherQObject create method, so we have inheritance of properties, slots and signals
method create(MyQObject myObject) =
create(MyOtherObject(myQObject)) # call MyOtherQObject create method
QtObject:
type MyQObject = ref object of QObject
m_name: string
method getName(myQObject: MyQObject): string {.slot.} =
result = myQObject.m_name
QtProperty name of string:
read = getName
write = setName
notify = nameChanged
but i'm not sure..re thinking about it i prefear your syntax (lets see what other says about it)
Really nice work!! I think you can continue cleaning it up. i'll integrate it as soon you feel it complete. Let's see if other people have some suggestions.
I'm trying to reproduce that particular bug with a more simple example (so far no luck!). Your syntax for properties certainly seems doable. I'll wait to see what others say, and then knock up a version with that syntax. I thought QtObject was a bit too similar to QObject, but I'm happy to change it.
One thing to mention is I'm not sure if we can get the super type in the same manner with generics. For example, in this case:
type A[T] = ref object of QObject
type B[T] = ref object of A[T]
do we need an inbuilt superType or is this doable with macros? We could just have the user manually write a superType template I guess.
Hi all, i'm happy to announce the release 0.2.0 of the NimQml module
[ChangeLog]
You can install the new version through Nimble or compile from git .
The module documentation is here http://filcuc.github.io/DOtherSide/ The API documentation can be generated through "nim doc2" command
Hi all, i'm happy to announce the release 0.3.0 of the NimQml module
ChangeLog
You can install the new version through Nimble or compile from git .
The module documentation is here http://filcuc.github.io/DOtherSide/ The API documentation can be generated through "nim doc2" command