In Qt Cmake compilation mode, adding Q_OBJECT macro compilation error: ninja:build stopped:subcommand failed.

1. Let’s talk about the answer first
Q_OBJECT does not need to be compiled in the .cpp file. It needs to be compiled in .h.
2. I recently moved the C# project to Linux, so I started reading Qt. Even a small problem can cause it to get stuck for several days. Post the first qt test program:
This is used to combine Qt with the front end.
This resource is required:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtWebEngineWidgets/QtWebEngineWidgets>
#include <QtWebChannel/qwebchannel.h>

QT_BEGIN_NAMESPACE
namespace Ui {<!-- --> class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{<!-- -->
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    QWebEngineView* m_view;
    QWebChannel* m_channel;

private:
    Ui::MainWindow *ui;
};


class MyClassOne : public QObject
{<!-- -->
    //Q_OBJECT must be added
    Q_OBJECT
public slots:
    Q_INVOKABLE void add(){<!-- -->
        QMessageBox::information(NULL, "class1", "class1");
    }

    Q_INVOKABLE void showMsgBox(const QString & amp; message) {<!-- -->
        qDebug() << "Received message:" << message;
    }
};

class MyClassTwo : public QObject
{<!-- -->
    Q_OBJECT
public slots:
    Q_INVOKABLE void add(){<!-- -->
        QMessageBox::information(NULL, "class2", "class2");
    }
};

#endif // MAINWINDOW_H

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{<!-- -->
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

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

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

    m_view = new QWebEngineView(this);
    setCentralWidget(m_view);
    m_channel = new QWebChannel(this);
    MyClassOne *myclassone = new MyClassOne();
    MyClassTwo *myclasstwo = new MyClassTwo();
    m_channel->registerObject(QStringLiteral("myclassone"), (QObject*)myclassone);
    m_channel->registerObject(QStringLiteral("myclasstwo"), (QObject*)myclasstwo);

    m_view->page()->setWebChannel(m_channel);

    QString htmlPath = "D://Desktop/calculator_h5/calculator.html";
    m_view->page()->load(htmlPath);

    connect(m_view->page(), & amp;QWebEnginePage::loadFinished, this, [this]() {<!-- -->
        //Execute JavaScript code
        m_view->page()->runJavaScript("js_func()");
    });

// QMessageBox::information(nullptr, "title", "open the door");
}

MainWindow::~MainWindow()
{<!-- -->
    delete ui;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.5)

project(testQWebEngine VERSION 0.1 LANGUAGES CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
find_package(Qt${<!-- -->QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
find_package(Qt5 REQUIRED COMPONENTS WebEngineWidgets)
find_package(Qt5 REQUIRED COMPONENTS Core)
find_package(Qt5 REQUIRED COMPONENTS WebChannel)


set(PROJECT_SOURCES
        main.cpp
        mainwindow.cpp
        mainwindow.h
        mainwindow.ui
)


if(${<!-- -->QT_VERSION_MAJOR} GREATER_EQUAL 6)
    qt_add_executable(testQWebEngine
        MANUAL_FINALIZATION
        ${<!-- -->PROJECT_SOURCES}
    )
# Define target properties for Android with Qt 6 as:
# set_property(TARGET testQWebEngine APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${<!-- -->CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
    if(ANDROID)
        add_library(testQWebEngine SHARED
            ${<!-- -->PROJECT_SOURCES}
        )
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
    else()
        add_executable(testQWebEngine
            ${<!-- -->PROJECT_SOURCES}
        )
    endif()
endif()

target_link_libraries(testQWebEngine PRIVATE Qt${<!-- -->QT_VERSION_MAJOR}::Widgets)
target_link_libraries(testQWebEngine PRIVATE Qt5::WebEngineWidgets)
target_link_libraries(testQWebEngine PRIVATE Qt5::Core)

# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${<!-- -->QT_VERSION} VERSION_LESS 6.1.0)
  set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.testQWebEngine)
endif()
set_target_properties(testQWebEngine PROPERTIES
    ${<!-- -->BUNDLE_ID_OPTION}
    MACOSX_BUNDLE_BUNDLE_VERSION ${<!-- -->PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${<!-- -->PROJECT_VERSION_MAJOR}.${<!-- -->PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

include(GNUInstallDirs)
install(TARGETS testQWebEngine
    BUNDLE DESTINATION.
    LIBRARY DESTINATION ${<!-- -->CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${<!-- -->CMAKE_INSTALL_BINDIR}
)

if(QT_VERSION_MAJOR EQUAL 6)
    qt_finalize_executable(testQWebEngine)
endif()

html

<!DOCTYPE html>
<h1>Hello, world!</h1>
<head>
  <title></title>
</head>
<body>
<div>
<input value="class1" type="button" onclick="button_1();" />
<input value="class2" type="button" onclick="button_2();" />
</div>
\t
  <script src="./qwebchannel.js"></script>
  <script type="text/javascript">
    var myclass;
var showMsgBox;
//Perform initialization after the page is loaded
document.addEventListener("DOMContentLoaded", function() {<!-- -->
new QWebChannel(qt.webChannelTransport, function(channel) {<!-- -->
one = channel.objects.myclassone;
two = channel.objects.myclasstwo;
});
});
    
    function button_1() {<!-- -->
one.add();
}
\t
function button_2() {<!-- -->
two.add();
}

function js_func(){<!-- -->
alert('This is a pop-up message');
}
  </script>
</body>
</html>

js is in the directory where qt is installed, mine is here D:\Qt\Examples\Qt-5.15.2\webchannel\shared

It’s so troublesome to write a test example on the Internet, why not keep it simple? Haha, I wrote the simplest one.