Introducing seaqt, a new set of bindings for Qt - hopefully of interest for anyone doing cross-platform GUI development.
The project is at this stage a proof-of-concept to explore where we could go next with user interfaces like status desktop - while nimqml has served us well, it lacks in coverage / access to the majority of Qt which has become a limiting factor - this is where seaqt comes in.
seaqt takes a slightly more modern approach to wrapping than nimqml/DOtherSide, namely using a clang-based parser inherited from miqt - a similar binding project for Go.
The parser spits out C wrappers which in turn are exposed to Nim via the usual importc dance. Unlike nimqt, this approach allows us to continue using nim c and a bonus on top is that we share most of the C codebase with the Go bindings. Of course,seaqt could also be used stand-alone in C applications, or as the base for other FFI-capable languages.
On top of that, there's a nimqml port to `seaqt` that removes the DOtherSide dependency and instead uses seaqt as backend - if you have an existing nimqml application, this can be used to seamlessly switch to `seaqt` and start using its facilities gradually instead of a full rewrite. Advantages include build simplification, compile-time meta-object support, low wrapper overhead and tighter integration while at the same time gaining access to all those sweet Qt modules.
Here's a sample Qt application that implements a list view, using the emulated inheritance of QAbstractItemModel:
import
seaqt/[qapplication, qabstractlistmodel, qlistview, qvariant, QtCore/gen_qnamespace]
type Model = ref object of VirtualQAbstractListModel
method rowCount(self: Model, idx: QModelIndex): cint =
1000
method data(self: Model, idx: QModelIndex, role: cint): QVariant =
if not idx.isValid():
return QVariant.create()
if role == ItemDataRoleEnum.DisplayRole:
QVariant.create("this is row " & $idx.row())
else:
QVariant.create()
proc main() =
let
_ = QApplication.create()
model = Model()
QAbstractListModel.create(model)
let v = QListView.create()
v.setModel(model[])
v.show()
discard QApplication.exec()
when isMainModule:
main()
All you need to compile it is seaqt itself, a Qt installation with -dev packages (pkg-config is used to find Qt) and Nim 2.0+ (memory management is done "mostly" via destructors).
There are of course still lots of things to work on - right now, the only DSL available for creating slots and signals is that of nimqml - there's still work to do on memory management and ownership of widgets - naming, ergonomics, module layout and really everything else is still up for grabs, which is what this post is for: an invitation to try it out, leave some feedback and/or join the seaqt org to get the project to really shine.
Further, the way the generator is written with clang doing the heavy C++ lifting, it's also pretty close to being able to wrap custom Qt libraries and widgets, or maybe even as a "general" C++ code importer (yay, yet another one!) as long as it's not too fancy. Enjoy!
Thank you very much. This is very much appreciated.
I have a meta question though which is more related to the status desktop app itself. The last time I tried it it did not feel particularly snappy at all. As in it did not feel better than an Electron application.
Was that the same experience you guys noticed or is it just me? I remember it still used QTWebEngine and was heavy.
I felt the same about Dropbox app as well (I think it used QTWebengine as well).
Are you happy with QT's performance overall?
performance
Status desktop has a pretty heavy backend running that has to deal with a lot of crypto complexity - the architecture itself also needs an overhaul, as it's currently shared with a mobile app that has completely different demands. A small part of the slowness is also due to nimqml that adds its own overhead and limitations.
I wouldn't pin desktop's performance issues on Qt itself, which I've seen work well in other applications if you let it (ie don't overload the UI event loop thread with computational work which to a certain extent is happening in desktop right now).
Re web engine, I don't know really. Seems kind of odd to host a web app in a Qt window, it would be missing the point somewhat since their whole thing is widgets, layouts and QML.
Status desktop has a pretty heavy backend running that has to deal with a lot of crypto complexity - the architecture itself also needs an overhaul, as it's currently shared with a mobile app that has completely different demands.
Understood. Would splitting the backend and GUI work a bit to give some feeling of snappiness? I think that's what the AV companies do.
Would splitting the backend and GUI work a bit to give some feeling of snappiness?
Indeed - that's already done to a certain extent but not to the extend that is needed - this is the architectural problem I hint at, that requires a redesign.
If you talk abut the QVariant serialization
One problem is the inability to return QObject* directly from signals/properties but rather having to wrap it "manually" - apart from being inconvenient in the nim code, apparently QML binding caching is affected somehow - either that or something else in nimqml's QMetaObject generation apparently causes trouble / increased traffic between QML and custom QObject instances.
I have not dug into the detail here, except to note that DOtherSide indeed uses "dynamic" metaobject generation which misses out on a few details such as a qt_static_metacall implementation etc which the seaqt backend for nimqml improves on - these papercuts add up when you have lots of QML objects bound, in a way that our Qt devs that previously worked with Qt/C++ natively say shouldn't be happening.
That said, I agree the slow performance of status desktop is mainly architectural, as I wrote above - getting rid of the papercuts would be an improvement but it would just kick the can down the road.