使用C++(Qt)调用STK并展示3D/2D控件(使用vscode+cmake构建)

上回书说到使用C#的winform技术实现了调用STK引擎并且展示其3D/2D控件(使用C#(winform)调用STK并展示其3D/2D控件-CSDN博客),回头想着把C++和Qt的也整一下,毕竟相对于winform而言,还是更熟悉Qt一些,于是找了些资料,重新回头看了看,同样实现了调用STK并展示3D/2D控件的功能。本文主要参考了QT调用STK12(STKX模块)_qt stk-CSDN博客一文。由于采用vscode开发并且使用cmake构建,踩了一些坑,也一并记录一下。

本文使用STK11.6,CMake>3.5,vs2022(作为CMake的生成器和编译器),Qt5.14.2(其中的msvc2017_64)。

vscode不多介绍了,这里主要介绍一下所用到的插件,主要有三个,分别是用来配置C++的一个和两个配置cmake的。配置cmake的分别是CMake和CMake Tools两个插件,前者提供基本的语法高亮与函数补全,后者是Microsoft官方出品的用于和CMake进行交互并触发构建、运行等功能的插件。

关于C++的插件这里有必要说一嘴,有两个常用的,一个是Microsoft官方出的C/C++,另一个是LLVM出的clangd,个人感觉前者在智能提示和语法解析的时候有点慢,于是一般用后者,但是在这里,由于STK是安装在Windows上的,所提供的组件也是以COM组件形式提供的,有大量特异于Windows平台的API被调用,因此CMake生成器和编译器部分必须选择vs(我这里用的vs2022),不能够是mingw或者clang之类的,而以vs作为生成器时,CMake不会生成compile_commands.json,因此clangd也无法提供语法提示,所以这里一定要选择Microsoft官方出的C/C++插件

安装完插件后,接下来规划项目结构,我的项目结构如下所示

其中

  • .vscode是vscode的配置文件夹
  • build文件夹不用管,是cmake构建和生成程序的文件夹
  • CppIncludes是STK官方所提供的C++二次开发库
  • src是源码目录
  • CMakeLists.txt是CMake配置文件

别着急,接下来我们一个个来仔细看。

.vscode文件夹中包含两个文件,settings.json和tasks.json,前者是当前工作区设置,后者是任务。在这里,我的settings.json的内容是

"cmake.configureEnvironment": {
    "CMAKE_PREFIX_PATH": "xxx/5.14.2/msvc2017_64/lib/cmake"
}

其中 xxx 是你安装Qt的路径,我这里Qt的版本用的是5.14.2,5.14.2最高带的msvc的版本就到2017,用的64位(因为STK也是安装的64位,之后生成程序也要生成64位的)。这一句设置主要是告诉cmake插件,在构建时传入一个CMAKE_PREFIX_PATH变量,其值为多少。这个变量帮助cmake在find_package时优先寻找某些目录,如果你的cmake找不到qt的cmake配置文件,可以试着加上这个设置。

tasks.json的内容是

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "run",
            "type": "shell",
            "command": "./build/Debug/qtstk.exe",
            "options": {
                "env": {
                    "path": "$env:path;xxx/5.14.2/msvc2017_64/bin;"
                }
            }
        }
    ]
}

这一段也是可有可无,他主要是为了在生成程序之后运行,由于cmake自带的运行(launch)在我写文章的此时还不支持自动加入环境变量,我又不愿意将qt的dll库路径加入全局环境变量污染,因此每次只能手动加入,写个task帮我自动加。这里我的程序的名称是qtstk.exe,其路径在build文件夹下的Debug文件夹下,这和一般用mingw生成的位置不太一样。xxx是你安装qt的路径。

CppInlcudes文件夹要到 你的STK安装路径/CodeSamples/ 这个路径下,有个压缩包,也叫CodeSamples,解压缩进入后,找到CommonFiles文件夹,里面就有这个CppInlcudes文件夹,统一一把复制到项目目录下面,这个后面有大用。

最后再讲src源码,先来讲讲CMakeLists.txt怎么写,我的CMakeLists.txt内容如下

cmake_minimum_required(VERSION 3.5)
project(qtstk CXX)

if(MSVC_IDE)
    add_compile_options(/utf-8)
endif()

find_package(Qt5 COMPONENTS
Core
Widgets
AxContainer
REQUIRED
)

file(GLOB sources ${CMAKE_SOURCE_DIR}/src/*)
add_executable(${PROJECT_NAME} ${sources})

target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/CppIncludes/
)

target_link_libraries(${PROJECT_NAME} PRIVATE
Qt5::Widgets
Qt5::AxContainer
)

set_target_properties(${PROJECT_NAME} PROPERTIES
AUTOMOC ON
AUTORCC ON
AUTOUIC ON
)

不得不佩服cmake的自动化性质,全篇没有一个绝对路径,而将绝对路径的配置都可以排除到文件之外,提供了极大的自动化能力和跨平台兼容性。

项目的名称叫做qtstk,也使用该名称作为生成的可执行目标的名称。

find_package找寻qt库,并且要求包含Core、Widgets和AxContainer(大小写注意)三个组件,其中AxContainer主要是容纳ActiveX控件的,因为STK的3D/2D控件是以这种形式提供的。

注意target_include_directories为生成目标添加include path,将之前STK提供的文件夹加入进去了。

后面就都是些稀松平常的内容了,有使用cmake构建编译qt应用的经验应该很容易看懂。

然后点击右边安装了cmake tools插件后提供的功能页面。

首先点击配置下面第一行配置工具包,选择vs2022,注意架构。

然后就没啥了,你也可以再点下生成目标和启动目标,不过无所谓,整个项目目前也只有一个目标,要么就是all,不影响。

选完工具包之后应该会进入第一次配置,等他下面输出结束后,看着没问题,就可以开始写代码了,不过考虑到当新加cpp文件后cmake所提供的智能感知会失效,最好是先新建要编写的文件,然后重新点一下配置(不是配置工具包)或是删除缓存并重新配置,然后再编码。

最后,我们来讲下代码。代码部分很简单,总共5个文件,main.cpp用于提供程序入口,stk.hpp和stk.cpp用于提供stk头文件include,STKWidget.hpp和STKWidget.cpp构建一个界面,最后显示的也是该界面。

首先是stk.hpp

cpp 复制代码
#pragma once

#include "agstkutil.tlh"
using namespace STKUtil;
#include "agvgt.tlh"
//
#include "agstkgraphics.tlh"
#include "agstkobjects.tlh"
using namespace STKObjects;
#include "stkx.tlh"
using namespace STKXLib;

stk.cpp

cpp 复制代码
#include "stk.hpp"

#include "agstkutil.tli"
//
#include "agstkgraphics.tli"
#include "agstkobjects.tli"
#include "stkx.tli"

上面两个文件一定要注意顺序!include的顺序不能乱!

然后是STKWidget.hpp

cpp 复制代码
#pragma once

#include "stk.hpp"
#include <QAxWidget>
#include <QtWidgets>

class STKWidget : public QWidget {
    Q_OBJECT

private:
    IAgStkObjectRootPtr m_pRoot;
    IAgSTKXApplicationPtr m_app;

public:
    STKWidget(QWidget* parent = nullptr);
};

STKWidget.cpp

cpp 复制代码
#include "STKWidget.hpp"
#include <QDebug>

STKWidget::STKWidget(QWidget* parent)
    : QWidget(parent)
{
    ::CoInitialize(nullptr);

    HRESULT ha = m_app.CreateInstance(__uuidof(AgSTKXApplication));
    if (FAILED(ha)) {
        qDebug() << "AgSTKXApplication Failed!";
        return;
    }

    HRESULT hr = m_pRoot.CreateInstance(__uuidof(AgStkObjectRoot));
    if (FAILED(hr)) {
        qDebug() << "AgStkObjectRoot Failed!";
        return;
    }

    setWindowTitle("STK");

    auto axw3d = new QAxWidget(this);
    axw3d->setControl("STKX11.VOControl");

    auto axw2d = new QAxWidget(this);
    axw2d->setControl("STKX11.2DControl");

    auto btn = new QPushButton(this);
    btn->setFont(QFont("微软雅黑", 12));
    btn->setText("New Scenario");
    connect(btn, &QPushButton::clicked, [&]() {
        
        m_pRoot->NewScenario("Test");
        
        // or
        // m_app->ExecuteCommand("Unload / *");
        // m_app->ExecuteCommand("New / Scenario Test");
    });

    auto grid = new QGridLayout(this);
    grid->setColumnStretch(0, 2);
    grid->setColumnStretch(1, 1);
    grid->addWidget(axw3d, 0, 0, 10, 10);
    grid->addWidget(axw2d, 0, 10, 5, 5);
    grid->addWidget(btn, 7, 12, 1, 1);
}

最开始的 ::CoInitialize(nullptr); 这一句话不要忘记。后面是初始化app和root的代码。再后面这里注意按钮的响应函数,可以直接NewScenario,也可以通过app执行指令。

最后就是main.cpp啦

cpp 复制代码
#include "STKWidget.hpp"
#include <QtWidgets>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    STKWidget w;
    w.show();
    app.exec();
    return 0;
}

很简单的内容,主要呈现STKWidget。

构建、编译、运行程序,应该有一个类似这样的界面出现

点击按钮,经过一小段时间的加载,就能够出现我们想要的场景啦!

相关推荐
唐诺4 小时前
几种广泛使用的 C++ 编译器
c++·编译器
mahuifa5 小时前
混合开发环境---使用编程AI辅助开发Qt
人工智能·vscode·qt·qtcreator·编程ai
冷眼看人间恩怨5 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客5 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin5 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
yuanbenshidiaos6 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室7 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0017 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我587 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
fpcc7 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存