Skip to content

A Qt6 project template with Conan, CI and custom Doxygen based on modern C++ project template using CMake

License

Notifications You must be signed in to change notification settings

LightTab2/qt-template

Repository files navigation

Actions Status Actions Status Actions Status GitHub release (latest by date)

[Project name]

[Project logo]

[Cool PNGs to attract people]

Doxygen example Doxygen example 2

[Project short info]

[Project usage example GIFs]

Install

Prerequisites

Install packages using Conan:

Run conanLibrariesInstall.sh or conanLibrariesInstall.bat, or simply execute these commands in the cloned repository's directory:

conan install conan/ --build=missing --settings=build_type=Debug
conan install conan/ --build=missing --settings=build_type=Release
Bash
cmake . -G [generator] -T [toolset] --build [PathToBuiltProject]

Example:

cmake . -G "Visual Studio 16 2019" -T v143 -Bbuild

GUI

The procedure is the standard one, but there are three things to be way of.

In-source builds are not allowed so these directories must differ:

CMake screenshot

You need to provide architecture and toolkit. If you leave them blank, project generation will likely fail. Also select option to specify the toolchain file:

CMake settings screenshot

If you did not tinker with Conan, the toolchain file should be found at conan/conan_toolchain.cmake.

CMake settings screenshot


Build the project

You can use your local IDE or CMake again:

cmake --build [pathToBuiltProject] --config [configuration] -j4 -DCMAKE_TOOLCHAIN_FILE=[pathToConanToolchainFile]

Example:

cmake --build build --config release -j4 -DCMAKE_TOOLCHAIN_FILE=conan/conan_toolchain.cmake

Features

[List of features]

Troubleshooting

Add a new library

Qt6 library

Simply modify cmake/Modules.cmake:

set(QT_COMPONENTS Core {Other Qt6 libraries you want})

Example:

set(QT_COMPONENTS Core Gui Widgets)

Other libraries

Step 1.

Modify conan/conanfile.txt:

[requires]
{Other libraries}
{New library's name from https://conan.io/center/}

Example:

[requires]
zlib/1.2.11
libcurl

Remember to run Conan after the changes:

conan install conan/ --build=missing --settings=build_type=Debug
conan install conan/ --build=missing --settings=build_type=Release

If the library cannot be found on ConanCenter, you could try going for Artifactory, but this requires some effort. You can always use plain CMake and modify CMakeLists.txt, ChatGPT might help with such quick fixes.

Step 2.

Now the library should be available but might not be linked to the CMake project.
Check if the library has components. Libraries with components are libraries like Qt6 or Boost, in which you can choose to use a few of their all features.

  • Header-only library

    Nothing needs to be done. Header-only libraries are already added in Conan toolchain file.


  • Library w/o components

    Modify cmake/Modules.cmake:

    set(MODULES {Libraries})

    Example:

    set(MODULES ZLIB libcurl)

    That's it!


  • Library with components This is quite some work :<

    Add to cmake/Modules.cmake:

    set({NEW_VARIABLE_WITH_COMPONENTS} {COMPONENTS})

    Example:

    set(BOOST_COMPONENTS filesystem)

    Modify CMakeLists.txt and after line 7: include(cmake/Modules.cmake) add following code:

    foreach(library IN LISTS {NEW_VARIABLE_WITH_COMPONENTS})
        find_package({LIBRARY_NAME} COMPONENTS ${library} REQUIRED)
    endforeach()

    Example:

    foreach(library IN LISTS BOOST_COMPONENTS)
        find_package(Boost COMPONENTS ${library} REQUIRED)
    endforeach()

    In the same file, after line 135: # Link libraries add following code:

    foreach(library IN LISTS {NEW_VARIABLE_WITH_COMPONENTS})
        target_link_libraries(${PROJECT_NAME} PRIVATE {LIBRARY_NAME}::${library})
        if(${PROJECT_NAME}_BUILD_EXECUTABLE)
            target_link_libraries(${PROJECT_NAME}_LIB PRIVATE {LIBRARY_NAME}::${library})
        endif()
    endforeach()

    Example:

    foreach(library IN LISTS BOOST_COMPONENTS)
        target_link_libraries(${PROJECT_NAME} PRIVATE Boost::${library})
        if(${PROJECT_NAME}_BUILD_EXECUTABLE)
            target_link_libraries(${PROJECT_NAME}_LIB PRIVATE Boost::${library})
        endif()
    endforeach()

    You should also link the library to the test projects. To do that, modify test/CMakeLists.txt and after line 53: # Link libraries add following code:

        foreach(library IN LISTS {NEW_VARIABLE_WITH_COMPONENTS})
            target_link_libraries(${test_name}_Tests PRIVATE {LIBRARY_NAME}::${library})
        endforeach()

    Example:

        foreach(library IN LISTS BOOST_COMPONENTS)
            target_link_libraries(${test_name}_Tests PRIVATE Boost::${library})
        endforeach()


Change the project's name

To change the project's name, you must correct a few entries:

  • CMakeLists.txt By default, the file starts with:
    cmake_minimum_required(VERSION 3.21)
    
    project("qt-template"
            LANGUAGES CXX)

    Change "qt-template" to the name of your project.

    Example:

    cmake_minimum_required(VERSION 3.21)
    
    project("myproject"
            LANGUAGES CXX)

    If you host your project on a GitHub repository and wish to use GitHub Actions for automatic deployment, you must provide a name that matches the repository name. It has to be lowercase. Otherwise, you need to change ${{ steps.repoName.outputs.name }} to your project's executable/library name (it is the CMake project name, unless you tinkered with CMakeLists.txt) in these files:

    • .github/workflows/macos.yml
    • .github/workflows/ubuntu.yml
    • .github/workflows/windows.yml

    If the name contains whitespace characters, you will need to enclose the entire entry in either " or '.
    Example:

    files: build/install/${{ steps.repoName.outputs.name }}_macOS_${{ steps.versionTag.outputs.tag }}.tar.gz

    Becomes:

    files: "build/install/Parrots and Cats_macOS_${{ steps.versionTag.outputs.tag }}.tar.gz"

  • config.desktop

    The Exec option should contain the project's executable/library name (it is CMake project name, unless you tinkered with CMakeLists.txt), while the Name is up to your choice.

    Change these entries:

    Name=Qt Template
    Exec=qt-template

    Example:

    Name=Parrots That Sing
    Exec=birds-and-stuff


Setup a Qt Quick project

Adding the "Quick" module to cmake/Modules.cmake will activate additional CMake steps for QML processing:

set(QT_COMPONENTS Core [...] Quick)

The .qml files should be placed in src/qml (creating subdirectories is possible) and they can be accessed from qrc:/qt/qml/{project_name}/{relativePathToFile}.

Example for a file src/qml/birds/Bird1.qml and project name parrot_songs:
qrc:/qt/qml/parrot_songs/birds/Bird1.qml.

There is a commented snippet in src/main.cpp with int main(int argc, char* argv[]) definition for Qt Quick, which contains example of accessing a QML file.


Qt6 is not found, despite being installed

Ensure that these environment variables are set properly:

  • Qt6_DIR - [path_to_Qt]/[version]/[compiler]/lib/cmake/Qt6
    Example: C:/Qt/6.5.1/msvc2019_64/lib/cmake/Qt6

  • Qt6GuiTools_DIR - [path_to_Qt]/[version]/[compiler]/lib/cmake/Qt6GuiTools
    Example: /usr/lib/x86_64-linux-gnu/6.5.1/clang_64/lib/cmake/Qt6GuiTools

  • Qt6CoreTools_DIR - [path_to_Qt]/[version]/[compiler]/lib/cmake/Qt6CoreTools
    Example: D:/Qt/6.3/msvc2019_64/lib/cmake/Qt6CoreTools


Missing or wrong architecture libraries | Conan profile errors

Ensure conan/conanfile.txt has listed all the needed libraries under [requires] section. Run:

conan install conan/ --build=missing --settings=build_type=Debug
conan install conan/ --build=missing --settings=build_type=Release

In case of a wrong architecture of the libraries and other possible profile errors, read: https://docs.conan.io/2.0/reference/config_files/profiles.html
If you don't have a profile, create one:

conan profile new default --detect

Change the icon of the project

Put your icon image in PNG format into a folder icon/ and rename it, so it matches this convention:

icon_[width]x[height].png

Example:

icon_256x256.png

The resolution should be one of these:

  • 16x16
  • 32x32
  • 48x48
  • 64x64
  • 128x128
  • 256x256

Further below, I will mention some scripts that use ImageMagick, so you need to install it, if you want to use them. On Ubuntu, it can be done by:

sudo apt install imagemagick

Beware that depending on your OS version, you can get either ImageMagick 6 or ImageMagick 7. Unix scripts contain [script]_ImageMagick7.sh versions, in case you did not obtain ImageMagick 6, but ImageMagick 7.

You can provide an icon with any resolution, and it will be rescaled to the other valid resolutions, if you use the script:

  • Windows/icon/WinScripts/rescale.bat
  • Unix/icon/UnixScripts/rescale.sh or /icon/UnixScripts/rescale_ImageMagick7.sh

If there are multiple icons with different resolutions, the highest resolution will be used to create other valid icons. They will overwrite any already existing ones! If you want to use different icons for different resolutions, provide them manually and do not use the script.

This is sufficient for Linux, but there are two other scripts, so the Windows and macOS applications will have icons too.

To generate an icon for Windows, use:

  • Windows/icon/WinScripts/createIco.bat
  • Unix/icon/UnixScripts/createIco.sh or /icon/UnixScripts/createIco_ImageMagick7.sh

macOS icon is slightly tricky to create on Windows, as we do not have a ready script. I recommend using WSL or another form of virtualization (i.e.: VirtualBox or Docker) and running the Unix script /icon/UnixScripts/createIcns.sh.
If you are on macOS, you can do this the native way, using iconutil. Otherwise, run /icon/UnixScripts/createIcns.sh, which requires png2icns library. On Ubuntu you can install it by:

sudo apt install icnsutils

Docs shouldn't contain private members and source code

If your project is a library, you might not want to add the private and protected members to your documentation. Editing one line in .github/workflows/doxygen.yml can change this behaviour. Find this step:

- name: Generate documents and deploy
    uses: DenverCoder1/doxygen-github-pages-action@v1.3.0
    with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        branch: docs
        config_file: doxygen/Doxyfile_dev

And change it to:

- name: Generate documents and deploy
    uses: DenverCoder1/doxygen-github-pages-action@v1.3.0
    with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        branch: docs
        config_file: doxygen/Doxyfile

If you want to further customize output and its display, all files related to documentation are stored in /doxygen folder.


"/bin/bash^M: bad interpreter: No such file or directory"

If you downloaded the project on Windows and try to run these scripts in Unix, you might encounter:
-bash: ./createIcns.sh: /bin/bash^M: bad interpreter: No such file or directory

Run icon/WinScripts/fixUnixScripts.ps1
Or some tool like dos2unix. On Ubuntu you can install it by:

sudo apt install dos2unix

And then simply run (assuming you are at qt-template/):

dos2unix ./icon/UnixScripts/*.sh

Add/change/remove custom filter

At line 174 of CMakeLists.txt you can modify following code for filters to change or remove them:

The code
 # For resource files 
source_group("Resources" FILES 
             "${CMAKE_BINARY_DIR}/.rcc/resources.qrc"
             "${CMAKE_BINARY_DIR}/.rcc/qrc_resources.cpp")
file(GLOB_RECURSE SOURCES "*.rc" "*.qrc")
foreach(SOURCE ${SOURCES})
    source_group("Resources" FILES ${SOURCE})
endforeach()
  # Resources defined in cmake/Resources.cmake
foreach(SOURCE ${RESOURCES})
    source_group("Resources" FILES ${SOURCE})
endforeach()

 # For source code
file(GLOB_RECURSE SOURCES "src/*.cpp" "src/*.h" "src/*.ui")
foreach(SOURCE ${SOURCES})
    get_filename_component(SOURCE_DIR "${SOURCE}" DIRECTORY)
    file(RELATIVE_PATH SOURCE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" ${SOURCE_DIR})
    string(REPLACE "src" "Source Files" SOURCE_PATH "${SOURCE_PATH}")
    source_group("${SOURCE_PATH}" FILES ${SOURCE})
endforeach()

 # For autogenerated by Qt ui_[name].h files for every src/*.ui file
file(GLOB_RECURSE SOURCES "src/*.ui")
foreach(SOURCE ${SOURCES})
    get_filename_component(FILE_NAME ${SOURCE} NAME_WE) # Extract file name without extension
    file(RELATIVE_PATH SOURCE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" ${SOURCE})
    string(REPLACE "${FILE_NAME}.ui" "" FILE_PATH "${SOURCE_PATH}" )
    source_group("Autogen/ui/${FILE_NAME}" FILES 
                 "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/include_Debug/${FILE_PATH}ui_${FILE_NAME}.h" 
                 "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/include_MinSizeRel/${FILE_PATH}ui_${FILE_NAME}.h"
                 "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/include_Release/${FILE_PATH}ui_${FILE_NAME}.h" 
                 "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/include_RelWithDebInfo/${FILE_PATH}ui_${FILE_NAME}.h")
endforeach()

 # For autogenerated by Qt stamp files
source_group("Autogen/autouic" FILES
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_LIB_autogen/autouic_Debug.stamp"
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_LIB_autogen/autouic_MinSizeRel.stamp" 
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_LIB_autogen/autouic_Release.stamp" 
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_LIB_autogen/autouic_RelWithDebInfo.stamp" )

 # For autogenerated by Qt stamp files
source_group("Autogen/autouic" FILES
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/autouic_Debug.stamp"
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/autouic_MinSizeRel.stamp" 
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/autouic_Release.stamp" 
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/autouic_RelWithDebInfo.stamp" )

 # For autogenerated by Qt MOC files
source_group("Autogen/moc" FILES 
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/mocs_compilation_Debug.cpp"
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/mocs_compilation_MinSizeRel.cpp"
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/mocs_compilation_Release.cpp"
             "${CMAKE_BINARY_DIR}/${PROJECT_NAME}_autogen/mocs_compilation_RelWithDebInfo.cpp")

source_group("CMake Rules" FILES "CMakeLists.txt")

You can add your own filters after line 232 in CMakeLists.txt, by adding:

source_group("[FILTER_NAME]" FILES "[FILE_PATHS]")

Example:

source_group("birds" FILES "src/flamingo.ui" "src/Birds/crow.h")

Contributing

This project follows these C++ Core Guidelines, and it would be fun if you followed them too. If you don't, someone will correct your code. An ugly contribution is better than no contribution. Thanks!

License

This project is licensed under the CC0 1.0 Universal; see the LICENSE file for details. It also uses the Qt library and possibly some of its additional modules that are licensed under the LGPL, but none of its code is present in this repository. Also note that Qt itself uses other third-party libraries under different license terms.