Understanding Qt At a Deeper Level

Started by
9 comments, last by Servant of the Lord 9 years, 8 months ago

I started diving into Qt again last night, and a few things made me wonder how it works. For example: when the UI is designed in the Designer View, is the .ui file (an XML schema) compiled into a header/source file that contains all of the controls as if instantiated in-code? The reason I ask is because the .ui file's corresponding header/source pair's class is also prototyped in the scope of the UI namespace. Then, a data member is created from that, called "ui" within the class itself. That ui data member appears to contain a pointer for each QWidget added in the Designer. For example, if I drag a QPushButton into the Designer, and name it as pushButton in its Properties Inspector, the ui member will have a ui->pushButton member pointer within it of type QPushButton. Funny thing is, I couldn't find where the "pushButton" member was declared in-code. This leads me to think that Qt Creator's Intellisense is smart enough to parse the XML file, and a class being changed behind the scenes, or at least at compile time.

Then, there are, signals and slots. It sounds like they have some sort of special definition that's triggered by the Q_WIDGET macro. Also, QWidget::connect()'s 2nd and 4th parameters are of type const char*, and it's common practice to use the SIGNAL() and SLOT() macros to pass through prototypes of functions (not methods because they don't appear to be associated with any class). I understand that that I'm calling two functions that these widgets seem to have, but how does that work under-the-hood, exactly?

Does Qt rely on some special C++ compiler, or special rules?

Advertisement

Does Qt rely on some special C++ compiler, or special rules?

Qt relies on code generation.

Qt applications are written to an extension of C++ and then turned into regular C++ via code generation. This happens behind the scenes if you are using QtCreator. If you are not using QtCreator you would have to explicltly initiate this code generation step yourself as a custom build step or additional call in your build script.

The process that does the code generation is called the "meta object compiler".

I started diving into Qt again last night, and a few things made me wonder how it works. For example: when the UI is designed in the Designer View, is the .ui file (an XML schema) compiled into a header/source file that contains all of the controls as if instantiated in-code?

That is correct. The uic program translates your UI-file into a header file (header only though, no source file) that you include in your code to instantiate the UI. If you have a UI called mywindow you typically have the mywindow.h and mywindow.cpp files, and you'll get a file ui_mywindow.h from the UI compiler that you include to instantiate the UI in mywindow.cpp.

The reason I ask is because the .ui file's corresponding header/source pair's class is also prototyped in the scope of the UI namespace. Then, a data member is created from that, called "ui" within the class itself. That ui data member appears to contain a pointer for each QWidget added in the Designer. For example, if I drag a QPushButton into the Designer, and name it as pushButton in its Properties Inspector, the ui member will have a ui->pushButton member pointer within it of type QPushButton. Funny thing is, I couldn't find where the "pushButton" member was declared in-code. This leads me to think that Qt Creator's Intellisense is smart enough to parse the XML file, and a class being changed behind the scenes, or at least at compile time.

When you create a project in QtCreator, you are asked for an intermediate directory for your project. For example, if your project is located in <path>/test, this intermediate build directory is usually <path>/build-test-Desktop_Qt_5_2_1_MSVC2012_OpenGL_64bit-Debug, or a variant thereof, and depends on which version (5.2.1 in this case), which compiler (MSVC2012) and Qt build options (w/ OpenGL, 64-bit, debug library) you use. You find all temporary files from, for example, the uic and the moc compiler there. This is where you find your compiled UI header files.

Then, there are, signals and slots. It sounds like they have some sort of special definition that's triggered by the Q_WIDGET macro. Also, QWidget::connect()'s 2nd and 4th parameters are of type const char*, and it's common practice to use the SIGNAL() and SLOT() macros to pass through prototypes of functions (not methods because they don't appear to be associated with any class). I understand that that I'm calling two functions that these widgets seem to have, but how does that work under-the-hood, exactly?

I won't, or can't, go into the details about signals and slots in general, and certainly not about Qt's signals and slots in particular. See them as callbacks on steroids. When something happens, for example a button is clicked, the button's clicked() signal is emitted. This, sort of, inserts a message into Qt's "message loop", and Qt then calls all connected slots. So if you connect your onQuitButtonClicked() function to the quit button's clicked() slot, Qt will call onQuitbuttonClicked whenever the quit button's clicked signal is emitted.

So:

connect(button, SIGNAL(clicked()), this, SLOT(onClicked()));

informs Qt to call this->onClicked() whenever button's clicked() signal is emitted. It does not call button->clicked() at any time, it only observes it and responds by calling this->onClicked().

I suggest you study the idea behind signals and slots in online resources, but basically it's a kind of callback system that let's you receive calls to functions when something happens. Sometimes the signal is passed through the message loop, and sometimes the slot can be called immediately from the signal, but those details are not really relevant to the concept of signals and slots.

Does Qt rely on some special C++ compiler, or special rules?

jwezorek already got a reply before I finished this, so I won't repeat much of it: no special C++ compiler, but it preprocesses your UI and source/header files using the UI and meta object compiler to generate additional source/header files that is passed to the C++ compiler.

Then, there are, signals and slots. It sounds like they have some sort of special definition that's triggered by the Q_WIDGET macro.

The Q_OBJECT macro actually; any QObject-derived class can make use of signals and slots. The Q_WIDGET macro implements the Q_OBJECT macro for QWidgets, adding additional QWidget-specific features.

The 'signals' and 'public slots' 'private slots' macros actually generate basically no code.

'signals' is:


#define signals public

(It just makes the following functions publicly-accessible)

And 'slots' is defined as:


#define slots

(It doesn't create any code)

The benefit is that Qt's preprocessor (qmoc) can search for the 'signals' keyword and the 'slots' keyword, and then know that any function declarations that follow it are signals or slots.

Also, QWidget::connect()...

You mean QObject::connect() - QWidgets inherit QObject's functions.

Also, QWidget::connect()'s 2nd and 4th parameters are of type const char*, and it's common practice to use the SIGNAL() and SLOT() macros to pass through prototypes of functions (not methods because they don't appear to be associated with any class). I understand that that I'm calling two functions that these widgets seem to have, but how does that work under-the-hood, exactly?

Here's a link I found interesting in the past: http://woboq.com/blog/how-qt-signals-slots-work.html

Ahhh, ok. Things are starting to click! Looks like my speculation of code generation is correct, and thanks to Brother Bob, I now know where it's stored as well as why that extra generated folder is so important as it contains all of my generated header files. I did read up on the Qt's uic program for compiling .ui files into a header file as I wanted to use Qt Designer with Code::Blocks directly for my font renderer since I didn't want to setup my engine's code-base to yet another IDE. The magic behind signals and slots are interesting, though... I need to dive into that a bit more.

In general I would advise not to actively use 'signals' or 'slots'. Since they are macros there is a risk for that to collide with third party libraries and to avoid problems here you can switch these macros off by configuring Qt accordingly. Instead, you should use 'Q_SIGNALS' and 'Q_SLOTS' which, while still macros, are not of such a risk of namespace collisions.

Also, for the purpose of understanding Qt at a deeper level I would forget about designing using .ui files. They don't allow you to do anything you could not also do by hand but they hide a lot of the details. That's good when you need to get something done quickly, it's bad when you really want to understand what is happening. Try building a few UIs yourself. Create layouts, add child widgets to them and research which signal/slot connections you need to get something done. That will be slow going at first but it will force you to do a lot of research in the documentation. Liberally read that, things linked there, as well as the introduction of Qt concepts.

I now know where it's stored as well as why that extra generated folder is so important as it contains all of my generated header files.

If you delete the folder and re-run the qmake process, they'll get regenerated. They're the same kind of idea as .o files - they aren't part of the "project" itself

.cpp, .h, .ui, .pro files are part of the "project", but .o and moc_XXX.cpp files are merely intermediary steps in the compile process and don't need to be backed up, preserved, or committed to repositories.

QtDesigner is really helpful - I definitely use it alot. However, it's good to learn how to programmatically create and layout the Qt widgets using the Qt API C++ functions, because occasionally you need to make something a bit more dynamic that QtDesigner can't handle directly. It's not un-common, at least in my projects. Any QWidget you create can be used in QtDesigner (by making a QWidget in the designer, and then 'promoting' it to your QWidget-derived class), so it's good to know both. So learn and use both methods (QtDesigner, and manual laying out of widgets using QLayouts)

@BitMaster: I actually started building a few simple UIs in-code to get the hang of it. It's kind of similar to how one would build an editor script for Unity, actually --everything is organized by horizontal and vertical layouts. My experience with the Unity Editor script helped me finally wrap my head around how simple the layouts actually are. When I first started diving into this, I thought there was more to it because it was so simple. Signals and slots are making more sense now too. The thing is, I might need to create one of those myself!

@Servant of the Lord: I came that conclusion as well --that 'temp' folder actually contains the generated header files that are the source manifestation of the .ui files generated in the Designer view. You also bring up a good point about doing stuff programmatically that the Designer might not be able to do directly. For example, you can't setup a custom widget of your own in the designer, and configure its properties. I think you have to setup a generic widget in the Designer (if you even use the Designer), then promote it to the custom widget's subclass. It'll be instantiated as that subclass in-code (I hope), and then I could setup the widget's specific properties from its pointer provided by my window's/dialog's "ui" member once it's been setup.

I think you have to setup a generic widget in the Designer (if you even use the Designer), then promote it to the custom widget's subclass. It'll be instantiated as that subclass in-code (I hope), and then I could setup the widget's specific properties from its pointer provided by my window's/dialog's "ui" member once it's been setup.

Yep, that's how it's done - and it works fairly well, once you get used to it.

If you make a generic widget that you'll use alot, you can code an interface class for Designer so you can visually see the widget and modify it's properties from within Designer - but I've never personally done that, so I'm not sure how well it works.

I think you have to setup a generic widget in the Designer (if you even use the Designer), then promote it to the custom widget's subclass. It'll be instantiated as that subclass in-code (I hope), and then I could setup the widget's specific properties from its pointer provided by my window's/dialog's "ui" member once it's been setup.

Yep, that's how it's done - and it works fairly well, once you get used to it.

If you make a generic widget that you'll use alot, you can code an interface class for Designer so you can visually see the widget and modify it's properties from within Designer - but I've never personally done that, so I'm not sure how well it works.

That's actually what I've done. I designed my TransformWidget in the Designer as a Widget, and then the source file would have setters/getters (I miss C# properties) to retrieve the values. Then, whenever I select one of my game's objects in my InspectorDock, I'd clear my InspectorDock, and instantiate a new TransformWidget at the top. I'm running into issues though... I might have to go over to the Qt Forums to get a better handle on this. I'm starting to run into a lot of road blocks.

This topic is closed to new replies.

Advertisement