通过CMake实现Qt项目

44

我正在尝试通过Cmake构建和运行Qt的非常简单和基本的示例,删除了.pro文件。 以下是Qt项目的代码(Qt项目的目录结构是自动生成的):

Cmake (my project name)
├── headers
│   └── mainwindow.h
├── sources
│   ├── main.cpp
│   └── mainwindow.cpp
└── forms
    └── mainwindow.ui

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

主程序.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

这是我的 CMakeLists.txt 文件。

project(Cmake)

find_package(Qt5Widgets)

set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

QT5_WRAP_CPP(Cmake_hdr_moc mainwindow.h)
QT5_WRAP_UI(Cmake_form_hdr mainwindow.ui)

add_library(mainwindow ${Cmake_hdr_moc} ${Cmake_form_hdr})
qt5_use_modules(mainwindow Widgets)

add_executable(Cmake main.cpp mainwindow)
qt5_use_modules(Cmake Core Gui Widgets)

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget"/>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>400</width>
     <height>29</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

当我构建项目并运行CMake时,它指向文件mainwindow.h,表示'ui_mainwindow.h'不存在。


你的 *.ui 文件在哪里? - Gluttton
1
我已经使用.ui文件编辑了这篇文章。到目前为止,我还没有对该文件进行任何更改,它仍然是自动生成的文件。 - user3877872
你是在IDE中还是在文件系统中展示你的项目结构? - Gluttton
我展示的结构是在IDE中的,在文件系统中它只是这样的:http://imgur.com/g3LJy6I - user3877872
3个回答

70
你的脚本有几个错误,还有一些可以改进的地方。在更改后,它将如下所示:链接
cmake_minimum_required(VERSION 3.0.2)
project(MyProject)

find_package(Qt5Widgets)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

add_library(mainwindow mainwindow.cpp)
target_link_libraries (mainwindow Qt5::Widgets)

add_executable(MyProject main.cpp)
target_link_libraries (MyProject mainwindow)

错误:

  1. Wrong add_executable directive. You try to add a library, but for this purpose you need to use target_link_libraries. So instead of:

    add_executable(Cmake main.cpp mainwindow)
    

    You need:

    add_executable(Cmake main.cpp)
    target_link_libraries (Cmake mainwindow)
    
  2. And one more mistake is missing *.cpp files in the add_library directive:

    add_library(mainwindow mainwindow.cpp ${Cmake_hdr_moc} ${Cmake_form_hdr})

建议:

  1. Also setting version of CMake would be appropriate. If you use CMAKE_AUTOMOC you need a version not less than 2.8.6, and if you use CMAKE_AUTOUIC you need a version not less than 3.0.2:

    cmake_minimum_required(VERSION 3.0.2)
    
  2. Using qt5_wrap_cpp with CMAKE_AUTOMOC isn't necessary.

  3. When you use CMAKE_AUTOMOC usage CMAKE_AUTOUIC instead of qt5_wrap_ui will be more appropriate.

  4. This script is correct for the project with the following structure in the file system:

    Project
    ├── CMakeLists.txt
    ├── main.cpp
    ├── mainwindow.cpp
    ├── mainwindow.h
    └── mainwindow.ui
    

    If you have another structure you should use include_directories as was mentioned by @steveire.

  5. (UPD) Since, I've written this answer, I suggested it several times for beginners who try to meet with Qt through CMake. They complain of an inappropriate name of the project - "Cmake". For beginners who just meet with CMake is difficult to realize where cmake - is just part of the project name (and isn't mandatory) and where cmake is part of a directive (and is mandatory). So I'd like to replace the name of the project from "Cmake" to "MyProject". This reduces connection between question and answer, but on the other hand this makes the answer more friendly for beginners.

  6. (UPD) As was mentioned by @Erik Sjölund qt5_use_modules is obsolete and target_link_libraries should be used instead.

注意: 就我个人而言,我使用CMAKE_AUTOMOC的经验并不成功;它对于结构简单的简单项目很好。但是,当我的包含文件存储在单独的目录中时,我遇到了问题:

.
├── include
│   └── QtClass.h
└── src
    └── QtClass.cpp

当不同的子目录中存在具有相同名称的文件时:

.
├── NamespaceA
│   ├── QtClass.cpp
│   └── QtClass.h
└── NamespaceB
    ├── QtClass.cpp
    └── QtClass.h

最后: 这是基于我的个人意见提出的建议,我想提出一个更明确的版本的脚本,不使用CMAKE_AUTOMOCCMAKE_AUTOUIC,它更冗长但另一方面你有更多的控制权:

cmake_minimum_required (VERSION 2.8.12)
project (MyProject)

find_package (Qt5Widgets)

set (MyProjectLib_src ${PROJECT_SOURCE_DIR}/mainwindow.cpp)
set (MyProjectLib_hdr ${PROJECT_SOURCE_DIR}/mainwindow.h)
set (MyProjectLib_ui  ${PROJECT_SOURCE_DIR}/mainwindow.ui)
set (MyProjectBin_src ${PROJECT_SOURCE_DIR}/main.cpp)

qt5_wrap_cpp(MyProjectLib_hdr_moc ${MyProjectLib_hdr})
qt5_wrap_ui (MyProjectLib_ui_moc  ${MyProjectLib_ui})

include_directories (${PROJECT_SOURCE_DIR})
include_directories (${PROJECT_BINARY_DIR})

add_library (MyProjectLib SHARED 
    ${MyProjectLib_src}
    ${MyProjectLib_hdr_moc}
    ${MyProjectLib_ui_moc}
)
target_link_libraries (MyProjectLib Qt5::Widgets)

add_executable(MyProject ${MyProjectBin_src})
target_link_libraries (MyProject MyProjectLib)

完整版本的项目源代码可在GitLab上获取。

是的,它奏效了,我确切地使用了上述脚本,找出了差异,我想问一下 include_directories (${PROJECT_BINARY_DIR}) 是什么意思?它是做什么的?我也删除了 add_library。 - user3877872
include_directories (${PROJECT_BINARY_DIR}) 告诉 CMake 在构建目录中查找包含文件。所有生成的文件都放置在构建目录中。 - Gluttton
@Gluttton:传奇!这是那种能真正加速CMake和Qt进展的回应 :-) - ssc
1
@Gluttton automoc对于小项目来说很好,但是当项目规模增大时,我们不得不从各个地方剔除它,因为它会使速度变得非常慢。最好显式调用qt5_wrap_cpp。 - peter karasev
1
@peterkarasev,感谢您分享您的经验! - Gluttton
显示剩余3条评论

0

撰写本文时,这是Qt应用程序的推荐cmake用法。只有几个需要注意的地方:

  1. 使用automoc。这将极大地减少维护成本。它的速度对于大型项目也足够快,因此您无需担心构建时间减慢。

  2. 使用无版本cmake目标,以便它们在Qt版本之间兼容。

  3. 由于主窗口是应用程序的一部分,因此不需要为其创建库。

  4. 如果可以,请利用QT_DISABLE_DEPRECATED_BEFORE变量。

  5. 作为奖励,如果您的目标是高质量的话,启用一些常见的警告、错误和卫生器检测。

    project(Application VERSION 1.0.0 LANGUAGES CXX)                                   
    
    set(CMAKE_CXX_STANDARD 23)                                                      
    set(CMAKE_CXX_STANDARD_REQUIRED ON)                                             
    
    set(CMAKE_AUTOMOC ON)                                                           
    set(CMAKE_AUTORCC ON)                                                           
    set(CMAKE_AUTOUIC ON)                                                           
    
    find_package(Qt6 COMPONENTS Widgets)                  
    if (NOT Qt6_FOUND)                                                              
      find_package(Qt5 5.15 REQUIRED COMPONENTS Widgets)                
    endif()
    
    if (CMAKE_CXX_COMPILER_ID MATCHES "(Clang|GNU)")                              
      add_compile_options(-Wall -Wpedantic -Wextra -Werror)                       
      add_compile_options(-fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer)
      add_link_options(-fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer)
    elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")                                 
      set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "/w")                
    endif()                                                                       
    
    add_compile_definitions(QT_DISABLE_DEPRECATED_BEFORE=0xFFFFFF)
    
    add_executable(Application                                                                                                                     
      mainwindow.cpp                                                                
      main.cpp                                                                                                                              
    )                                                                               
    
    target_link_libraries(Application Qt::Widgets)
    
    

0

你没有告诉我们CMakeLists.txt在目录结构中的位置。如果它在顶层,那么你应该有

add_executable(Cmake sources/main.cpp sources/mainwindow.cpp)

你需要

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/headers)

无论如何,找出 ui_*.h 文件生成的位置,并使用 include_directories 添加该目录。

当我使用Cmake构建我的项目时,它显示的目录结构为:http://imgur.com/nntQ7O5 - user3877872

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接