Qt – 3 Methods Of Adding & Integrating Designer Forms

The Qt Designer tool allows us to create forms for our application in a visual manner, freeing us from the tedium of hand-coding. While this saves time, it’s important to understand the various ways Qt creates these forms and by extension, the methods at our disposal to integrate these forms into a larger application.

In this post we’ll look at the 3 main technical options Qt Designer employs to serve this function:

  • Aggregation as a pointer member
  • Aggregation
  • Multiple Inheritance

We’ll also see examples of each method in practice. By the end of this post we should have a good grasp on the various ways we can use Qt Designer to create and integrate designer forms into a larger application.

When considering which method to use it’s important to understand why multiple methods exist in the first place.

The first part of our answer is to understand that when we design an application, our user interface will at some point need to interact with our underlying program logic and/or data structures. Thus, while we can create a new form for our application using the visual editor, it’s up to us to make a programmatic connection so these elements actually do something meaning ful in the context if the larger application.

In short then, how do we get say, a dialog box to write values to a main window? That is, how does one window (designer form) talk to another?

The three methods mentioned above are an abstract answer to this question.

So let’s open Qt Creator and create a basic, empty Qt GUI Application. For our purposes we’ll leave the default names the same, which means we should have a few basic files in our project:

main.cpp

mainwindow.cpp

mainwindow.h

mainwindow.ui

formsdemo.pro

At least in Qt Creator 2.0, the first window that should be open when we leave the create project wizard is the integrated Qt Designer tool. We can close that for now, as our focus is the code files Qt Creator has created for us.

Specifically, mainwindow.cpp and its definition file, mainwindow.h.

The contents of these files will differ based on a setting you may not have seen before:

Qt Preferences > Designer > Embedding of the UI Class

Notice the three options:

  • Aggregation as a pointer member
  • Aggregation
  • Multiple Inheritance

The default on a fresh install should the first: Aggregation as a pointer member. If it’s not, then set it as such and recreate the project (delete the one you just created and start over).

The reason why is with this setting Qt Creator designates the mainwindow.cpp and mainwindow.h files to utilize a private pointer to our Ui::MainWindow object, which is in turn defined in mainwindow.h, which itself part of the Ui:: namespace, of which Ui_MainWindow is a subclass of MainWindow, our main window widget.

It’s a mouthful, and may not the easiest to understand strait away. The end result is that in mainwindow.cpp, so long as we include:

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"

…our Ui objects defined in ui_mainwindow.h should all be accessible (exposed) via our private ui-> pointer in mainwindow.cpp.

Sure enough, our MainWindow constructor contains a call to setupUi() using this pointer:

  1. MainWindow::MainWindow(QWidget *parent) :
  2.     QMainWindow(parent),
  3.     ui(new Ui::MainWindow)
  4. {
  5.     ui->setupUi(this);
  6. }

Again, this is somewhat of a mouthful and is not always the easiest to understand. This technique gets at the heart of some of C++’s more advanced Object Oriented features, many of which could be completely foreign to you. Complexities aside, the end result is with this logic any time we need to access a Ui element in our MainWindow class, we simply use the ui-> pointer.

This is an incredibly important point, as it sets us up for understanding how to add and integrate our own designer forms. In other words, when faced with the task of linking a Ui_ definition file (ui_mainwindow.h) to the mainwindow.h and .cpp files, this is what the designers of Qt have decided to do…it’s a pretty good idea to follow this lead!

Creating A New Designer Form

To see why, right click on your projects name in the Protect Panel (the listing with all the project files, left and side), and from the context menu, click Add New…

Depending on your platform and version, you need to locate the Qt Designer From Class template.

Choose Dialog without Buttons and click continue.

Now set the Class name MyDialog.

This will update the other fields in the wizard such that your header and source files will be mydialog.cpp and mydialog.h respectively.

Again, the first window that will open is the Qt Designer editor. Go ahead and add a single lineEdit item to the dialog and save and close the Qt Designer editor.

Let’s assume this dialog box needs to communicate with the MainWindow form. How do we do that?

We have two main options: Signals and Slots, and Direct Exposure.

Signals and Slots

This is perhaps the most ‘pure’ method, as is means we do not have to force direct connections between widgets. We simply set up a signal from MyDialog and create a slot in MainWindow to process any signals it receives. The only down-side to this method is it creates more coding work, and can be harder to maintain. The upshot is as we’re using signals and slots, we have a greater ability to compose multiple objects together and only listen to the elements we choose. In short, the dialog and caller remain disconnected for the most part.

Direct Exposure

Direct Exposure means we manipulate the mydialog.h file to gain direct access to its ui members from the calling class, in this case MainWindow. The manipulation is a bit of a force though, as our change is making the ui-> member pointer public instead of the default private.

I addition to the privilege change, we must also include both mydialog.h and ui_mydialog.h in the mainwindow.cpp file.

The upshot to this manipulation and inclusion is our MyDialog ui members are now visible to MainWindow, and can be accessed in a manner similar to:

  1. if(m1b.exec()){
  2.     // we can now access method1 ui members like in Method2
  3.     QString value = m1b.ui->lineEdit->text();
  4.     ui->lineEdit1->setText(value);
  5. }

Again, it’s important to note that in order to get to this point we needed to make a private member public, as well as include two header files in our mainwindow.cpp file. This could be considered a bit of heavy-handed coding, and may not always be what you want. That said, it’s a totally legitimate solution, and is certainly less code-heavy and maintenance prone than the Signals and Slots approach.

What About The Others?

Up until this point we’ve only looked at the first of Qt Creators three options for creating code, Aggregation as a pointer member. The other two are just as important, and in some ways, perhaps a bit more convenient.

Aggregation

First we have aggregation. This is very similar in usage terms to the first, in that we aggregate the ui object into our definition file. However, as it’s not a pointer the syntax is a bit cleaner, and in terms of includes, is a bit easier to keep track of as the ui_.h file (the one created by moc), is included in the .h file of the form implementation, meaning we do not need to include it in the mainwindow.cpp class. However, we still need to make the Ui:: member public instead of private, which means we need to edit the moc generated code. This is fine, but is something you should be aware of when faced with creating many widgets.

The end result however, is that we can now access the ui members from MainWindow:

  1. // must make [ Ui::Method2 ui ] public in .h
  2. Method2 m2;
  3. if(m2.exec()){
  4.     QString value = m2.ui.lineEdit->text();
  5.     ui->lineEdit2->setText(value);
  6. }

Notice that as our Ui member was created on the stack as opposed to the heap like the first method, we access the ui elements using the member operator (.)

Multiple Inheritance

This is an interesting approach, as from a pedagogical standpoint many of the Qt learning tools use this method in their examples. In other words, knowing about this method helps us learn Qt.

As the name suggests, in this method we subclass both the Base Class of our Widget as well as the namespaced class, which designates the Ui_ class as a subclass.

This dependency chain thus provides our base class with direct access to our forms Ui elements. As can be seen, this makes for the cleanest, most easy-to-understand code:

  1. // must change generated .h code to be public::
  2. Method3 m3;
  3. if(m3.exec()){
  4.     QString value = m3.lineEdit->text();
  5.     ui->lineEdit3->setText(value);
  6. }

However, it should be said this benefit comes with a price. Classes defined in this way or almost certainly less flexible for more complex projects, as now we’ve created a tight bond between ui definition and implementation.

Overall though it’s not a surprise why Qt chooses this method in many example code blocks, it’s the easiest to understand and totally sufficient, at least for smaller projects.

Conclusion

This post is rather heavy on theory, so to aid in its consumption I’ve created a test project which shows off each method. The project is purposely light on comments, as the goal here is to just open it up and stalk through the code, letting your own explorations decipher it.

Please keep in mind that their are no doubt other techniques and strategies/design patterns for adding and integrating Qt Designer forms.

Code Download

Contains a simple project showing the methods discussed in this post.

uimembers

Comments are closed.