Hacker News new | past | comments | ask | show | jobs | submit login
How to architect a Qt/C++ Application (cleanqt.io)
155 points by AlexFagrell on Jan 7, 2019 | hide | past | favorite | 61 comments



Hi guys, This is the 7th post in the series "Crash course in Qt for C++ developers." Do you structure and architect your application in a similar way? Let me know what you think and I would appreciate if you have any feedback.


I'm going to write a GUI in 3 months using qt + rust. Bookmarking for later.


You should checkout gtk-rs[0] too if you're settled on rust but have an option to pick other GUI library.

0: https://gtk-rs.org/


Can you say a little bit about what makes it great? Is it just a well designed wrapper? Super easy to use? Good documentation? More mature than Rusts Qt bindings?

Personally, I like Qt a lot more than GTK (both as a programmer and a user), but to each their own. QtQuick/QML is really great way of quickly whipping together complex UI’s. I’ve never used Qt on Rust though.


Hopefully my experience could be useful, having tried rust-qt just a while ago on a project. Bottom line: it is usable.

It is pretty trivial if you want to build an almost purely QML app. As soon as you want to do some C++ stuff (e.g. registering new C++ classes, using QtWidgets), however, things go south very quickly. The main problem is that Qt has its own ideas about ownership (QObject-parent-stuff), and Rust really does not like that. You end of working a lot with raw pointers and boxes. It feels like you’re writing C++ but in Rust (if that makes sense).

Rust-qt is really nice IMO, and I would extremely encourage anyone interested to take a look, and provide more feedbacks and contributions to the maintainers (I am not one of them). Some non-trivial examples would help a lot. But expect sharp edges.


I have virtually no experience with Qt (as a developer). That said, I find the gtk-rs bindings good and easy to use in rust. They are automatically generated from C code, and as the generator matures the bindings come more and more complete. Tho' some of the bindings are manually written. AFAIK Gtk has QML counter part, but I haven't used that either.

The documentation isn't perfect but its _really_ easy to map the documentation of the bindings to the original C documentation. From quick glance the situation seems to be more or less (maybe a bit less) the same with python bindings for Gtk.

I personally have enjoyed the gtk-rs bindings.


I have not but I looked at qml and find it elegant. I will give it a look.


Have you found any good tutorials or even documentation for using rust-qt?

The bindgen that seems to show up on google search is very un-intuitive to use (or maybe that's because I'm a rust newbie)


I seem to remember having come a Ross a link where the code objects in qml were rust and not Js. Can't find now. Seen this:?

https://www.vandenoever.info/blog/2017/02/17/a-simple-rust-g...


That uses the qml crate that has not been updated in 2 years. Quite unlikely to not work at all.

I've tried most QT Rust projects and none were all that usable. I'm afraid your plans are not likely to succeed.


I'm developing a few hobby projects with Qt and Rust. The Rust Qt Binding Generator I developed for that works so well for me that I've barely needed to improve it in months.

https://www.vandenoever.info/blog/2018/10/30/building_qt_app...


That json glue is quite ofputting tbh. I did try it, but failed building it on macos.


The JSON glue could be changed to Rust macros, but I've not found the time to do that. It's a bit ugly but free and works for me on Linux and I've had reports of success on Raspberry Pi and Windows.

If it fails to build on Mac OS, I'd like to see why and help. The latest version uses Cargo for building, so e.g. `cargo qrep && qrep` should give you a working Rust + Qt example application.


My project, Qml.Net (https://github.com/qmlnet/qmlnet), does this as well, but with .NET.


You have too many CMakeFiles. I'm sure you can just simply them all into a single CMakeFile at the top level.


In the very least modern cmake requires one CMakeList.txt per module (application or library), not to mention the project.


No it doesnt. You can define multiple build targets in a si gle cmake file.


Hey thanks, I'm really enjoying this series. It's nice to read a clear approach after the many revisions to the C++ standard.

Keen to see more on the QtQuick/QML stuff. I keep reading things about how great it is but never had the time to get into it.

For your "good-to-know topics" perhaps a brief direction for deploying applications? Bonus points for mobile deployment.


I disagree with your diagram of model-view-controller. Traditionally, the view and the controller are both part of the user interface, so should be on the top part of your diagram. In fact the controller is higher level than the view.

The distinction between these two parts is that the view is just a display of the data (model) whereas the controller is a way for the user to edit it. This was more relevant back in the 1970s where these things were more often separate. For a modern(ish) example, think of Vim's separation between the main display of your text file (the view) and its command line at the bottom (the controller). In most modern GUIs this distinction is meaningless, so you would group controller and view into the view and have a simple overall model-view separation.

Now I'm not saying that all of the classes you've called (something)Controller should be moved to the UI, I'm just saying that they're misnamed (at least by the traditional definitions). I think that your interpretation of model vs controller is raw model data vs non-graphical operations that act on them. But operations on the data are just as much part of the model as the data itself. For example, if I have a word processor then my model is a document, including operations on it. If I want to update the document programmatically (bypassing the GUI), for example to add a word, then I expect to call a method of the model. This should do everything to keep the model consistent, such as returning an updated word count. But that still doesn't need anything called a controller.

I just said that I'm not telling you to move your controller classes to the UI (view) layer. But that's a general comment about the meaning of "model view controller". The specific example you present on that page is "MenuModel" and "MenuController". These sound like things that both belong in the UI layer. Going back to the word processor example, your model might have a SaveToFile() method (although it would expect a file name parameter - popping up a save file dialogue box is the UI's job). But it won't know whether that came from a File->Save menu item or the user hitting ctrl+S. It's the UI's (view's) job to create a menu, choose what commands go on it, and map the OnClick events to actions on the model.

No doubt others will disagree with my understanding of the definition of "controller". This is the one thing you can be really sure about with that term! And it's another good reason to avoid it. Everyone agrees with "model" and "view" mean, so if you use those terms then people will know what you mean, but if you describe something as a "controller" then it just causes confusion.


I think the MVC paradigm has such disagreement and so many variations and perspectives that it has effectively salted those words.

There's some really good ideas in it, and you definitely make valid points. But this ^ happens too much for "MVC" to be a valuable term.


The controller is always where the trouble arises because it glosses over a lot of details [1], and there's way too much stuff you can put in there if you're not careful. Before you know it you have thousands of lines of highly coupled code. This has been especially true for me in mobile applications (though much of this is relevant on other platforms too).

Over the years I've extracted many components from my controllers to the point where they're finally getting pretty slim. At first I let the controller fetch the model - e.g. update a model from the network, but later extracted this into a manager/provider object so multiple controllers and views can easily share this. Then I removed anything resembling business logic from the controller as I realised that was part of the model too, and moved this into service objects or utility functions (and not on the model-objects themselves, as I try to keep these focused just on data storage and serialization/deserialization). This might also include some form of dispatch if the business logic is triggered by a command. Similarly I moved all kinds of formatting code into presenter objects. Then I added viewmodels as an adapter layer on top of the actual model - both to insulate the controller from the actual model (didn't seem that important at first, but I finally 'got it' later) and to have a place to store local state such as current selection, scroll position, etc. And finally, I recently went all the way with the viewmodel after I extracted the logic of which view leads to which view out of the controller and into a flow coordinator object. This way, a given controller doesn't know what view is shown when you click the 'Next' button - instead it asks its viewmodel. That way the controller doesn't know anything about what needs to happen next or how the next view is shown. And that way all the logic about which view leads to which view resides in the flow coordinator which injects this into the controllers via the viewmodel. That way a view-controller pair becomes a completely reusable component that can be shown in multiple ways from multiple locations as part of different flows, and even using different models.

I don't always bother with all of the above, but it goes to show just how many separate concerns can actually end up in a controller, or instead be extracted to its own object: Fetching the model (provider), formatting the data (presenters), mutating the model (service/dispatch objects), wrapping the model (viewmodel), local ui state (viewmodel) and navigation between views (flow coordinator). It really goes to show that MVC isn't so much an architecture as it's the foundation for one; You've separated your view and model - great, now what about the rest of the application?

[1] http://i.imgur.com/rCr9A.png


In the Qt model view and controller are implemented under the same interface (QAbstractItemView). This is fairly typical for many practical MVC implementations I've seen.


Thanks, great comment!

While outlining the post, I was uncertain what to call the particular group of classes (or concern) now called the Controllers. If you look through the GitHub project, you'll find that at one point they were called Routers. I agree with you that this design is not a MVC by the traditional definition (i.e. Smalltalk's), however there are many variants out there, see for example Apple's https://developer.apple.com/library/archive/documentation/Ge..., that I didn't think it would hurt 'to add' another. As long as it's clearly defined what it is. But perhaps it is not clear.

In this design, the 'traditional controller' is already part of the front-end. One of the goals of this design is to keep the front-end only concerned with styling and the layout of the application, and not the logic or the flow. So from the front-end, the user-interactions will be forwarded down to the 'back-end' Controllers. Those controllers are responsible for the flow of the application. The back-end includes logic and models in the traditional sense, see for example the DocumentsModel (https://github.com/Fagrell/clean-editor/blob/master/lib/publ...), however there are also models that correspond to specific Components. Those "Model Components" contain logic and a state, e.g. the MenuModel referred to in the post.

Perhaps an even better separation would be to add another layer?

- UI front-end - View/Component layer -> includes the 'view and controller by the traditional definition'.

- Router/Controller layer -> includes Models for specific components and Routers (as Controllers are defined in the post). Defines the flow.

- Back-end layer -> includes models by the traditional definition.

Thoughts?


The apple diagram doesn’t look too far off what quietbritishjim described though, assuming that the “updates” arrow from controller to model is the model asking for an update, but the model actually implementing the logic.

Personally, I hate the name controller because its really unclear to me what they actually do. What do they control? The actual update logic is part of the domain knowledge, so that should live in the model itself, as quietbritishjim said. Router is a better name imho (even if itself not a perfect name).


The distinction you describe between view and controller should be relevant nowadays in the exact same way as it was on the 1970s.

For example, the logic of inserting text when insertion mode is active, removing the selected text when the delete key is pressed etc is all part of the controller, not the view.

By splitting the view and the controller, a different view, for example a console one, or a remote one, can be done with the exact same editing capabilities, since they are to use the same controller.


The MVC pattern dates back to the time when people actually had to write code for the "view" part. These days most people don't write code for this anymore (except for games and other graphical apps) but use a toolkit. In this case the Qt provides the view so the code where the user writes the signal handlers to handle signals such as button presses etc. is the controller. The view part doesn't need to be done.


> so you would group controller and view into the view and have a simple overall model-view separation.

> ...

> But operations on the data are just as much part of the model as the data itself.

So you want to move controller into the view or to the model? I can't tell. :)


Sorry if that wasn't clear.

I understand the term "model" to include non-graphical operations on model data. For example, SaveToFile() or InsertTextAtPosition() methods. These should definitely NOT be merged with the view!

--> I believe that the linked article called these non-graphical operations "controllers", but I disagree.

----------------

I understand the term "controller" to be graphical controls that allow manipulation of the data. For example, a the QMenuItem "save" along with its associated signal (which of course will just call a method on the model), or the OnKeyPress handler on an edit control.

--> I believe that the linked article just considers these to be part of the "view". I'm actually OK with this, because I the distinction between controller (in this sense) and view is not very useful.


[flagged]


FYI Qt like frameworks were here before Electron, this toolkits are not reinventing the Web world for the desktop even if some will use web tech sometimes like JS or cSS.

Also what is the Electron equivalent for QGridView , a table? or you mean Electron + 200+ npm packages, where each project uses different packages and 50% of the packages are the same package but different version


Is this sarcasm?

QT is a decade-plus older than Electron, and UI toolkits in general even more so.

Furthermore, Electron hasn't solved anything except the "I want to make a desktop app but only employ JS/HTML devs" (and that problematically).


More precisely, Qt 1.0 was released in 1996, QML 1 in 2009. Current versions are Qt 5(.12) and QML 2. Electron was first released in 2013.


I'm sure that a well-architected Electron app can have perfectly good separation of model and view (and potentially contoller, whatever you mean by that). It's an orthogonal discussion compared to what graphical toolkit to use. As for reinventing the wheel: MVC predates Electron by at least 40 years.


Electorn is the one reinventing the wheel. Qt and many many other GUI libraries are much older than electron.


you know that Electron uses blink, right ? here's a small excerpt from the Blink source code, for you : https://github.com/chromium/chromium/blob/662ef5f51c1c4612be...

I invite you to look up who Lars Knoll is, and what became of the KHTML project (https://en.wikipedia.org/wiki/KHTML)


You go just use QML and C# as well.

https://github.com/qmlnet/qmlnet

Disclaimer: I'm the author.


Apologies for the somewhat off-topic questions:

* Could this be mixed into an existing C# WinForms applications? I'm just talking about using a mix of different window types; I don't want/expect a mix within individual windows.

* Do you know if QT Quick has significantly better performance than WinForms for 2D graphics? I know that WinForms is not hardware accelerated (because it's based on GDI+ which was abandoned by Microsoft before they added hardware acceleration support). But I find documentation about QT Quick's hardware acceleration vague and confusing e.g. [1].

I work on an old WinForms application that makes heavy use of traditional 2D graphics APIs (i.e. Pen and Brush objects used during OnPaint events) but performance is a significant practical problem, and I'd like to find a way to move to something faster somewhat incrementally. My current thinking is to use WPM for new windows, but QT might be a bit nicer.

[1] http://doc.qt.io/QtQuick2DRenderer/qtquick2drenderer-perform...


Well, you are looking at the description of the software renderer. Qt Quick is hardware accelerated (OpenGL) by default. A subset of it is supported by the software renderer. With hardware acceleration, performance is generally great.

Actual rendering overview (rather technical): http://doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html

Cool blog post about performance improvement through draw call batching: http://blog.qt.io/blog/2013/09/02/new-scene-graph-renderer/


Thanks for the help!


do you support core?


At the top of the repo it lists Framework, Core and Mono support.


whoops


re: clean. What happened to Qbs? [1] Is it still not popular?

I ask because this same tutorial has an explanation on how to use CMake with Qt. Arguably Qbs is a lot "cleaner" than CMake scripting... but it doesn't appear to be a popular choice.

1: http://doc.qt.io/qbs/


Agree, unfortunately the Qt company decided to "deprecate" it, or rather to not put anymore resources into it: http://blog.qt.io/blog/2018/10/29/deprecation-of-qbs/


Reading that post and the comments makes me want to try out Qbs! I was vaguely aware of its being announced a few years ago, but I hadn't realised it had even become mature, let alone deprecated. Sounds like a good match for some of my gnarlier qmake-based projects. (Can't abide CMake)


This is very sad indeed, because Qbs was both well architected and well executed. Very fast and the only build system where I never had to clean the build because the build system broke itself due to bad design/assumptions (e.g. mtime checking).


From what I heard at the QtWS in December they're veeery slooowly deprecating qmake in favor of cmake, but I guess cmake is not yet the default and qmake won't be abolished for years, if ever... But the direction seems clear.


Qbs is (was?) a great build system even for non-Qt projects. The declarative syntax is simple, clear, and in no way Qt specific. Sadly it just never gained the traction it deserved.


How can I follow this blog? I don't see RSS. Feedly doesn't know it. And there seems to be no email option either.

How do you follow blogs in general these days. My twitter is too full of things, so I'd probably miss the posts sometimes.


Hey buddy! I've meant to add RSS and email to the blog but haven't got around to do it yet. Sorry... Thanks for the reminder though, I'll see what I can do over the weekend! Cheers, Alex


I just want to ask, why?

Today we've a superior technology like Electron. Why should we use old technology like Qt?

VSCode is built in Electron.


Electron is not superior. Web grew because of the great "application delivery" system and despite everything else that originated from the static web. By having huge number of js developers the thing was forced to do other tasks. Now with Electron it may as well do desktop apps (by removing the awesome "application delivery" system; isn't that funny?). Its only advantage is that you've got lots of js developers.

The "new" js desktop technology is new only in the sense that js is new to the desktop (so it still doesn't really know about all the desktop requirements). Same for servers: there are better tools for the job from the purely technical point of view.


How exactly is Electron superior?

> VSCode is built in Electron.

So is slack, which (from what I've heard, I have no personal experience) eats RAM like a maniac. Neither of these example applications tips the scale between Qt or Electron on being superior.

Again, based on what I've heard/read, Qt being less recourse hungry and written in C++ allows it to be (for example) embedded (to cars or iot devices, for example).


> Today we've a superior technology like Electron.

If there is anything superior to Qt, Electron clearly aint it.


Why Electron is superior? We do mobility simulation and the UI part is the minor of our concerns. We are focused in modelling capabilities and performance (so C++). Having a solution that uses the same language across all the application layers help us a lot.


> VSCode is built in Electron.

And it takes up 3GB of RAM on my 8GB machine.


Because Electron is not "superior". Both have their strengths and weaknesses, and which one is the right choice depends on what you're doing and what your existing skills and components are.


QT gives you a native-ish RAM-efficient GUI across a wide range of systems.


I thought QuickTime Player is only available on Mac OS nowadays.


In the embedded world, you have much more chance to run QT than Electron, because QT also supports RTOS like VxWorks or QNX.


Qt also has a renderer that's simply superior by design for embedded use cases. A web engine has to bent over backwards to do anything comparable.


Yes. HTML + DOM + CSS + JS was not created to write interactive applications, and it still shows. Most of the inconvenience can now be worked around with frameworks, but a large performance penalty remains. And Moore's Law is over.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: