ui因为一直想实现QT+CLion+Revit的开发路径,所以从头开始学习QT与C++,在这里记录一下QT遇到的问题及后续的问题。如果开发周期不足估计会用一两周爆肝出来,随时会断更。
目前的思路是C++创建dll,在dll里面编写QT界面,之后用C++ Revit 的API接口实现整个步骤。 中间需要用到sqlite3的数据库组件,目前就想到这么多。现在已经实现c++读取sqlite3并完成数据接口。
由于项目进度,之前为了实现cpp+QT耽误了太多时间,所以国庆肝了三天完成整个程序,能够将整个数据库的结构模型创建在revit中,在思路已经清晰的情况下,写起来速度确实快了很多,用完cpp再用c#发现c#的继承还挺好用的,测试一下整个项目之后再继续修复这个项目。下面是所有楼板的成果,21层的框架结构预估时间在十分钟以内可以完成,我是分块测试没有集中测试,明天去公司测试一下。
@[toc]
2021/09/14 SQLite3 带有中文路径的字符串转码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 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 "$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/" ) add_custom_command (TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll" "$<TARGET_FILE_DIR:${PROJECT_NAME}>/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" "$<TARGET_FILE_DIR:${PROJECT_NAME}>" ) endforeach (QT_LIB) endif ()
网上参考了很多链接,有说是debug与realse版本同时生成造成静态库冲突的问题,还有说是定义全局变量导致的问题,后来我发现我的问题是由于
1 2 int main () int main (int argc , char * argv[])
在类中没有初始化QApplication造成的,归根结底还是自己菜。 3. 创建的ui.cpp,ui.h,ui.ui三件套在cpp中一直报错,但是运行时不报错,先build后运行也是没有问题但是依旧是错误提示不知道是IDE的问题还是确实存在这个事情。
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所以选择相应版本即可
点开后进入项目文件,我这里cd无法进入,可以直接输入盘符,然后执行cd命令
因为我的ui文件在单独的ui文件夹中所以先qmake创建项目,使用命令:qmake -project,qmake ReadDatabase.pro在qmake项目之后会有.pro文件,可以直接去文件夹看名称
之后cd进入ui文件执行uic命令pathwindow.ui -o ui_pathwindow.h,头文件名称按照自己的UI文件命名即可
connector函数无效 如果在QT 5中使用连接器的老式写法,及connector( , SINGNAL() , ,SLOT()) 会发现函数无效,比如listview无法调出clicked函数,这时我们需要采用新式写法,直接调取相应构件下的函数,如:QListview::clicked , 如果是自己写的函数,直接 pathWindow::show即可调用。具体的操作可以参照豆子的博客 。
2021/09/16 解决办法,参照上面难问题,进入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:
1 2 3 4 5 6 7 8 9 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继承自model类,listwidget继承自item,listview相对灵活,listwidget操作方便。具体解释参照链接:QListView和QListWidget的区别
2021/09/17 QListview中添加Checkbox之后,如何点击行,改变Checkbox状态。
因为ListView继承自Model 所以数据存放在Model中,如果想要获取某一行的数据或是CheckBox的数据,需要进入QAbstractiteModel,获取改行某一列的具体值类型为QVaiant类型,QT中数据赋值依据相应的Role,像Checkbox就是CheckStatusRole,如果单纯的数据直接导出即可,自定义的话需要在加上强制转换即可。
1 2 3 4 5 6 7 8 9 10 11 12 int r_count = ui->listView->model ()->rowCount (); for (int i = 0 ; i < r_count; ++i) { 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的分割使用空格,如下面示例
1 2 QString fileName = QFileDialog::getOpenFileName (this , tr ("open the data file" ), "D:/" , tr ("files(*.db *.ydb)" )); qDebug ()<<fileName;
QTableview 简单初始化,及赋值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ui->tableView->verticalHeader ()->hide (); QStandardItemModel *tableModel = new QStandardItemModel (this ); 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中文件路径分隔符为/在Linux中使用这种格式,在Windows中需要将分隔符变为\,此时需要函数进行转换。来源:https://blog.csdn.net/Littlehero_121/article/details/115078556
1 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即可
1 2 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环境配置好,就可以引用相应的内容,见下图
制作调用dll的函数接口 调用的话需要将dll文件中的部分函数公开,通过在头文件中定义使用extern "C" __declspec(dllexport) int hello2(int argc , char* argv[]); 并在cpp文件中实现 ,然后在引用文件中声明函数,即可使用函数接口实现功能
1 2 3 4 5 extern "C" { void get_lib () ; int hello2 (int args,char * argv[]) ; }
2021/09/25 C++ exe文件执行时报错缺少必要dll文件
add command to cmake file
1 set (CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++" )
如添加1依旧报错,从MinGW的bin文件夹中copy出相应dll放在同一目录下即可
2021/09/28 multiple definition of 问题 昨天定义了一个单例类,向作为导出类导出交互数据,创建完单例模式之后运行报错重复定义,位置是在我的ui_pathwindow.h和unit.h,根据网上的办法
.pro文件内文件重复引用 排查后不是此问题
使用extern关键词,因为太麻烦没有使用
排查是否类名重复命名,导致生成时错误。重新修改名称为唯一名称后还是报错 最后博客 与github 案例,博客中警告具体的内容写在cpp文件中,不要再头文件定义内容,单例的初始化也在cpp中完成 ,案例也是分开编写,将原有的单例模式分成.h和.cpp后编译通过正常运行.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 pathwindow.h class getSingletonValue {private : static getSingletonValue* instance; private : getSingletonValue () = default ; ~getSingletonValue () = default ; 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 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的上方,让编译器首先编译宏命令即可解决。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 extern_dll.h #ifndef READDATABASE_EXTERN_DLL_H #define READDATABASE_EXTERN_DLL_H #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 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 参照链接
Qt5.8生成dll和调用详细图文教程
QT窗口调用子窗口一闪而过问题
讲qt中的界面做成dll以便调用
QT创建dll并引用 今天继续修复之前的问题,考虑到之前实在CLion中创建的QT窗口,并且使用的时QMainWIndow类,所以这次将项目移植到QT Creater中并使用QWidgets类创建,整体都和之前一样就是修改导出函数和之前不一样。
QT Creater中导出类文件编写 还是和c++写的时候宏定义不同,如果QT直接创建Library会得到一个*_global文件,直接将文件宏定义复制把这个文件删除就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #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 <QtCore/qglobal.h> using namespace std;class getSingletonValue {private : static getSingletonValue* instance; private : getSingletonValue () = default ; ~getSingletonValue () = default ; 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; }; struct IDLL { 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 ; }; class RDSTRUCALSOURCE_EXPORT GetObj{public : GetObj (); void MakeShow () ; IDLL* GetValue () ; private : IDLL* _value; }; #endif
链接sqlite3出现的undefine问题 由于是项目移植所以没有使用QT += sql这种配置模式,通过点击项目右键添加库选择外部库,选择自己的lib文件夹即可配置,我在这里头文件直接写的绝对路径,这里还没有修改试错过
1 2 3 4 5 win32: LIBS += -L$$PWD/../lib/ -lRDstrucalSource INCLUDEPATH += $$PWD/../ DEPENDPATH += $$PWD/../
链接dll 和上面的链接库一样的操作步骤,将界面dll,.a,.h文件拷贝到一个文件夹中并添加外部连接,直接在项目中引用头文件即可使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include "widget.h" #include <QApplication> #include "E:\QTprogram\lib\external_dll.h" #include <qdebug.h> 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(),程序就会等待界面运行完成后继续运行后续的程序。
1 2 3 4 5 6 7 void GetObj::MakeShow () { PATHWINDOW *pathwindow = new PATHWINDOW (); pathwindow->show (); }
今天下午没有什么事情,试了一下自己的想法目前一切正常,在下班时完成mingwc++项目间的相互调用,明天继续vc++的调用。放一张成果图。