Integrating Qt Designer Files Into Your Project

Creating forms with Qt Designer gets you only halfway to completing your application. Somehow you have to integrate the created forms into your application; and not only into your application but also into your automatic build process.

To start discussing how you can achieve this, let's review the steps needed to create a form with Qt Designer:

As you can see, there are quite a number of steps involved. From the command line, the following could be the steps needed once you have saved the .ui file (let's call it myform.ui) in Qt Designer:

uic -o myform.h myform.ui # generate header file
uic -o myform.cpp -impl myform.h myform.ui # generate implementation file
uic -o myformimpl.h -subdecl MyFormImpl myform.h myform.ui # generate subclass header file
uic -o myformimpl.cpp -subimpl MyFormImpl myformimpl.h myform.ui # generate subclass implementation file
edit myformimpl.h # edit subclass header file
edit myformimpl.cpp # edit subclass implementation file
moc -o moc_myform.cpp myform.h # generate moc code for base class
moc -o moc_myformimpl.cpp myformimpl.h # generate moc code for subclass
c++ -c myform.cpp -I$(QTDIR)/include # compile base class, use similar command on Windows
c++ -c moc_myform.cpp -I$(QTDIR)/include # compile base class moc code, use similar command on Windows
c++ -c myformimpl.cpp -I$(QTDIR)/include # compile subclass, use similar command on Windows
c++ -c moc_myformimpl.cpp -I$(QTDIR)/include # compile subclass moc code, use similar command on Windows
c++ -o myform myform.o moc_myform.o myformimpl.o moc_myformimpl.o -L$(QTDIR)/lib -lqt # link everything together, use similar command on Windows

But this is not enough. If you need to change anything in your dialog design, you go back to Qt Designer, make your changes interactively, save the ui file again, and go through most of the previous steps again. All this cries for automation, but before you learn how to automate these steps, we need to talk a bit about the generation of subclass files.

For two reasons, the generation of subclass files falls a bit out of the ordinary build process: First, you are required to edit the generated files, as hinted at earlier; if you do not change these files and do not add your own implementation of certain methods, there is no point in having a subclass at all.

Second, and this is a consequence of the first point, you do not regenerate the files over and over again after changes as you do with the other files. This is because you add changes to these files by hand, and if you overwrote the files, your changes would be lost.

As a result, you usually use uic only once per form with the options -subdecl and -subimpl.

If you later add a new slot with Qt Designer, you cannot easily use uic to add this slot to the subclass, because your changes would be overwritten. Of course, you can always copy your changed versions of the files to files with different names, let uic generate the new skeletons, and manually copy your changes back (e.g., in an editor), but this is usually more work and trouble than just adding the individual slots by hand.

But even after ruling out these steps from the ordinary build process, there are an awful lot of steps left. How you integrate these into your build process largely depends on how your build process is organized, which tools you use, etc. If you use handwritten makefiles (a solution we do not recommend) and your project is very small, you could just add the build steps by hand. For the aforementioned project, this could look like the following in Unix make syntax (Windows make syntax will differ slightly but not much):

all:
    myform

myform: myform.o moc_myform.o myformimpl.o moc_myformimpl.o
    c++ -o $@ $+ -L$(QTDIR)/lib -lqt

myform.o:	myform.cpp myform.h
    c++ -c $$< -I$(QTDIR)/include

myformimpl.o: myformimpl.cpp myformimpl.h myform.h
    c++ -c $$< -I$(QTDIR)/include

moc_myform.o: moc_myform.cpp myform.h
    c++ -c $$< -I$(QTDIR)/include

moc_myformimpl.o: moc_myformimpl.cpp myformimpl.h myform.h
    c++ -c $$< -I$(QTDIR)/include

moc_myform.cpp: myform.h
    moc -o $@ $<

moc_myformimpl.cpp: myformimpl.h
    moc -o $@ $<

myform.h: myform.ui
    uic -o $@ $<

myform.cpp: myform.ui myform.h
    uic -o $@ -impl myform.h $<

Note that there is no rule here for generating myformimpl.cpp and myformimpl.h. As mentioned earlier, you generate them once by hand from the command line and then edit them with your text editor without regenerating them.

Of course, the previous makefile with every relation explicitly spelled out does not hold for anything but the smallest project. You can achieve a little bit more generality by using suffix rules as follows:

# clear SUFFIXES list
.SUFFIXES:

# define SUFFIXES
.SUFFIXES: .h .cpp .ui

# create a .h file from a .ui file
.h.ui:; uic -o $@ $<

# create a .cpp file from a .ui file
.cpp.ui:; uic -o $@ -impl $*.h $<

But depending on your project, this might not be enough sophistication.

Using tmake For Generating And Building Qt Designer Files

We heartily recommend that you use a good system for automatically generating makefiles from a project description. There are several such systems available for both Unix and Windows, but for projects involving Qt and the Qt Designer, we suggest you give tmake a try.

tmake is free software that can be downloaded free of charge from ftp://ftp.trolltech.com/pub/freebies/tmake/. Just make sure that you get Version 1.5 or higher. On Unix systems, tmake requires Perl to be installed, which is usually the case these days.

If you do not know tmake yet, we suggest that you start reading the included documentation; tmake is really easy to use. We will only cover tmake's special features for use with Qt Designer here.

Actually, once you know how to use tmake for ordinary Qt projects, using it for projects that involve Qt Designer files is surprisingly easy. All you need to do is list your .ui files in the new INTERFACES tag in your tmake project file. For the aforementioned project, the following could be enough:

INTERFACES = myform.ui

When you then generate the makefile from the tmake project file, it will contain all the clauses mentioned earlier. The only thing you need to be sure of is that uic is in your path, just as moc and your C++ compiler are.

Again, this method does not do anything with generated and edited subclass files; as earlier, you just generate them by hand and then edit them as if they were ordinary source or header files (which in fact they are).