QT+ CLion + Revit 问题汇总

ui因为一直想实现QT+CLion+Revit的开发路径,所以从头开始学习QT与C++,在这里记录一下QT遇到的问题及后续的问题。如果开发周期不足估计会用一两周爆肝出来,随时会断更。

目前的思路是C++创建dll,在dll里面编写QT界面,之后用C++ Revit 的API接口实现整个步骤。
中间需要用到sqlite3的数据库组件,目前就想到这么多。现在已经实现c++读取sqlite3并完成数据接口。


由于项目进度,之前为了实现cpp+QT耽误了太多时间,所以国庆肝了三天完成整个程序,能够将整个数据库的结构模型创建在revit中,在思路已经清晰的情况下,写起来速度确实快了很多,用完cpp再用c#发现c#的继承还挺好用的,测试一下整个项目之后再继续修复这个项目。下面是所有楼板的成果,21层的框架结构预估时间在十分钟以内可以完成,我是分块测试没有集中测试,明天去公司测试一下。
QT+ CLion + Revit 问题汇总_第1张图片

文章目录

    • 2021/09/14
      • SQLite3
        • 带有中文路径的字符串转码
      • QT
        • 1. CMAKE文件
        • 2. mingw 编译报错 `Must construct a QApplication before a QWidget`
    • 2021/09/15
      • 1. ui::pathwindow提示无法找到,但是可以正常生成
      • connector函数无效
    • 2021/09/16
      • 1.再CLion中使用external tools 修改ui文件后无法更新格式
      • 使用MinGW-64编译是报错:**multiple definition of xxx**
      • 生成QTlistview 最下面一行数据始终无法显示完整
      • qlistview添加checkbox
      • 关于listview与listwidget的区别
    • 2021/09/17
      • QListview中添加Checkbox之后,如何点击行,改变Checkbox状态。
      • 实现点击按钮打开FileDialog对话框
      • QTableview 简单初始化,及赋值
      • vector<> 自定义类中使用const char*传递字符串为空值
      • 变换文件分隔符 ' \ ' '/'
    • 2021/09/22
      • 数组出界,自己检查代码不规范引发的 SIGTRAP (Trace/breakpoint trap)
    • 2021/09/24
      • CLion生成的带有dll窗口的类如何被其他c++调用
      • 修改CMAKE生成文件格式后无法生成dll文件
      • 静态载入dll的方法
      • 制作调用dll的函数接口
    • 2021/09/25
      • C++ exe文件执行时报错缺少必要dll文件
    • 2021/09/28
      • multiple definition of 问题
      • function 'IDLL* GetObj()' definition is marked dllimport
    • 2021/10/15
      • 参照链接
      • QT创建dll并引用
      • QT Creater中导出类文件编写
      • 链接sqlite3出现的undefine问题
      • 链接dll
      • QT 链接dll文件中界面一闪而逝的情况

2021/09/14

SQLite3

带有中文路径的字符串转码

inline string UTF8ToGB(const char* str)
{
    string result;
    WCHAR *strSrc;
    LPSTR szRes;

    //获得临时变量的大小
    int i = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
    strSrc = new WCHAR[i + 1];
    MultiByteToWideChar(CP_UTF8, 0, str, -1, strSrc, i);

    //获得临时变量的大小
    i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
    szRes = new CHAR[i + 1];
    WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL);

    result = szRes;
    delete[]strSrc;
    delete[]szRes;

    return result;
}

const char* path = strdup(UTF8ToGB(R"(C:\Users\xu.lanhui\Desktop\二次开发\test_db_files\morelevel.db)").c_str());

bool getValueFromDatabase::OpenSQLite(const char* path) {
    if(path == NULL)
        return false;
    int nlen = 0,codepage;
    char cpath[130];
    wchar_t wpath[130];
    codepage = AreFileApisANSI()?CP_ACP:CP_OEMCP;
    nlen = MultiByteToWideChar(codepage,0,path,-1,NULL,0);
    MultiByteToWideChar(CP_ACP,0,path,-1,wpath,nlen);
    nlen = WideCharToMultiByte(CP_UTF8,0,wpath,-1,0,0,0,0);
    WideCharToMultiByte(CP_UTF8,0,wpath,-1, cpath,nlen,0,0);
    int result = sqlite3_open_v2(cpath,&db,SQLITE_OPEN_READONLY,NULL);
    if(result == SQLITE_OK)
        return true;
    else
        return false;
}

QT

1. CMAKE文件

cmake_minimum_required(VERSION 3.20)
project(ReadDatabase)

set(CMAKE_CXX_STANDARD 14)
#自动调用moc,uic,rcc处理qt的部分
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

SET(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_PREFIX_PATH "E:\\Qt\\Qt5.14.2\\5.14.2\\mingw73_64")

find_package(Qt5 COMPONENTS
        Core
        Gui
        Widgets
        REQUIRED)


set(SOURCE_FILES  library.cpp sql_src/sqlite3.c unit.h ui/pathwindow.cpp ui/pathwindow.h)

add_executable(ReadDatabase ${SOURCE_FILES})

target_link_libraries(ReadDatabase
        Qt5::Core
        Qt5::Gui
        Qt5::Widgets
        )

if (WIN32)
    set(DEBUG_SUFFIX)
    #if (CMAKE_BUILD_TYPE MATCHES "Debug")
    #set(DEBUG_SUFFIX "d")
    #endif ()
    set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}")
    if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
        set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
        if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
            set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
        endif ()
    endif ()
    if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll")
        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                COMMAND ${CMAKE_COMMAND} -E make_directory
                "$/plugins/platforms/")
        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                COMMAND ${CMAKE_COMMAND} -E copy
                "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll"
                "$/plugins/platforms/")
    endif ()
    foreach (QT_LIB Core Gui Widgets)
        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
                COMMAND ${CMAKE_COMMAND} -E copy
                "${QT_INSTALL_PATH}/bin/Qt5${QT_LIB}${DEBUG_SUFFIX}.dll"
                "$")
    endforeach (QT_LIB)
endif ()

2. mingw 编译报错 Must construct a QApplication before a QWidget

网上参考了很多链接,有说是debug与realse版本同时生成造成静态库冲突的问题,还有说是定义全局变量导致的问题,后来我发现我的问题是由于

int main() //修改前
int main(int argc , char* argv[]) // 修改后

在类中没有初始化QApplication造成的,归根结底还是自己菜。
3. 创建的ui.cpp,ui.h,ui.ui三件套在cpp中一直报错,但是运行时不报错,先build后运行也是没有问题但是依旧是错误提示不知道是IDE的问题还是确实存在这个事情。
QT+ CLion + Revit 问题汇总_第2张图片


2021/09/15

1. ui::pathwindow提示无法找到,但是可以正常生成

接上面的问题,由于能够显示界面所以昨天降错误忽视,结果今天赋值的时候发现无法赋值,因为ui指针是无效指针,并且无法找到ui_pathwindow.h的文件。注意到QT文件生成时会有一句注释,昨天研究过但是因为懒没有继续深入,造成今天的错误。。。。
// You may need to build the project (run Qt uic code generator) to get "ui_pathWindow.h" resolved
注释告诉我们build项目之前应该先使用uic生成一下ui_pathwindow.h,下面是uic的使用步骤,参照博客:QT中 uic 工具的使用

  • 直接在Window搜索界面搜索QT的控制台程序,因为我的程序使用的是MinGW64所以选择相应版本即可
    QT+ CLion + Revit 问题汇总_第3张图片
  • 点开后进入项目文件,我这里cd无法进入,可以直接输入盘符,然后执行cd命令
  • 因为我的ui文件在单独的ui文件夹中所以先qmake创建项目,使用命令:
    qmake -project,qmake ReadDatabase.pro在qmake项目之后会有.pro文件,可以直接去文件夹看名称
  • 之后cd进入ui文件执行uic命令pathwindow.ui -o ui_pathwindow.h,头文件名称按照自己的UI文件命名即可
    QT+ CLion + Revit 问题汇总_第4张图片

connector函数无效

如果在QT 5中使用连接器的老式写法,及connector( , SINGNAL() , ,SLOT())
会发现函数无效,比如listview无法调出clicked函数,这时我们需要采用新式写法,直接调取相应构件下的函数,如:QListview::clicked , 如果是自己写的函数,直接 pathWindow::show即可调用。具体的操作可以参照豆子的博客。


2021/09/16

1.再CLion中使用external tools 修改ui文件后无法更新格式

解决办法,参照上面难问题,进入ui文件位置输入命令uic pathwindow.ui -o ui_pathwindow.h重新生成ui-pathwindow.h文件,有时间的可以直接修改.h文件小的构件可以这样修改,如果改动较大还是重新跑一边比较好。

使用MinGW-64编译是报错:multiple definition of xxx

解决办法:使用内联函数,通过内联函数减少重复定义,解决此问题。

生成QTlistview 最下面一行数据始终无法显示完整

重新生成一遍.h文件以后可以正常显示,不清楚具体的原因,todo:

qlistview添加checkbox

如果希望给listwidget添加checkbox可参照下面链接:http://www.qtcentre.org/threads/47119-checkbox-on-QListView
code:

    for (int i = 0; i < count; ++i) {
        QString string = static_cast<QString>(stringList.at(i));
        QStandardItem *item = new QStandardItem(string);

        item->setCheckable(true);
        item->setCheckState(Qt::Checked);

        itemModel->setItem(i,item);
    }

关于listview与listwidget的区别

listview继承自model类,listwidget继承自item,listview相对灵活,listwidget操作方便。具体解释参照链接:QListView和QListWidget的区别


2021/09/17

QListview中添加Checkbox之后,如何点击行,改变Checkbox状态。

  • 因为ListView继承自Model 所以数据存放在Model中,如果想要获取某一行的数据或是CheckBox的数据,需要进入QAbstractiteModel,获取改行某一列的具体值类型为QVaiant类型,QT中数据赋值依据相应的Role,像Checkbox就是CheckStatusRole,如果单纯的数据直接导出即可,自定义的话需要在加上强制转换即可。
int r_count = ui->listView->model()->rowCount();
    for (int i = 0; i < r_count; ++i) {
        //selected
        QModelIndex b_index = ui->listView->model()->index(i,0);
        QString level = b_index.data().toString();
        bool checked = b_index.data(Qt::CheckStateRole).toBool();
        if(checked)
        {
            float f_level = level.toFloat();
            tFloors.push_back(f_level);
        }
    }

实现点击按钮打开FileDialog对话框

这里面Filter的分割使用空格,如下面示例

QString fileName = QFileDialog::getOpenFileName(this, tr("open the data file"), "D:/", tr("files(*.db *.ydb)"));
    qDebug()<<fileName;

QTableview 简单初始化,及赋值

//ui style

    //hide row number
    ui->tableView->verticalHeader()->hide();
    //create model
    QStandardItemModel *tableModel = new QStandardItemModel(this);


    //set column titles

    QStringList columnTitles;
    columnTitles<<"Location"<<"Floor"<<"Name";
    tableModel->setHorizontalHeaderLabels(columnTitles);

    for (int i = 0; i < columns.size(); ++i) {
        stcolumn value = columns.at(i);
        QString column_locate = QString(value.location.display().c_str());
        QString column_name = QString(value.name.c_str());
        tableModel->setItem(i,0,new QStandardItem(column_locate));
        tableModel->setItem(i,1,new QStandardItem(QString::number(value.floor.evaluate)));
        tableModel->setItem(i,2,new QStandardItem(column_name));
        qDebug()<<column_locate;
        qDebug()<<value.floor.evaluate;
        qDebug()<<value.name.c_str();
    }

    ui->tableView->setModel(tableModel);

vector<> 自定义类中使用const char*传递字符串为空值

我定义了一个类,类中字符串使用const char*记录值,但是当值存放在vector容器中时,相应的字符串值变为空值,应该是vector作为一个指针存放数据,const char 此时指向失败,更换为string类型即可正常显示*
QT+ CLion + Revit 问题汇总_第5张图片

变换文件分隔符 ’ \ ’ ‘/’

QT中文件路径分隔符为/在Linux中使用这种格式,在Windows中需要将分隔符变为\,此时需要函数进行转换。来源:https://blog.csdn.net/Littlehero_121/article/details/115078556

QDir::toNativeSeparators(fileName).toStdString().c_str()

2021/09/22

经过假期与肠胃炎的争斗,现在继续编写程序-.-。

数组出界,自己检查代码不规范引发的 SIGTRAP (Trace/breakpoint trap)

节前取一个模块的数据,发现一直报错SIGTRAP (Trace/breakpoint trap),或是Process finished with exit code -1073740940 (0xC0000374),打开搜索引擎开始复制粘贴发现数组错误,或是数组读取出界又或是析构函数发现错误,为此我改动了我的类结构,发现还是此错误,最终发现是因为工厂模式中指向出现错误,将楼板类指向了楼层类,因此直接读取vector容器报错,不过以后碰到数组出界的问题可以参照下面的博客解决。c++调试在容器释放内存时报Unknown Signal 或 Trace/breakpoint trap异常

2021/09/24

程序的功能基本完成,简单点来说就是数据库的数据读取与整理,完成后因为要与Revit关联所以中建需要用到VC++,为了方便便将窗口程序打包成了dll,方便后期的使用,中间遇到了一些问题记录一下

CLion生成的带有dll窗口的类如何被其他c++调用

-  为了测试方便,所以我将原来的动态链接文件修改为执行文件,只一个修改一下CMAKElist即可
```cmake
# add_executable(ReadDatabase ${SOURCE_FILES})

add_library(ReadDatabase SHARED ${SOURCE_FILES})
```

修改CMAKE生成文件格式后无法生成dll文件

修改cmake文件后发现一直无法生成dll文件,发现将原有的.exe可执行文件删除后即可成功生成。
在这里插入图片描述

静态载入dll的方法

CMAKE文件,我们采用静态载入的办法,关于静态载入或是动态载入可以参照Windows:CLion下编写及动/ 静态调用DLL,重新创建项目编写cmake文件,将lib载入即可使用,此处注意一个事情,如果事情像我一样需要载入带有QT窗口的dll,需要将工程文件debug下的三个QT开头的dll一起copy过来,不然会一直报错无法找到文件,这种错误debug也不会有提示,错误名称为:Process finished with exit code -1073741515 (0xC0000135),此时我们需要将文件copy到debug文件下面,如下图。之后修改CMAKE文件与窗口文件相同,将QT环境配置好,就可以引用相应的内容,见下图
QT+ CLion + Revit 问题汇总_第6张图片

QT+ CLion + Revit 问题汇总_第7张图片

制作调用dll的函数接口

调用的话需要将dll文件中的部分函数公开,通过在头文件中定义使用extern "C" __declspec(dllexport) int hello2(int argc , char* argv[]); 并在cpp文件中实现,然后在引用文件中声明函数,即可使用函数接口实现功能


extern "C"{
    void get_lib();
    int hello2(int args,char* argv[]);
}

2021/09/25

C++ exe文件执行时报错缺少必要dll文件

  1. add command to cmake file
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
  1. 如添加1依旧报错,从MinGW的bin文件夹中copy出相应dll放在同一目录下即可

2021/09/28

multiple definition of 问题

昨天定义了一个单例类,向作为导出类导出交互数据,创建完单例模式之后运行报错重复定义,位置是在我的ui_pathwindow.hunit.h,根据网上的办法

  1. .pro文件内文件重复引用 排查后不是此问题
  2. 使用extern关键词,因为太麻烦没有使用
  3. 排查是否类名重复命名,导致生成时错误。重新修改名称为唯一名称后还是报错
    最后博客与github案例,博客中警告具体的内容写在cpp文件中,不要再头文件定义内容,单例的初始化也在cpp中完成,案例也是分开编写,将原有的单例模式分成.h和.cpp后编译通过正常运行.
pathwindow.h

/*
 * singleton from
 * https://zhuanlan.zhihu.com/p/37469260
 */

class getSingletonValue{
private:
    static getSingletonValue* instance;
private:
    getSingletonValue() = default;
    ~getSingletonValue() = default;
    //prevent copying
    getSingletonValue(const getSingletonValue&) =delete;
    getSingletonValue& operator=(const getSingletonValue&) = delete;

private:
    class _Deletor{
    public:
        ~_Deletor();
        static _Deletor deletor;
    };

public:
    static getSingletonValue* getInstance();

    vector<st_slab> _Slabs;
    vector<st_wall> _Walls;
    vector<st_beam> _Beams;
    vector<float> _Levels;
    vector<st_column> _Columns;


};

pathwindow.cpp

//must initialization static variables out of class
getSingletonValue* getSingletonValue::instance = NULL;

getSingletonValue *getSingletonValue::getInstance() {
    if(instance == NULL)
        return new getSingletonValue();
    return instance;
}

getSingletonValue::_Deletor::~_Deletor() {
    if(getSingletonValue::instance != NULL)
        delete getSingletonValue::instance;
}

function ‘IDLL* GetObj()’ definition is marked dllimport

使用宏定义dll导出接口报错,无法标记dllexport,需要将宏定义放在#include的上方,让编译器首先编译宏命令即可解决。

extern_dll.h

#ifndef READDATABASE_EXTERN_DLL_H
#define READDATABASE_EXTERN_DLL_H

//1. macro definition
#ifdef DLL_EXPORTS
#define DLL_API_ __declspec(dllexport)
#else
#define DLL_API_ __declspec(dllimport)
#endif

#include "unit.h"
#include "library.h"

using namespace std;



class IDLL{
public:
    virtual vector<st_column> GetColumn() = 0;
    virtual vector<float> GetLevel() = 0;
    virtual vector<st_beam> GetBeam() = 0;
    virtual vector<st_slab> GetSlab() = 0;
    virtual vector<st_wall> GetWall() = 0;
    virtual void Release() = 0;

protected:
    getSingletonValue* _instance = getSingletonValue::getInstance();
};
extern "C" DLL_API_ IDLL* __stdcall GetObj();



#endif //READDATABASE_EXTERN_DLL_H


extern_dll.cpp

#define DLL_EXPORTS

#include "extern_dll.h"
#include "library.h"


class ImiDLL: public IDLL{
    vector<st_column> GetColumn() override;
    vector<float> GetLevel() override;
    vector<st_beam> GetBeam() override;
    vector<st_slab> GetSlab() override;
    vector<st_wall> GetWall() override;
    void Release() override;
};

vector<st_column> ImiDLL::GetColumn() {
    return _instance->_Columns;
}

vector<float> ImiDLL::GetLevel() {
    return _instance->_Levels;
}

vector<st_beam> ImiDLL::GetBeam() {
    return _instance->_Beams;
}

vector<st_slab> ImiDLL::GetSlab() {
    return _instance->_Slabs;
}

vector<st_wall> ImiDLL::GetWall() {
    return _instance->_Walls;
}

void ImiDLL::Release() {
    delete this;
}

extern "C" DLL_API_ IDLL* __stdcall GetObj(){
    return new ImiDLL;
}

2021/10/15

参照链接

  1. Qt5.8生成dll和调用详细图文教程
  2. QT窗口调用子窗口一闪而过问题
  3. 讲qt中的界面做成dll以便调用

QT创建dll并引用

今天继续修复之前的问题,考虑到之前实在CLion中创建的QT窗口,并且使用的时QMainWIndow类,所以这次将项目移植到QT Creater中并使用QWidgets类创建,整体都和之前一样就是修改导出函数和之前不一样。

QT Creater中导出类文件编写

还是和c++写的时候宏定义不同,如果QT直接创建Library会得到一个*_global文件,直接将文件宏定义复制把这个文件删除就可以了


#if defined(RDSTRUCALSOURCE_LIBRARY)
#  define RDSTRUCALSOURCE_EXPORT Q_DECL_EXPORT
#else
#  define RDSTRUCALSOURCE_EXPORT Q_DECL_IMPORT
#endif


#ifndef EXTERNAL_DLL_H
#define EXTERNAL_DLL_H


#include "unit.h"
#include "library.h"
#include 

using namespace std;

class getSingletonValue{
private:
    static getSingletonValue* instance;
private:
    getSingletonValue() = default;
    ~getSingletonValue() = default;
    //prevent copying
    getSingletonValue(const getSingletonValue&) =delete;
    getSingletonValue& operator=(const getSingletonValue&) = delete;

private:
    class _Deletor{
    public:
        ~_Deletor();
        static _Deletor deletor;
    };

public:
    static getSingletonValue* getInstance();

    vector _Slabs;
    vector _Walls;
    vector _Beams;
    vector _Levels;
    vector _Columns;

};


struct IDLL{
    virtual vector GetColumn() = 0;
    virtual vector GetLevel() = 0;
    virtual vector GetBeam() = 0;
    virtual vector GetSlab() = 0;
    virtual vector GetWall() = 0;
    virtual void Release() = 0;
};

class RDSTRUCALSOURCE_EXPORT GetObj{

public:
    GetObj();
    void MakeShow();
    IDLL* GetValue();
private:
    IDLL* _value;
};



#endif // EXTERNAL_DLL_H

链接sqlite3出现的undefine问题

由于是项目移植所以没有使用QT += sql这种配置模式,通过点击项目右键添加库选择外部库,选择自己的lib文件夹即可配置,我在这里头文件直接写的绝对路径,这里还没有修改试错过

win32: LIBS += -L$$PWD/../lib/ -lRDstrucalSource

INCLUDEPATH += $$PWD/../
DEPENDPATH += $$PWD/../

链接dll

和上面的链接库一样的操作步骤,将界面dll,.a,.h文件拷贝到一个文件夹中并添加外部连接,直接在项目中引用头文件即可使用。

#include "widget.h"

#include 
#include "E:\QTprogram\lib\external_dll.h"
#include 

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

   GetObj* getObj = new GetObj();
   getObj->MakeShow();

    qDebug()<<"success";
    return a.exec();
}

QT 链接dll文件中界面一闪而逝的情况

其实是堆栈的应用,我们可以将创建窗口指针,show(),程序就会等待界面运行完成后继续运行后续的程序。

void GetObj::MakeShow()
{
    //show window
    PATHWINDOW *pathwindow = new PATHWINDOW();
    pathwindow->show();
}

今天下午没有什么事情,试了一下自己的想法目前一切正常,在下班时完成mingwc++项目间的相互调用,明天继续vc++的调用。放一张成果图。
QT+ CLion + Revit 问题汇总_第8张图片

你可能感兴趣的:(C++,Revit二次开发,QT,qt,bim)