Hi,
it took me some time, but I have generated bindings for the Qt framework. The main goal with these bindings is supporting the creation of GUIs, not exposing the complete, massive Qt framework. Currently Qt 6.4.1 is targeted, but I guess Qt 6.*.* (and Qt5 and Qt4, if some compiler flags are changed) should also work, as the API is stable enough.
In addition to the bindings, I also created some macros to make using it in nim as painless as possible :) One macro (inheritQObject) allows for easy creation of new objects that inherit from a QObject (or descendant), together with overriding methods and defining new slots for the object. Another macro allows for easily generating layouts, using a QGridLayout, QVBoxLayout, QHBoxLayout and QSplitter, and conveniently setting properties and connecting signals with slots.
nimble install https://github.com/jerous86/nimqt
I will add it later to the nimble.directory, but for now I just want some feedback from here :)
import nimqt
import nimqt/[qpushbutton, qboxlayout]
nimqt.init
let app = newQApplication()
inheritQObject(GuiHandler, QObject):
slot_decl on_helloWorld_clicked()
let guiHandler: ptr GuiHandler = newGuiHandler()
let win: ptr QWidget = newQWidget()
win.makeLayout:
- newQPushButton(Q "Click me!!"):
connect(SIGNAL "clicked()", guiHandler, SLOT "on_helloWorld_clicked()")
- newQPushButton( Q "Click me!!"):
connect(SIGNAL "clicked()", guiHandler, SLOT "on_helloWorld_clicked()")
proc on_helloWorld_clicked(this: ptr GuiHandler) =
let sender = cast[ ptr QPushButton]( this.get_sender())
sender.setText( Q "Hello world!")
win.show()
discard app.exec()
will generate something like
when run with nim cpp -r examples/hello.nim.
The README.md on https://github.com/jerous86/nimqt contains information on how to use the library. In the examples directory in the repository there are a couple of examples.
For the availability of modules, just look in the qt/6.4.1_minimal/nimqt directory in the repository.
For documentation on the Qt modules, see the Qt documentation at https://doc.qt.io/qt-6/.
Note that not all methods are supported due to some issues with recursive module imports, but in general, I think the most useful methods are available. The modules are mainly those that support creating GUIs (i.e. QtGui and QtWidgets components), and skipping many of the QAbstract* classes.
There are scripts, using libclang, to generate bindings for other classes (i.e. QRegularExpression etc), but these are not added as I don't think they add much value. The set of classes for which there are bindings might change over time.
Aww
Wonderful. I assume you consider nimqml worthy to stay on its own and plan to not create any bindings to qt-quick / QML.
So we have a qt-widgets and and a qt-quick binding now, effectively.
Thanks!
Very clever to use XML for the header file translation.
I am gonna try it (right after newyearsday). This is good work! I am gonna work onto the ".ui" files and push them to your github.
Thank you 🎉🎉🎉
Can't wait to test this, great work 👍👍👍
Good luck with implementing .ui support! Don't know how straightforward it will be :)
My findings: I think the library works pretty smooth. Speed should probably be on par with using just c++ (unless maybe you do a lot of conversions to and from Qstring?). I have not looked at the memory consequences. It's been a very long time since I've used qt and python, so cannot really compare, but I think the provided macros are pretty convenient :)
One serious concern is that the cpp backend is required. Hence, if you use a library that does not work with the cpp backend, it'll be suddenly very complex to get everything working.
Good luck with implementing .ui support! Don't know how straightforward it will be :)
I saw the Go version and want to implement that.
One serious concern is that the cpp backend is required. Hence, if you use a library that does not work with the cpp backend, it'll be suddenly very complex to get everything working.
This is indeed a major pain that is recurring. I don't know the answer to that. I think that we have a couple of problems:
Yes, this is a major pain indeed.
Maybe we could have a separate Qt install option? You mean nimqt6 and nimqt5 packages that are identical, except for the compiler flags?
It would be nice if we could pass switches to nimble install (e.g. like nimble install nimqt -qt=5. Or maybe use different git branches?). I don't want to maintain two practically identical packages.
This nimble install nimqt -qt=5 would be good if you want to maintain Qt5, but I think that everyone is switching to Qt6.
But I meant that you can install Qt[5|6] with different methods. You can even choose a different Qt version and not install it. And I think that we need to document this.
I'm having trouble getting this to work on macOS.
The final compile is failing with 'file not found QtCore/qnamespace.h'. Looks like the compile is having trouble finding the headers in the frameworks in Qt/6.4.2/macOS/lib.
I think its finding qmake okay.
And then it went silent...
But I have a question: Can you give me a status about QML? Are you planning of integrating nimqt with QML?
What is the diffrerence between binding in nimqml and nimqt?
So far I have found in the docs.
nimqml uses https://github.com/filcuc/DOtherSide for the binding and creation of QObjects and compiles to c. Developer need to compile DOtherSide to a dynLib before you can use the fw.
nimqt uses https://github.com/woboq/verdigris for the binding and creation of QObjects at runtime and compiles to cpp. All is compiled statically.
Verdigris seems to be more lightweight for binding to Nim.
I think a separate project (for only Qml/QtQuick) with a bare minimum wrapper for communication between Nim and Qml based on Verdigris would offer a declarative way of Qml to design the ui and nim for the complete logic. No Qml-Js-runtime only the Qt application container to start and end the Qml app.
import NimQml
QtObject:
type Contact* = ref object of QObject
name: string
surname: string
proc delete*(self: Contact)
proc setup(self: Contact)
proc newContact*(): Contact =
new(result, delete)
result.name = ""
result.setup
proc delete*(self: Contact) =
self.QObject.delete
proc setup(self: Contact) =
self.QObject.setup
proc firstName*(self: Contact): string {.slot.} =
result = self.name
proc firstNameChanged*(self: Contact, firstName: string) {.signal.}
proc setFirstName(self: Contact, name: string) {.slot.} =
if self.name == name: return
self.name = name
self.firstNameChanged(name)
proc `firstName=`*(self: Contact, name: string) = self.setFirstName(name)
QtProperty[string] firstName:
read = firstName
write = setFirstName
notify = firstNameChanged
I like the Qml-Bindings syntax in nimqlm. So the question is, would nimqml based on Verdigris bring some advantages?
I have no idea, I haven't looked into Qml for a long time, and mostly forgot about it.
I think an advantage of DOtherside is that it uses c. There are more libraries supported by the c backend, and mixing c with c++ code was difficult, when I last tried it. And with respect to performance, I would guess the difference is small?
But more importantly, there doesn't seem to be good QML support in Verdigris for Qt6 on all platforms, I think.