Qt C++ 工具箱
从零开始的Qt开发之路
里面大概会写一些和Qt相关的内容,也不说是从0开始,感觉Qt做东西和用 C#也差不了很多?也许吧,总之慢慢来,一步一个脚印,直到给它拿下。
2022.5.13
关于ifdef预编译的问题
一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。
条件编译命令最常见的形式为:
#ifdef 标识符
程序段1
#else
程序段2
#endif
往往可以用于一些公司的发布版本代码的修改,比如我想这个版本和那个版本,通过一些参数或者变量来实现,这就需要最好就通过ifdef来实现,这样可以省很多编译资源,而不用用户全部编译完成之后再进行相应的比对。这些指令只检查使用 #define 定义的标识符是否存在,而不检查在 C 或 C++ 源代码中声明的标识符。
2022.5.12
template 模板
有时候我们可能会写一大堆同名函数,这个时候就需要用到template函数模板
比如我们常用的swap交换两个数字的函数,交换数据的双方可能是int,double,float,string等等,总不能同名函数一直这么写下去吧,所以template应运而生。
例如:
void swap(int&a , int& b) {
int temp = a;
a = b;
b = temp;
}
首先我们把函数模板的声明形式摆在这:
template <class identifier> function_declaration;
template <typename identifier> function_declaration;
就比如前面提到的swap函数,我们来给它指定一个模板
//method.h
template<typename T> void swap(T& t1, T& t2);
#include "method.cpp"
template<typename T> void swap(T& t1, T& t2) {
T tmpT;
tmpT = t1;
t1 = t2;
t2 = tmpT;
}
这样就是一个模板的声明和定义了,那模板该如何进行实例化呢?
范例:
int num1 = 1, num2 = 2;
swap<int>(num1, num2);
printf("num1:%d, num2:%d\n", num1, num2);
尖括号中可以写一些需要返回的数据类型,然后直接向函数中输入已被定义的参数即可。
类模板
考虑我们写一个简单的栈的类,这个栈可以支持int类型,long类型,string类型等等,不利用类模板,我们就要写三个以上的stack类,其中代码基本一样,通过类模板,我们可以定义一个简单的栈模板,再根据需要实例化为int栈,long栈,string栈。
//statck.h
template <class T> class Stack {
public:
Stack();
~Stack();
void push(T t);
T pop();
bool isEmpty();
private:
T *m_pT;
int m_maxSize;
int m_size;
};
//stack.cpp
template <class T> Stack<T>::Stack(){
m_maxSize = 100;
m_size = 0;
m_pT = new T[m_maxSize];
}
template <class T> Stack<T>::~Stack() {
delete [] m_pT ;
}
template <class T> void Stack<T>::push(T t) {
m_size++;
m_pT[m_size - 1] = t;
}
template <class T> T Stack<T>::pop() {
T t = m_pT[m_size - 1];
m_size--;
return t;
}
template <class T> bool Stack<T>::isEmpty() {
return m_size == 0;
}
至于其他的,包括模板参数,模板专门化等,详情看文章C++模板template用法总结
C++回调函数概要
可以举个实例来看看
#include <iostream>
class ProgramA {
public:
void FunA1() { printf("I'am ProgramA.FunA1() and be called..\n"); }
static void FunA2() { printf("I'am ProgramA.FunA2() and be called..\n"); }
};
class ProgramB {
public:
void FunB1(void (*callback)()) {
printf("I'am ProgramB.FunB1() and be called..\n");
callback();
}
};
int main(int argc, char **argv) {
ProgramA PA;
PA.FunA1();
ProgramB PB;
PB.FunB1(ProgramA::FunA2);
}
上述代码中,也就是说,在一个函数里面写一个typedef (*callback) 则代表向这个函数里面传入了一个 函数的指针,然后就可以在这个函数里面 调用这个函数
比如上面这个FunB1里面,传入一个callback()的区域函数指针,然后在函数体内可以调用这个callback()函数
在实际调用中,我们向FunB1中传入Program类中FunA2函数进去,然后再FunB1的函数实现中,实际上调用的就是FunA2函数了
结果如下:
I'am ProgramB.FunA1() and be called..
I'am ProgramB.FunB1() and be called..
I'am ProgramB.FunA2() and be called..
要注意一些问题,就是回调函数在类内、类外、静态函数、非静态函数之间的调用关系,详情见下方文章
这里简单摘抄一下:为了避免 static 函数不能访问非static 成员变量或函数,会严重限制回调函数可以实现的功能。而且在programB中FunB1还使用 programA的类型,也就我预先还要知道回调函数所属的类定义,当programB想独立封装时就不好用了。
一个比较常用的方法就是:可以把非static的回调函数 包装为另一个static函数,这种方式也是一种应用比较广的方法。
#include <iostream>
class ProgramA {
public:
void FunA1() { printf("I'am ProgramA.FunA1() and be called..\n"); }
void FunA2() { printf("I'am ProgramA.FunA2() and be called..\n"); }
static void FunA2Wrapper(void *context) {
printf("I'am ProgramA.FunA2Wrapper() and be called..\n");
((ProgramA *)context)->FunA2(); // 此处调用的FunA2()是context的函数, 不是this->FunA2()
}
};
class ProgramB {
public:
void FunB1(void (ProgramA::*callback)(), void *context) {
printf("I'am ProgramB.FunB1() and be called..\n");
((ProgramA *)context->*callback)();
}
void FunB2(void (*callback)(void *), void *context) {
printf("I'am ProgramB.FunB2() and be called..\n");
callback(context);
}
};
int main(int argc, char **argv) {
ProgramA PA;
PA.FunA1();
ProgramB PB;
PB.FunB1(&ProgramA::FunA2, &PA); // 此处都要加&
printf("\n");
PB.FunB2(ProgramA::FunA2Wrapper, &PA);
}
但是C++其实有自带的转换宏,不需要程序员去这样写方法,太麻烦,而且也没什么含义。
std::funtion和std::bind的使用
std::funtion和std::bind可以登场了。
std::function是一种通用、多态的函数封装。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等[1]。
std::bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的[2]。
这里直接上代码,看std::funtion和std::bind如何在回调中使用。
#include <iostream>
#include <functional> // fucntion/bind
class ProgramA {
public:
void FunA1() { printf("I'am ProgramA.FunA1() and be called..\n"); }
void FunA2() { printf("I'am ProgramA.FunA2() and be called..\n"); }
static void FunA3() { printf("I'am ProgramA.FunA3() and be called..\n"); }
};
class ProgramB {
typedef std::function<void ()> CallbackFun;
public:
void FunB1(CallbackFun callback) {
printf("I'am ProgramB.FunB2() and be called..\n");
callback();
}
};
void normFun() { printf("I'am normFun() and be called..\n"); }
int main(int argc, char **argv) {
ProgramA PA;
PA.FunA1();
printf("\n");
ProgramB PB;
PB.FunB1(normFun);
printf("\n");
PB.FunB1(ProgramA::FunA3);
printf("\n");
PB.FunB1(std::bind(&ProgramA::FunA2, &PA));
}
std::funtion支持直接传入函数地址,或者通过std::bind指定。
简而言之,std::funtion是定义函数类型(输入、输出),std::bind是绑定特定的函数(具体的要调用的函数)。
相比于wrapper方法,这个方式要更直接、简洁很多。
Qt 服务端中 设置心跳断连的实例
2022.5.11
int char*、string 和char
直接用(int)进行强制转换可能会导致一些不可预料的错误,需要使用到atoi函数C++:string类型、char *类型,int类型之间的转换:atoi(),stoi(),c_str()
2022.5.10
定义问题
注意,千万注意
已经定义过的关键字,就不要再定义了
比如
char* serverip = "0";
int port = 4443;
char* otherUserName = "0";
char* otherUserSeat = "0";
char* currentUserName = "0";
char* currentUserSeat = "0";
if (true) {
char* serverip = "192.168.110.122";
char* port = 4443;
char* otherUserName = "11";
char* otherUserSeat = "s1";
char* currentUserName = "22";
char* currentUserSeat = "s2";
char* qDebug() << "true";
}
这可能会导致一些问题,使得程序无法进入到这个分支里面去,出现严重错误!!!
注意格式转换问题
如果你拿到一个QString类型,先转到char* 再转回QString,可能会缺失一部分内容,导致报错
这个websoup什么的exe,如果你给定的ip 连接错误或者连接失败,会持续的更换线程ID,同时会不停地更换输入法,导致使用失败
睡眠真的很重要
关于我在多屏幕之间的坐标转换这个问题上犯了蠢,geometry这个方法设定窗体坐标位置,注意是 x y w h
所以第一个参数不应该带有主桌面的x坐标,因为主窗体第一个坐标永远是0 , 0 !我之前想了半天这个问题,为什么我锁定了主屏幕,结果窗口出现在副屏幕上
this->setGeometry(screenRect.x() - wholeFrm.width, screenRect.y(), wholeFrm.width, wholeFrm.height);
这显然是错误的,因为screenRect.x() = 0 ,所以该窗体永远只会显示到主屏幕的左边去!\
正确做法范例:
//获取主屏幕索引
int mainScreenID = desktop->primaryScreen();
//获得主屏幕宽高
QRect screenRect = desktop->screenGeometry(mainScreenID);
//将窗体挪到对应位置去
this->setGeometry(screenRect.width() - wholeFrm.width, screenRect.y(), wholeFrm.width, wholeFrm.height);
2022.5.9
在VS中 Qt的release模式下进行调试
报错 This application failed to start because it could not find or load the Qt platform plugin"windows" in ""
缺少了platforms的支持,找到msvc文件夹中的platforms文件夹,粘贴到 Release文件夹中就行了
VS工程中常用路径宏定义表示$(SolutionDir) $(ProjectDir) $(ProjectName) $(Configuration)
一个关于VS工程中常用路径宏定义表示的说明 VS工程中常用路径宏定义表示$(SolutionDir) $(ProjectDir) $(ProjectName) $(Configuration) (good解释的很清楚)
关于TestDemo中给定的默认的引用路径为:$(SolutionDir)$(Configuration)\LBD_VideoMeeting.lib;%(AdditionalDependencies)
关于如何向c++编译生成的exe中设定参数这回事
一般是通过命令行,具体如何实现请参考
详情请参考:C/C++生成的exe文件如何传参数到main中
关于如何在qt中添加点击事件
博客 Qt QLable 响应单击点击事件 中写的比较清楚了,注意一点就是,eventFilter是继承过来的方法,所以不能直接取用,需要添加一个override参数,例如:
public slots:
bool eventFilter(QObject *, QEvent *) override;
2022.5.7
一个关于如何将widget变成一个像商用软件那样 不可拖拽大小、没有开启、关闭等按钮的固定窗口Qt::FramelessWindowHint无边框化,移动,大小调整插入此业务代码的时候需要注意到就是,这个Direction不是Qt自带的,而是一个Enum,需要注意以下这个点
一些和窗口控件有关的、选中标红的方案:QT的基本控件的焦点定位及切换
2022.5.6
将窗口设置为全屏化
但是qt里应该有自带的,我稍微研究下
qt中 按钮状态的切换
可以设置一下按钮按下去就陷下去,再按一下回弹的方法,就是哪个clicked(bool)方法,调用案例如下:
首先在ui初始化的时候需要对按钮进行设置:ui.btn_Audio->setCheckable(true);
void LBD_VideoMeeting_SingalMeet::on_btn_Audio_clicked(bool isSilent)
{
if (isSilent) {
qDebug() << "isSilent turns into TURE" << endl;
}
else {
qDebug() << "isSilent turns into FALSE" << endl;
}
}
2022.5.6
第一个项目,可视化对讲 关于窗口句柄,其实理解上是没有问题的,为什么5.5找了一天问题呢?其实不是我自己的问题,是Release里面少了个exe文件,但是我并不知道这回事。其中关于访问网络有两个文件时必须的,一个是MediasoupWebSocket.exe,另一个是CLBDVideoMeeting.ini,这两个文件是被LBD_ViedeoMeeting引用了的,但是这里并未提及。
获得
2022.5.5
将句柄转成int类型,示例:QString var = QString("%1%2%3").arg("param1").arg(QString::fromLocal8bit(char*串)).arg("param2");
因为陶工给的lib是Release版的,所以这里我们也可以用Release版打开,但是这里有个问题,就是在vs中打开一个qt的工程文件,打开debug版是没有问题的,但是release版不行,原因是生成Release版的时候,它并没能正常的生成一个plugins文件,少了dll所以会报错。
※ 重要内容
如何在QT中获得窗口&控件的句柄:获得窗口句柄
第一次开始进行Qt的界面开发,第一个项目:LBD_VideoMeetingTest,有个问题,就是运行会报错,提示无法解析的外部符号。注:目前为止遇到的所有无法解析外部符号的问题都是由无法调用起来.lib文件造成的,要多加注意这点。因为是隐式调用,而且lib文件的调用是直接设置到工程文件里面去的而不是通过#pragma调用,具体设置方式是 右键工程文件->属性->链接器->附加依赖项->输入具体的.lib文件路径(相对或者绝对都行)包含文件名,如$(SolutionDir)$(Configuration)\LBD_VideoMeeting.lib;%(AdditionalDependencies),这时就相当于用了一个#pragma声明引用了该.lib文件,使用对应的.h头文件,即可直接调用dll
2022.4.18
看到个挺全的教程文档,当个参考
QString::Comapre 函数
之前遇到一个坑点就是关于QString::Compare函数,我以为这个函数是用作比对两个字符串是否相同的,但是并不是,它是用作两个字符串比大小的,具体实例如下:
QString::compare("ab","ab"); //值为0;
QString::compare(“ab”,“df"); //值为<0;
QString::compare("df","ab"); //值为>0;
反正很傻逼,我开始的时候怎么判都潘不对,如果要判断两个字符串是否相同,则需要用QString::compare(a,b) == 0;
qt绑定数据库 遇到的一些问题
首先向工具箱中添加两个实例,一个是如何在qt的tableView中展示数据库,一个是如何连接数据库
注意使用构造函数的时候你声明的定义,如果你自定义了构造函数,而且参数非缺省,则你在任何地方都不应该出现如QInser Inser;这种语句,否则可能会报错。
另外,就是在使用执行命令的query类的指令时,一定要进行初始化,比如QSqlQuery query = QSqlQuery(db)
2022.4.16 数据库?
就是有个问题,你这个数据库连接了对吧,你建立了一个query对象,他就可以执行你的代码吗?理论上说是你的query直接和你当前的databse绑定了,但是并不一定,最好是直接在建立之后进行一下初始化
比如: 在.h文化中初始化 QSqlQuery query = QSqlQuery(db);
然后在cpp中进行绑定 QSqlQuery query(db);
在执行中要注意一点,就是第一行并不是默认的,要先query.next()
举个例子
while(query.next()) //遍历数据库查找数据,先next,否则
{
name = query.value(1).toString();
sex = query.value(2).toString();
stu_number = query.value(3).toString();
qDebug() << name;
qDebug() << sex;
qDebug() << stu_number;
}
2022.4.15
Driver not loaded?
真草泥马了,这问题是真的傻逼,我的QT 可能跟我的 数据库mysql装的不是一个位数的,mysql是32位的,其实我的qt用的编译器也是32位的,但是系统却不是32位的可能,所以对mysql数据库的连接connector 不能直接用mysql给定的,具体原理我也不知道,总之网上的教程很多其实都没说到点子上,反正我自己的问题并没有解决,而在我一段时间的思考之后,我才意识到可能是我的connector也下错了,而是要下个32位的connector
因为我的mysql版本是5.5 , 所以这个connector的版本可以下个旧一点的,比如这个 Windows (x86, 32-bit), ZIP Archive 下个这个就行
QtDesigner 中 窗口没有工具栏?
因为Widgets类本身就是没有 工具栏 状态栏等等,也没法用Action,只有MainWindow类才继承了这些功能,如果需要用这些的话只能再新建一个MainWindow。
如果需要就新建一个MainWindow类,然后将Widget中所有组件复制过去这样
driver not loaded?
转移lib和dll 的方法都试过了,唯一的问题就是数据库和qt的版本之间不兼容,32位的数据库
然后数据库连接还是可能出错,不要用127.0.0.1做本地,按理说是没问题的,但还是报错
qt 中文报错?
如果是qt5以上版本,将
if _MSC_VER >= 1600
pragma execution_character_set("utf-8")
endif
插入对应msvc目录下的qglobal.h文件中即可解决。
来自博客 qt中文乱码解决方案
2022.4.14 路径取进程名 || 一些参数类型转换问题 ||QTimer 控件的问题
QTimer使用的问题
其实QTimer这个控件的使用方法和C#中的timer控件差别并不大,但是要注意,QTimer控件指向的方法一般是通过connect指向槽函数
我整晚遇到的问题就是,我的connect函数 触发的槽函数 没有在头文件中定义,它并不报错,但是触发器connect并不执行,这就是很傻逼的地方这次犯了,下次记得找回场子来
窗口模态打开的问题
qt中窗口有两种打开方式,一种是->show(),一种事->exec(),前者打开之后就无了,后者打开之后会锁定父窗口
其实在这里打开对话框之后就相当于事进入了一个while()循环,直到它关闭,为什么这么说呢?可以打个比方
int ret = dlgTableSize->exec(); //打开一个窗口,进入等到
if(ret == QDialog::Accepted) //如果点击窗口的确认,则触发此if
{
...
}
delete dlgTableSize; //用完记得清除窗口,直接delete就行,或者close
子窗口如何操作主窗口?
声明一个子窗口的方法:instance = new QWDialogLocate(this); //加一个this指针声明当前窗口是其子窗口的父窗口
可以通过一行语句获得父窗口对象:MainWindow parWind = (MainWindow)parentWidget();
这样就可以得到一个指向父窗口的指针
由路径取进程名:
/内存安全的版本/
char *get_filename(char *fullpath)
{
char *ptr = NULL;
int len = strlen(fullpath);
if (fullpath == NULL)
return NULL;
ptr = fullpath + len;
while (ptr != fullpath && *ptr != '\') // 注1
ptr--;
if (*ptr == '/')
ptr++;
return ptr;
}
注1:注意一个问题,这里的*ptr != '/' 其实是有点问题的,因为常规给定的路径不会是这样,而是\ 两种斜杠不同,当然这都取决于你输入的路径如何
C++参数类型转换
其实c++写的dll哪里还需要什么类型转换啊?,主要是说一下在dll里面的类型转换
我这个Firewall代码本身的代码其实不是纯c++,而是c#和c++的混合编程,也就是说其内部有一个带string类型的接口,在托管C++代码中将char*类型转换成 System::String 类型的方法如下:
String^ apppath = System::Runtime::InteropServices::Marshal::PtrToStringAnsi((IntPtr)strAppPath);
源代码中其实是有一个自带const wchar_t*指针的接口,但是这个接口的问题就是:我们不可能要求外部调用者也用这样的类型,所以必须得转置一下。
System::String 转制 const wchar_t*类型的方法 如下:
wchar_t* chrAppPath = (wchar_t*)(Marshal::StringToHGlobalUni(strAppPath)).ToPointer();
详情见项目Firewall.dll 带demo
2022.4.13 C#和QT链接之后的一些问题 || 关于链接库
C#和QT之间链接出现的一些问题
首先一个问题就是我在c#中使用的dataset类型,在c++中是不存在的,所以我如果是在c#中写的那些函数需要返回一个dataset的话,那这个方法其实是不可用捏。
然后说说托管的事,如果你想要从dll中引出一个类,那么你就需要在这个c++的项目中声明一个托管的类来承载这个类中的内容,需要用到ref关键字
比如:ref class Dataoperator 然后如果需要声明一个数值类型比如int bool等,则可以直接如System::Boolean blnToolBroadcastFlagA; 这样声明,如果你需要声明的是一个类,那么则需要在其后加上一个,如:System::String strPath;
另外注意,如果你是托管类中的声明,如果只有声明接口但是却没有写任何实现,则会报错LNK2020 无法解析xxx,这是个很坑的点,因为并没有很明确的写清楚错误原因。
格式转换的问题
现在我这个c#转c++/clr 已经成功了,现在要做的就是把c++打包成一个纯c++的dll,做法不难,但需要进行对接,这时候格式就需要进行转换。
就拿我之前写的防火墙距离,比如我现在有一个方法bool CFirewall::AddApplication(System::String^ strAppPath, System::String^ strAppName) 我现在需要将其打包成一个纯c++的接口,我该怎么做?
首先我们可以先写一个接口类,我就称其为Caller.cpp,在其中写一个对外的接口DLL_FIREWALL bool AddApp(char* strAppPath, char* strAppName) (DLL_FIREWALL 是一个宏,你知道是extern "C" _declspec (dllexport)就可以了)
DLL_FIREWALL bool AddApp(char* strAppPath, char* strAppName) //接收的参数是char*类型
{
System::String^ apppath = gcnew System::String(strAppPath); //将char*类型通过构造函数转换成System::String类型
System::String^ appname = gcnew System::String(strAppName);
CFirewall cfer;
bool isok = cfer.AddApplication(apppath, appname); //然后再调用其函数即可
if (isok) {
printf("应用已成功添加");
}
else {
printf("应用添加失败");
}
getchar();
return true;
}
c#转c++遇到的一些问题
之前我觉得可能c++中不好使用C#的类,但是其实我错了,还是能有好办法的只是我懂得太少了,现在我在这里简单写一下步骤和我踩过的坑。
1.首先需要一个c#的项目和c++的项目
注意这个c#的项目必须是类库,而且这个c++的项目必须是clr空项目(当然你要是那种普通的c++项目也行,但是那个/clr的设置会是你过不去的坎)。注意,这里让你建立的clr空项目并不是让你直接用的,因为它/clr并不是一个qt兼容的设置所以这个c++项目只能做一个中间态的兼容类,到时候转换成 c++的dll,再让qt去调用。
2.建好项目之后,需要修改两个设置
3.修改完两个设置之后,就可以开始写了,需要在头文件中输入
using "D:\Workshop\ClassLibrary1\Debug\ClassLibrary1.dll"
对其进行引用
在源文件中 使用其命名空间 如 using namespace LeventureDesign;
然后实例化一个类即可
LeventureDesign::PublicClass^ pc = gcnew LeventureDesign::PublicClass();
注意由于这个是托管类,需要^符号进行标注
4.使用
这个类应该就可以使用了,不管是静态方法还是类方法,都是可以用的了。
2022.4.12 .ui文件打开失败 || 文件操作 || 导入数据库功能
数据库功能
就在qt中使用数据库功能需要在.pro文件内添加一个QT+=sql的参数,但是我在vs里没有找到添加这个参数的接口,我一开始以为是默认添加的,其实不是,是在创建项目的时候就添加上了,如果后期想要加上,则需要添加参数,具体步骤:VS"Qt"菜单-"Qt Project Settings"-"Qt Modules"-勾选SQL library
.ui文件打开失败:
右键.ui文件->打开方式->添加.. ->找到msvc2015\Bin目录下的desinger.exe 确定->将其设为默认,再打开,就可以用了
文件操作:
写在前面,因为在qt中编码多数是有问题的,所以需要解决一下Unicode的识别问题,在main函数下写上:
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForLocale(codec);
一般再QT中操作文件需要先实例化一个QFile对象,比如我现在可以写一个方法:
bool QtFirst::openTextByIODevice(const QString & aFileName)
{
QFile aFile(aFileName);
if (!aFile.exists()) //假如文件不存在
{
return false;
}
if (!aFile.open(QIODevice::ReadOnly | QIODevice::Text)) { //如果打开方式不是只读或是文本文件
return false;
}
aFile.close();//关闭文件
return true;
}
放置一个按钮pushButton,点击后可以选择文件,并调用上面这个方法
void QtFirst::on_pushButton_clicked() { //注意,如果需要一个点击的槽函数,可以以on_控件名称_clicked()的形式写一个函数,这样就点击会触发该槽函数了
QString curPath = QDir::currentPath();
QString dlgTitle = "打开一个文件";
QString filter = "文本文件(*.txt *.docx *.doc);;所有文件(*.*)";
//通过这个getOpenFileName方法打开一个选择文件的窗口,输入的参数有父窗口指针this,窗口标题dlgtitle,一个默认目录,和一个筛选器,选择特定的文件格式
QString aFileName = QFileDialog::getOpenFileName(this, dlgTitle, curPath, filter);
if (aFileName.isEmpty()) {
return;
}
ui->textEditDevice->setPlainText(aFile.readAll()); //读取这个文件aFile,并将其中的内容写入这个textEdit中去
openTextByIODevice(aFileName);
}
以上就是打开一个文件的方法,主要是先通过getOpenFileName方法操作一个选择文件的窗体,然后返回一个文件路径,然后再操作其文件。
至于保存文件,则同样的,与上面的不同的是,打开文件的窗口控件变成了getSaveFileName,读取也变成了readall也变成了写入write
注意,写入时不能直接写入QString,要将QString转换成QByteArray,比如QString str = ui->textEdit->toPlainText(); QByteArray strBytes=str.toUtf8();
这样才能写入一个文件aFile.write(strBytes,strBytes.length());
2022.4.11 简单说下vs+qt的配置问题
说实在的,qt自带的那个creator倒也不是不能用,就是单纯的不好用,所以一般的来说,我们会把这个qt绑定在vs上使用,一个是vs的在作为编译器这方面远远比qt creator好用
首先说下qt的版本,我这里用的qt版本是5.10.1版本,vs在工作端用的是vs2015,在学生端采用的是vs2019,但是基本上差不了很远,这里简单说说2015端的配置方法
第一步,安装好qt和vs。可以确认一下是否可以新建项目什么的,确认无误之后可以开始进一步的操作
第二步,安装插件。如果是在vs2015,则可以在工具->扩展与更新->联机 中 搜索qt,第一个就是了
第三步,选择版本。在上方工具栏中出现了一个QT VS Tools,点击之后有一个QT Versions,进入之后进入Qt->Versions->add new Qt version 找到qt的安装目录,转到msvc2015->bin目录下的 qmake.exe
第四步,新建一个项目。现在应该已经可以新建一个项目了,但是还没法运行,因为会提示缺少库文件。这个时候就需要右键项目->属性->vc++目录->常规->包含目录 添加一个msvc2015\include
第五步,到这里应该差不多就可以了,但是还要注意一点,就是QT VS Tools->General->Qt/MSBuild->Path to Qt/MSBuild files 这条,默认可能是空,需要自己填,我这里填的是C:\Users\Administrator\AppData\Local\QtMsBuild,具体情况具体分析
2022.4.10
中间休了两天,实在是上班上的头都有点晕了。
从文件操作开始,到摄像头控件的使用
通常这个 receiver需要代表的是一个窗口类,而不是某个特定的控件
比如我们写一个函数,它的作用是让一个label的text变成正宗大肥猪
方法就这么写:
void MainWindow::textChange(){
ui->label->setText(" 正宗大肥猪 ");
}
而我们的connect就需要这么写:
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(textChange()));
就写在mainwindow.cpp中,当然直接卸载mainwindow.h中也是可以的
另外有个问题,就是这个qt连接vs,我现在还没搞明白,主要是创建了一个新的项目之后,没有哪个ui.h文件,我也不知道为什么
2022.4.8
从零开始,Qt开发
第一步,部署Qt环境
因为公司用的是vs,而我前面的老哥用的是qt5.10,所以我也用qt5.10
安装方案如下:(VS2015 - Qt5.10)[https://www.likecs.com/show-204623088.html]
我用的是戴工老哥的安装包,按照博客中的方法安装,插件没法很好地兼容,会提示有的文件不存在,所以我选择了使用另外一个插件,就可以使用了
🤗在qt中可以直接在ui设计界面设定一个connect
先说下什么是信号与槽,其实直白点说就是触发器和接收器,翻译问题让这两个令人有点难以理解
信号:Signal,槽:Slots
不知道谁翻译的,确实难懂
总之说下普遍的连接方法吧:
QObject:connect(sender,SIGAL(signal()),receiver,SLOT(slot());
这个QObject:: 可以省略,就是connect(sender,SIGAL(signal()),receiver,SLOT(slot());