Differences between revisions 8 and 9
Deletions are marked like this. Additions are marked like this.
Line 291: Line 291:

 * If the linker complains of "undefined symbols" even if you are linking everything correctly, it might be due to the lack of `moc_<foo>.cpp` file in the `BUILT_SOURCES` automake variable. Note that these `moc_` files are produced by the `qmake` automatically but since we are going the autotools route, they have to be explicitly specified in `Makefile.am`

Index

This page is targeted at those who want to develop Qt applications in the FreeSurfer code tree.

1. Usual Workflow of Qt Developers

If you are new to Qt programming, please start with the official documentation from Qt site.

  1. (optionally) design the user interface using Qt Designer. This generates .ui files

  2. (optionally) write the Qt Resource file which is an XML specification of the binary files needed by the application. This has .qrc extension.

  3. Code the .cpp and .h files
  4. Generate a Qt project file with qmake -project command line. This has .pro extension.

  5. Generate Makefile using qmake command line

  6. Make using make

1.1. Workflow in FreeSurfer Tree

Unfortunately, at this moment, this workflow is not possible in FreeSurfer because there is no straightforward way of transferring configuration details from Autotools to Qt Project files. The build system follows the Autotools way where one writes Makefile.am file which configure script reads and generates Makefile. So, the first 4 steps of the workflow is the same and these are the subsequent steps

  • (step 5) Write Makefile.am using the generated .pro file and instructions in this page.

  • (step 6) Execute make and make install

In fact, Step 4 ( generating .pro file ) is optional if one is comfortable with writing Makefile.am from the start. This is outlined below.

2. Writing Makefile.am for a Qt Application

This is not much different from the .pro file and freeview/freeview.pro is used as a reference file. The following diagram gives a graphical overview of the Qt build system.

Makefile.am should account for the intermediate files produced. That's probably the only additional thing one would write compared to .pro file. The following is a skeleton Makefile.am of freeview which you can copy to your Qt binary directory and make appropriate changes.

if ENABLE_QT_APPS

include $(top_srcdir)/build-aux/autotroll.mk

bin_PROGRAMS = freeview

BUILT_SOURCES = ui_MainWindow.h \
        .....
        moc_BrushProperty.cpp \
        .....
        qrc_freeview.cpp 

freeview_SOURCES = $(BUILT_SOURCES) \
    Annotation2D.cpp \
    .....
    QVTKWidget.cxx \
    QVTKPaintEngine.cxx \
    BrushProperty.h \
    .....

if HAVE_MAC_OSX
AM_CXXFLAGS=\
        -fno-strict-aliasing \
        -Wno-deprecated \
        -Wno-unused \
        -Wno-uninitialized \
        -Wno-return-type \
        -Wno-reorder \
        -Wno-sign-compare \
        -I$(top_srcdir)/include \
        $(GL_CFLAGS) \
        $(VTK_COCOA_CXXFLAGS) \
        -I$(top_srcdir)/vtkfsio \
        -I$(top_srcdir)/vtkutils \
        $(ITK_CFLAGS) -DHAVE_ITK_LIBS \
        $(VXL_CFLAGS)


freeview_CXXFLAGS = $(QT_CXXFLAGS) $(AM_CXXFLAGS)
freeview_CPPFLAGS = $(QT_CPPFLAGS) $(AM_CPPFLAGS)
freeview_LDFLAGS  = $(QT_LDFLAGS) $(OS_LDFLAGS) -framework Cocoa -framework IOKit
freeview_LDADD    = $(QT_LIBS)  \
        $(addprefix $(top_builddir)/, $(LIBS_MGH)) \
        $(top_builddir)/vtkfsio/libvtkfsio.a \
        $(top_builddir)/vtkutils/libvtkutils.a \
        $(VTK_COCOA_LIBS) $(VTK_VERDICT_LIB) -lvtkGraphics \
        $(VTK_EXPAT_LIB) $(VTK_FREETYPE_LIB) \
        $(VTK_TIFF_LIB) $(VTK_JPEG_LIB) \
        $(VTK_METAIO_LIB) $(VTK_PNG_LIB) $(VTK_Z_LIB) \
        $(VTK_SQLITE_LIB) \
        -lvtkImaging -lvtkFiltering \
        -lvtkCommon -lvtksys -lvtkGenericFiltering \
        -lvtkexoIIc -lvtkNetCDF \
        -lvtkVolumeRendering -lvtkRendering -lvtkftgl \
        -lvtkWidgets -lvtkHybrid \
        -lvtkIO -lvtkDICOMParser $(VTK_MPEG2ENCODE_LIB) 
   

# need to create a bundle for Macs
install-exec-hook:freeview
        mkdir -p $(DESTDIR)$(bindir)/Freeview.app
        mkdir -p $(DESTDIR)$(bindir)/Freeview.app/Contents
        mkdir -p $(DESTDIR)$(bindir)/Freeview.app/Contents/MacOS
        mkdir -p $(DESTDIR)$(bindir)/Freeview.app/Contents/Resources
        mkdir -p $(DESTDIR)$(bindir)/Freeview.app/Contents/Resources/English.lproj
#       mkdir -p $(DESTDIR)$(bindir)/Freeview.app/Contents/Frameworks
        cp $(top_builddir)/freeview/Info.plist $(DESTDIR)$(bindir)/Freeview.app/Contents
        echo -n 'APPL????' > $(DESTDIR)$(bindir)/Freeview.app/Contents/PkgInfo
        cp $(top_builddir)/freeview/freeview $(DESTDIR)$(bindir)/Freeview.app/Contents/MacOS/freeview.bin
        echo "#!/bin/tcsh -ef" > $(DESTDIR)$(bindir)/freeview
#       echo "source \$$FREESURFER_HOME/bin/tcl_setup" \
                >> $(DESTDIR)$(bindir)/freeview
        echo "source \$$FREESURFER_HOME/bin/qt_setup" \
                >> $(DESTDIR)$(bindir)/freeview
        echo "exec $(DESTDIR)$(bindir)/Freeview.app/Contents/MacOS/freeview.bin \$$*" >> $(DESTDIR)$(bindir)/freeview
        chmod a+x $(DESTDIR)$(bindir)/freeview
#       ./mac_package_dylibs.sh $(WXWIDGETS_DIR)lib $(DESTDIR)$(bindir)/Freeview.app/Contents/Frameworks $(DESTDIR)$(bindir)/Freeview.app/Contents/Mac
OS/freeview.bin
        if [[ -f /usr/pubsw/bin/chrpath ]]; then \
        /usr/pubsw/bin/chrpath -d $(DESTDIR)$(bindir)/Freeview.app/Contents/MacOS/freeview.bin; fi

uninstall-hook:
        rm -f $(DESTDIR)$(bindir)/freeview
        rm -rf $(DESTDIR)$(bindir)/Freeview.app

else

AM_CXXFLAGS=\
        -fno-strict-aliasing \
        -Wno-deprecated \
        -Wno-unused \
        -Wno-uninitialized \
        -Wno-return-type \
        -Wno-reorder \
        -Wno-sign-compare \
        -I$(top_srcdir)/include \
        $(GL_CFLAGS) \
        $(VTK_CXXFLAGS) \
        -I$(top_srcdir)/vtkfsio \
        -I$(top_srcdir)/vtkutils \
        $(ITK_CFLAGS) -DHAVE_ITK_LIBS \
        $(VXL_CFLAGS)


freeview_CXXFLAGS = $(QT_CXXFLAGS) $(AM_CXXFLAGS)
freeview_CPPFLAGS = $(QT_CPPFLAGS) $(AM_CPPFLAGS)
freeview_LDFLAGS  = $(QT_LDFLAGS) $(OS_LDFLAGS) 
freeview_LDADD    = $(QT_LIBS)  \
        $(addprefix $(top_builddir)/, $(LIBS_MGH)) \
        $(top_builddir)/vtkfsio/libvtkfsio.a \
        $(top_builddir)/vtkutils/libvtkutils.a \
        $(VTK_LIBS) $(VTK_VERDICT_LIB) -lvtkGraphics \
        $(VTK_EXPAT_LIB) $(VTK_FREETYPE_LIB) \
        $(VTK_TIFF_LIB) $(VTK_JPEG_LIB) \
        $(VTK_METAIO_LIB) $(VTK_PNG_LIB) $(VTK_Z_LIB) \
        $(VTK_SQLITE_LIB) \
        -lvtkImaging -lvtkFiltering \
        -lvtkCommon -lvtksys -lvtkGenericFiltering \
        -lvtkexoIIc -lvtkNetCDF \
        -lvtkVolumeRendering -lvtkRendering -lvtkftgl \
        -lvtkWidgets -lvtkHybrid \
        -lvtkIO -lvtkDICOMParser $(VTK_MPEG2ENCODE_LIB) 

# put a wrapper around the bin, used to setup tcltktixblt,vtk,kww enviro vars
# and if the OS is MACOSX have a different install-hook to create a bundle
install-exec-hook:freeview
        cp $(top_builddir)/freeview/freeview $(DESTDIR)$(bindir)/freeview.bin
        echo "#!/bin/tcsh -ef" > $(DESTDIR)$(bindir)/freeview
        echo "source \$$FREESURFER_HOME/bin/tcl_setup" \
                >> $(DESTDIR)$(bindir)/freeview
        echo "source \$$FREESURFER_HOME/bin/vtk_setup" \
                >> $(DESTDIR)$(bindir)/freeview
        echo "source \$$FREESURFER_HOME/bin/qt_setup" \
                >> $(DESTDIR)$(bindir)/freeview
        echo "freeview.bin \$$argv" >> $(DESTDIR)$(bindir)/freeview
        chmod a+x $(DESTDIR)$(bindir)/freeview
        if [[ -f /usr/pubsw/bin/chrpath ]]; then \
        /usr/pubsw/bin/chrpath -d $(DESTDIR)$(bindir)/freeview.bin; fi

uninstall-hook:
        rm -f $(DESTDIR)$(bindir)/freeview.bin
endif
....
endif

2.1. Explanation

if ENABLE_QT_APPS
include $(top_srcdir)/build-aux/autotroll.mk

if ENABLE_QT_APPS is a necessary guard that ensures that configure script continues if it can't find Qt.

The excellent Autotroll is made use of to seamlessly use Autotools to build Qt applications. In summary, it does the hardwork of populating relevant variables by calling qmake on a sample Qt project.


bin_PROGRAMS = freeview

This is the name of your binary. freeview in this case.


BUILT_SOURCES = ui_MainWindow.h \
        .....
        moc_BrushProperty.cpp \
        .....
        qrc_freeview.cpp 

BUILT_SOURCES variable takes care of the accounting of intermediate files produced in the above diagram. As you can notice, all of moc_, ui_ and qrc_ files are accounted. For example, if your source file has MainWindow.ui file generated by the Qt Designer, you'll include it as ui_MainWindow.h in BUILT_SOURCES variable in your Makefile.am


freeview_SOURCES = $(BUILT_SOURCES) \
    Annotation2D.cpp \
    .....
    QVTKWidget.cxx \
    QVTKPaintEngine.cxx \
    BrushProperty.h \
    .....

All your source files .cpp and .h files go here along with the BUILT_SOURCES variable.


freeview_CXXFLAGS = $(QT_CXXFLAGS) $(AM_CXXFLAGS)
freeview_CPPFLAGS = $(QT_CPPFLAGS) $(AM_CPPFLAGS)
freeview_LDFLAGS  = $(QT_LDFLAGS) $(OS_LDFLAGS) 
freeview_LDADD    = $(QT_LIBS)  \
                    ....

install-exec-hook:freeview
        cp $(top_builddir)/freeview/freeview $(DESTDIR)$(bindir)/freeview.bin
        echo "#!/bin/tcsh -ef" > $(DESTDIR)$(bindir)/freeview
        echo "source \$$FREESURFER_HOME/bin/tcl_setup" \
                >> $(DESTDIR)$(bindir)/freeview
        echo "source \$$FREESURFER_HOME/bin/vtk_setup" \
                >> $(DESTDIR)$(bindir)/freeview
        echo "source \$$FREESURFER_HOME/bin/qt_setup" \
                >> $(DESTDIR)$(bindir)/freeview
        echo "freeview.bin \$$argv" >> $(DESTDIR)$(bindir)/freeview
        chmod a+x $(DESTDIR)$(bindir)/freeview
        if [[ -f /usr/pubsw/bin/chrpath ]]; then \
        /usr/pubsw/bin/chrpath -d $(DESTDIR)$(bindir)/freeview.bin; fi

This is if we detect Linux x86 or x86_64. Note that the Qt information is captured in the variables starting with QT_ variables -- QT_CXXFLAGS, QT_CPPFLAGS, QT_LDFLAGS and QT_LIBS. Also, we rename the binary to binary.bin and write a script named binary which calls binary.bin and sets up various shared library paths. Since freeview needs Qt, VTK and Tcl, we call tcl_setup, vtk_setup and qt_setup before invoking freeview.bin.

2.2. Special considerations for MacOSX


if HAVE_MAC_OSX
AM_CXXFLAGS=\
        ...
        $(VTK_COCOA_CXXFLAGS) \
        ...
freeview_CXXFLAGS = $(QT_CXXFLAGS) $(AM_CXXFLAGS)
freeview_CPPFLAGS = $(QT_CPPFLAGS) $(AM_CPPFLAGS)
freeview_LDFLAGS  = $(QT_LDFLAGS) $(OS_LDFLAGS) -framework Cocoa -framework IOKit
freeview_LDADD    = $(QT_LIBS)  \
        $(addprefix $(top_builddir)/, $(LIBS_MGH)) \
        $(top_builddir)/vtkfsio/libvtkfsio.a \
        $(top_builddir)/vtkutils/libvtkutils.a \
        $(VTK_COCOA_LIBS) $(VTK_VERDICT_LIB) -lvtkGraphics \
        $(VTK_EXPAT_LIB) $(VTK_FREETYPE_LIB) \
        $(VTK_TIFF_LIB) $(VTK_JPEG_LIB) \
        ....

We have a separate logic if we detect MacOSX. This is due to a few reasons: if the Qt application uses VTK, a separate Cocoa enabled static VTK library only seems to work with Qt. This separate VTK is detected at configure time and the information is there in VTK_COCOA_CXXFLAGS and VTK_COCOA_LIBS variables. Also, the binary_LDFLAGS need the -framework Cocoa -framework IOKit argument ( and possibly more, depending on the application). install-exec-hook takes care of packaging the Qt application as a Mac Application bundle.


There is a bug in 4.7.1 Cocoa version of Qt in which the items in the menu don't work. The workaround is to simply copy the following directory to the Contents/Resources of the new app.

        if [[ -d  /usr/pubsw/packages/qt/current/lib/qt_menu.nib ]]; then \
        cp -rv /usr/pubsw/packages/qt/current/lib/qt_menu.nib $(DESTDIR)$(bindir)/Freeview.app/Contents/Resources; fi

3. Odds and Ends

  • Out of all these possible modules in Qt, only core, gui and opengl are enabled in configure.in. If you want to include additional Qt modules for compilation, append to the AT_WITH_QT variable. For example, if you want the sql module, change AT_WITH_QT([opengl])  to AT_WITH_QT([opengl sql])  in dev/configure.in

  • Qt is installed in the path /usr/pubsw/packages/qt/current/ in the NMR center. There you can find directories like demos/, examples/ etc. which has more Qt examples. qmake is in /usr/pubsw/packages/qt/current/bin. Also, the MacOSX version is not installed as a framework ( --no-framework option ) but as a bunch of shared libraries.

  • If the linker complains of "undefined symbols" even if you are linking everything correctly, it might be due to the lack of moc_<foo>.cpp file in the BUILT_SOURCES automake variable. Note that these moc_ files are produced by the qmake automatically but since we are going the autotools route, they have to be explicitly specified in Makefile.am

DevelopersGuide/QtDevelopersGuide (last edited 2012-02-06 19:13:48 by KrishSubramaniam)