#pragma section-numbers on #acl WikiUserGroup:read,write,delete,revert All:read '''Index''' <> This page is targeted at those who want to develop Qt applications in the FreeSurfer code tree. === Usual Workflow of Qt Developers === If you are new to Qt programming, please start with the official documentation from [[http://doc.qt.nokia.com/latest/gettingstartedqt.html|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}}} ==== 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. === 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. {{attachment:qt-build-system.jpg}} Taken from [[http://developer.qt.nokia.com/quarterly/view/using_cmake_to_build_qt_projects|Qt Developer Site]] `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 }}} ==== 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 [[http://tsunanet.net/autotroll/|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`. ==== 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 }}} === Odds and Ends === * Out of all [[http://doc.qt.nokia.com/4.7/modules.html|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_.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`