QT基础学习


写在前面

本文主要是Qt常用控件与基础记录

常用控件

Qt程序组成有:应用程序类,窗口类
应用程序类有且仅有一个

基础学习

按钮(QPushButton等)

普通按钮

常用其父类QAbstrackButton的信号
新建一个按钮:

QPushButton(QWidget *parent = Q_NULLPTR)

QPushButton(const QString &text, QWidget *parent = Q_NULLPTR)

QPushButton(const QIcon &icon, const QString &text, QWidget *parent = Q_NULLPTR)
常用成员函数:
设置父类:setParent(QWidget *parent)
设置位置:move(int x, int y)
设置大小:resize(int w, int h)
设置文本:setText(const QString &text)
什么情况下不需要手动回收:

  • 从QObject类直接或间接派生
  • 直接或派生出的类,指定父对象,父对象析构时被释放

单选按钮(QRadioButton)

互相互斥,用groupBox将每组进行分组。

复选按钮(QCheckBox)

可进行多选
专属信号:QCheckBox::stateChanged();//显示

QToolButton按钮、

设置图片和图片大小
ui->last->setIcon(QIcon(":/Image/last.png"));
ui->last->setIconSize(QSize(200,30));

子窗口(QListWidget等)

QListWidget

可通过成员函数addItem()添加一行数据

//添加普通文本
ui->listWidget->addItem("状态栏");
//添加图标,方式1
ui->listWidget->addItem(new QListWidgetItem(QIcon(":/Fail/fail.png"),"失败"));
//添加图标,方式2()
QListWidgetItem *item = new QListWidgetItem(QIcon(":/Fail/affirm.png"), "affirm", ui->listWidget);

QTableWidget

需要先设定表的行列

//1.指定行
ui->tableWidget->setRowCount(50);
//2.指定列
ui->tableWidget->setColumnCount(2);
//设定表头,方式1
QStringList list;
list<<"姓名"<<"性别";
//QStringList list = QStringList()<<"姓名"<<"性别";
ui->tableWidget->setHorizontalHeaderLabels(list);

//设定表头,方式2
QStringList list;
list.push_back("姓名");
list.push_back("性别");
ui->tableWidget->setHorizontalHeaderLabels(list);

QGroupBox

将控件作为一组进行分组

QScrollArea

带滚动条的窗口,当控件过多窗口显示不下时使用

QToolBox

抽屉窗口,在每一页都能添加控件

QTableWidget

表型窗口,可切换窗口

QStackWidget

栈窗口,需要通过其他控件来实现窗口的切换

//通过按钮切换窗口
connect(ui->musicButton,&QPushButton::clicked,ui->stackedWidget,[=](){
    ui->stackedWidget->setCurrentIndex(0);});
connect(ui->moiveButton,&QPushButton::clicked,ui->stackedWidget,[=](){
    ui->stackedWidget->setCurrentIndex(1);});
connect(ui->dataButton,&QPushButton::clicked,ui->stackedWidget,[=](){
    ui->stackedWidget->setCurrentIndex(2);});

文本编辑框(QLineEdit,QTextEdit,QPlainTextEdit)

区别:

  • QLineEdit只能显示单行内容
  • QTextEdit可显示多行内容
  • QPlainTextEdit可显示多行,但是只能显示文本

新建一个文本编辑框:

QTextEdit(QWidget *parent = Q_NULLPTR)

QTextEdit(const QString &text, QWidget *parent = Q_NULLPTR)
常用信号

##标签(QLabel)
可显示文字,图片,动态图片等

//设置图片
ui->testLabel->setPixmap(QPixmap(":/Fail/cancel.png"));
//设置动态图片,只能播放gif类型,不能播放mp4等格式
QMovie *movie = new QMovie(":my/mario.gif");
ui->testLabel->setMovie(movie);
movie->start();//启动播放

文件对话框(QFileDialog)

常用其静态成员函数(Static public members)

  • 获得文件名:getOpenFileName
    #信号与槽
    connect(&对象1,&信号函数,&对象2,&槽函数)
    lamda表达式 ={}
  • ps:信号可以连接信号
    ##自定义信号
    1.void
    2.信号可以重载,重载之后需要通过函数指针访问
    3.需要关键字:signals
    4.不需要函数定义,只需要声明
    ##自定义槽
    1.void
    2.信号可以重载,重载之后需要通过函数指针访问
    3.一个信号可以链接多个槽
    4.槽函数的参数必须来自信号,即槽函数的参数只能少于等于信号的参数个数
    函数指针:函数返回值类型 (* 指针变量名) (函数参数列表);void (QPushButton::*p)(int, int)

##QMainWindow
1.菜单栏
2.工具栏
3.浮动窗口
4.状态栏
菜单栏(QMenu):可为menu添加动作,动作为QAction类

  • QAction类常用信号:&QAction::triggered
    工具栏:菜单栏有的才能设置在工具栏中,将动作从ui界面往上拖动即可
  • 在属性的QToolBar设置是否可以拖拽和停靠

浮动窗口(QDockWidget):在ui界面直接拖动添加,可在浮动窗口内添加控件
状态栏(statusBar):用addWidget()添加状态(常用label)

ui->statusBar->addWidget(new QLabel("ok"));

添加图片

为菜单栏添加图片

  • 添加资源文件:添加新文件-Qt-Qt Resourse File
  • 添加前缀,添加文件
  • 为对应控件添加图片,使用setIcon()
ui->action_as->setIcon(QIcon(":添加的前缀+文件路径"));

对话框(QDialog)

  • 非模态对话框:可以对其他窗口进行操作

  • 模态对话框:不可以对其他窗口进行操作

    QDialog dlg(this);//指定父窗口,也可不指定父窗口,不指定时为独立窗口
    //显示非模态对话框
    dlg.show();
    //显示模态对话框
    dlg.exec();//运行时,程序会阻塞,停在此处,要关闭对话框后才会继续执行
    

    要使非模态对话框一闪而过,new一个对话框,防止随着父类一起被析构

    QDialog *dlg = new QDialog(this);
    dlg->show();
    //设置对话框属性
    dlg->setAttribute()
    
    消息对话框(QMessageBox)

    常用其静态成员函数(Static public members)
    静态成员函数(可根据返回值来设置按下不同按钮的功能):
    QMessageBox::about();
    QMessageBox::critical();//错误对话框
    QMessageBox::warning();//警告对话框
    QMessageBox::question();
    QMessageBox::information();

    颜色与字体对话框

    颜色对话框(QColorDialog):常用其静态成员函数(Static public members)
    字体对话框(QFontDialog):常用其静态成员函数(Static public members)
    ps.打印QString类型:font.family().toUtf8().data()

布局

inf: 每个控件都应该进行布局,否则容易被遮挡,显示不出来或显示不全
技巧:布局可以先添加副窗口,然后再将副窗口内进行布局。
添加弹簧可以让窗口发生拉伸时,相对位置不发生变化,还可以设置弹簧为fixed类型,然后设置为固定宽度。

自定义控件

对于使用率较高的功能,可以封装为一个控件(类)。

  1. add File-Qt-Qt设计师界面类,新建一个自定义类,在该类的ui界面进行编辑。
  2. 在主窗口添加一个空Widget,再在新建的空Widget内对该窗口进行提升,提升为自定义的类,运行即可实现自定义控件
  3. 在新建的自定义类中对各控件进行链接
  4. 再在自定义类中添加对应的接口函数(常见的如SetValue()和GetValue())
    ps.重载的信号与槽函数,要通过函数指针类访问
//将滑动条与旋钮控件链接起来
int min =0;
int max = 1000;
ui->horizontalSlider->setRange(min,max);
ui->spinBox->setRange(min,max);
void(QSpinBox::*SigValueChange)(int) = &QSpinBox::valueChanged;
connect(ui->horizontalSlider, &QSlider::valueChanged, ui->spinBox, &QSpinBox::setValue);
connect(ui->spinBox,SigValueChange,ui->horizontalSlider,&QSlider::setValue);

事件

鼠标键盘事件在继承的虚函数中(Reimplemented Protected Functions)
新建一个c++类,并继承对应的控件类,之后将对应的控件提升为新建的C++类,之后即可在新建的C++类中对该控件进行事件处理。

鼠标事件

//继承自Widget类.h文件中声明
protected:
//鼠标进入
void enterEvent(QEvent *);
//鼠标离开
void leaveEvent(QEvent *);
//鼠标按下
void mousePressEvent(QMouseEvent *ev);
//鼠标释放
 void mouseReleaseEvent(QMouseEvent *ev);
//鼠标移动
void mouseMoveEvent(QMouseEvent *ev);
//定时器
void timerEvent(QTimerEvent *);

/**************实现**********************/
//.cpp文件中的实现
void MyLabel::enterEvent(QEvent *)
{
    setText("进来了");
}
void MyLabel::leaveEvent(QEvent *)
{
    setText("你离开了");
}
//鼠标按下
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
  //"%1,%2,%3"为占位符,对应后面的第几个arg
  if(ev->button() == Qt::RightButton){
      QString str = QString("mouseLeftPres(%1,%2)").arg(ev->x()).arg(ev->y());
      setText(str);
  }
}
//鼠标移动,持续状态使用buttons()函数,
void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
    QString btn;
    if(ev->buttons() & Qt::RightButton){
        btn = "RightButton";
    }
    else if(ev->buttons() & Qt::LeftButton)
    {
        btn = "LeftButton";
    }
    else if(ev->buttons() & Qt::MidButton)
    {
        btn = "MidButton";
    }
    QString str = QString("%3:(%1,%2)").arg(ev->x()).arg(ev->y()).arg(btn);
    setText(str);
}

如果需要实时检测鼠标事件(默认不实时追踪),则需要在窗口构造函数中,设置窗口实时追踪鼠标事件
this->setMouseTracking(true);

定时器

声明如上所示

//方法一
//启动定时器,每过xx时间则加1,如需要使用两个定时器则启动两个定时器即可,通过id来访问不同定时器
//参数1:定时器的时间,单位ms
//参数2:默认
//返回值:定时器id
id = startTimer(100);
//关闭定时器
killTimer(id);

//方法二,常用
//包含QTimer头文件
QTimer *timer = new QTimer(this);
timer->start(100);//每100ms触发一次信号
connect(timer, &QTimer::timeout,this,[=](){
  static int num = 0;
  this->setText(QString::number(num++));
});

定时器常需要使用静态局部变量 static int num=10;静态局部变量在该函数被释放时,不会被释放,直到程序运行结束以后才释放。

  • 问题:定义的static变量提示未定义。
  • 原因:因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。
  • 解决方法:需要在类外添加初始化
//.h声明了一个static bool status时
bool Widget::status = true;
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
    {
        ui->setupUi(this);
    }

事件分发器(event())

  • 虚函数
  • 常用功能:
    过滤事件
QObject::event(QEvent *e);//return true则认为已经执行不继续往下运行
//示例,让原来的鼠标按下事件不操作
if(e->enent()==QEvent::MouseButtonPress)
    return true;

事件过滤器(eventFilter)

  • 虚函数
  • 使用方法:
  1. 为需要过滤的窗口安装过滤器 ui->mylabel->installEventFilter(this);//
  2. 在事件过滤器函数中进行处理

绘图类和绘图设备(QPainter)

  • QPainter->QPainEngine->QPainDevice
    画家->引擎(不影响使用)->绘图设备
    QPixmap,QImage(图片);QPicture()

画家(QPainter)

重写虚函数 void QWidget::paintEvent(QPaintEvent *);
函数的特点:

  • 回调函数
  • 此函数不需要用户与调用,再刷新得时候会自行调用
    1. 窗口显示时;
    1. 最大化,最小化
    1. 窗口被遮挡,重新显示得时候
    1. 强制刷新得时候
  • 如果要使用画家类对窗口中进行画图,操作必须在paintEvent函数中完成
void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);//this为给画家painter设置的绘图平台
    //painter.load("D:/sunny.png"); //加载图片
    painter.drawPixmap(10,10,QPixmap(":/Fail/affirm.png"));
}
  1. 画笔类(QPen)
    • 创建画笔 (QPen pen;)
    • 设置画笔属性(颜色,笔的粗细,)
    • 画家拿笔(painter.setPen(pen);)
  2. 刷子(QBrush)
    • 可以填充,包括填充图片pixmap,文字等

实时刷新窗口

updata();//推荐使用,原理:调用updata就会运行了paintEvent()
repaint();

绘图设备

QPixmap,QImage,QPicture,QBitmap
即画布,各画布的特点:

  • QPixmap:主要用来显示,依赖平台(加载到内存中格式会变),只能用于UI线程
  • QImage:不依赖平台,可在多线程中操作
  • QBitmap:黑白图片显示,参考QPixmap
//QPixmap
QPixmap pix(300,300);//纸张大小
QPainter painter(&pix);//新建画家
/***可以新建画笔进行设置***/
painter.drawRect(10,10,200,230);//画图
pix.save("D:/myPixmap.jpg");//保存图像
//QImage
QImage img(200,200,QImage::Format_RGB32);
//更改绘图设备,因为之前画笔在pix上,所以要重新指定绘图设备
painter.begin(&img);
/**/
painter.end();

不规则窗口

this->setWindowFlags(Qt::FramelessWindowHint)//去窗口边框

#文件

文件读取和写入(QFile)

操作:

  1. 创建文件对象 QFile file(filename);
  2. 指定打开方式 file.open(QFile::ReadOnly);
  3. 读文件内容 QByteArray context = file.readAll();//read(),readline()
QString fileDir = "E:/Project/qt/lesson6";
static QString fileName;
connect(ui->pushButton,&QPushButton::clicked,this,[=]()
{
    fileName = QFileDialog::getOpenFileName(this, "Open File", fileDir);
    ui->lineEdit->setText(fileName);
    //新建文件对象
    QFile file(fileName);
    //设置文件打开方式
    bool readOk = file.open(QIODevice::ReadOnly);
    if(!readOk)
    {
        QMessageBox::critical(this, "Error", "文件打开失败");
    }
    //读文件
    QByteArray context = file.readAll();//返回为utf8格式
    QByteArray context += file.readLine();
    ui->textEdit->setText(context);
    file.close();
});
//写文件
connect(ui->writePushButton,&QPushButton::clicked,this,[=]()
{
    QString newContext = ui->textEdit->toPlainText();
    QFile file(fileName);
    bool writeOk = file.open(QIODevice::WriteOnly);
    if(!writeOk)
    {
        QMessageBox::critical(this, "Error", "文件写入失败");
    }
    file.write(newContext.toUtf8());
    //file.write()
});

文件流

  • QTextStream //操作数据类型:int float string
  • QDataStream //QImage QPoint QRect ;不依赖平台,应用于大量数据传输
QTextStream myStream(&file);    //将文件对象的地址传给流
myStream.setCodec("utf8");      //设置流的编码格式为utf8        
while(false == myStream.atEnd())
{
    QString context = myStream.readAll();
    ui->textEdit->setText(context);
}

//写文件
myStream<<QString("通过流写入的字")<<1234;

QDataStream用法一样,而且可以对内存进行操作

//对内存进行读写
QImage img("D:/test.png");
QByteArray buf;
QDataStream dataStream(&buf, QIODevice::ReadWrite);
dataStream<<img;//将图像写入内存空间

文件属性

QFileInfo info(“D:/lucy.png”);
成员函数:info.size();info.path();…

#include 
//打印最后修改时间要包含头文件和按输出的格式写好
info.lastModified().toString("yyyy-MM-dd hh:mm:ss");

通信协议(TCP,UDP)

##TCP通信
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hev9Ndpp-1626187970133)( https://i.loli.net/2021/05/04/6Q3h5sLUGl7f1xI.jpg)]

QTcpServer类(需要工程包含network)
QTcpSocket类
使用

//.pro
QT       += network
//main函数
Client c;
c.setWindowTitle("客户端");
c.show();
Server w;
w.setWindowTitle("服务器");
w.show();
//服务端
/*服务端需要一个监听对象指针和一个链接指针,首先新建服务端对象,再监听,如果有链接则会收到信号,然后通过链接指针接收请求,之后就可以通过链接指针进行发送和接收数据了(write和read)*/
ui->ip->setText("127.0.0.1");
ui->port->setText("6000");
//实列化
server = new QTcpServer(this);
//监听
server->listen(QHostAddress(ui->ip->text()), 6000);
//链接
connect(server,&QTcpServer::newConnection,this,[=]()
{
    //接收客户端的套接字
    ui->recordTextEdit->setText("有新的链接....");
    conn = server->nextPendingConnection();
    //发送数据
    //conn->write("服务器发的数据");
    ui->recordTextEdit->setText("有新的链接....");
    //接收数据
    connect(conn,&QTcpSocket::readyRead,this,[=]()
    {
        QByteArray array = conn->readAll();
        ui->recordTextEdit->append("客户端me:"+array);
    });

});
//发送消息
connect(ui->sendButton,&QPushButton::clicked,this,[=]()
{
    conn->write(ui->textEdit->toPlainText().toUtf8());
    ui->recordTextEdit->append("Host:" + ui->textEdit->toPlainText().toUtf8());
    ui->textEdit->clear();
});
//客户端
/*客户端只需要链接指针,首先实列化链接指针,然后链接上服务器,即可进行文件读写*/
ui->ip->setText("127.0.0.1");
ui->port->setText("6000");
client = new QTcpSocket(this);
//链接服务器
client->connectToHost(QHostAddress(ui->ip->text()), 6000);
//ui->recordTextEdit->setText("链接上服务器");

//接收数据
connect(client,&QTcpSocket::readyRead,this,[=]()
{
    QByteArray array = client->readAll();
    ui->recordTextEdit->append("Host:" + array);
});
//发送数据
connect(ui->sendButton,&QPushButton::clicked,this,[=]()
{
    client->write(ui->textEdit->toPlainText().toUtf8());
    ui->recordTextEdit->append("me:" + ui->textEdit->toPlainText());
    ui->textEdit->clear();
});

! 192.0.0.1无法设置为端口,应该是被占用了

UDP

无客户端和服务器概念,直接发信息到端口即可
QUdpSocket类
服务端和客户端都要进行:

  • bind套接字,并设置ip和端口
  • 发数据writeDatagram和接收数据readDatagram
//服务器端
ui->sIp->setText("127.0.0.9");
ui->cPort->setText("2222");
ui->sPort->setText("3333");
udp = new QUdpSocket(this);
//链接
if(udp->bind(ui->cPort->text().toInt()))
{
    ui->record->setPlainText("链接成功,服务器端口为:"+ui->sPort->text().toUtf8());
}
//send msg to server
connect(ui->send,&QPushButton::clicked,this,[=]()
{
    udp->writeDatagram(ui->message->toPlainText().toUtf8(),QHostAddress(ui->sIp->text()),ui->sPort->text().toInt());
});

//recieve msg from server
connect(udp, &QUdpSocket::readyRead, this, [=]()
{
    char data[4096];
    qint64 size = udp->pendingDatagramSize();
    udp->readDatagram(data, size);
    ui->record->appendPlainText(data);
});
//客户端只需要将bind的端口换为客户端端口,发送数据的端口换成服务器端口即可

?发送大文件采用udp怎么分配内存,实现udp传输一个视频
disconnectFromHost

线程

多线程

继承QThread类
在程序处理过程中有个复杂过程,假设sleep()了,如果只有一个线程则程序直接死了,所以要采用多线程,默认线程为主线程(或称ui线程)

4.7版本之前多线程操作

  1. 重写一个类,继承QThread(在进行操作时,新建一个继承QObject的类,然后改为继承QThread)
  2. 在类内重写线程处理虚函数run()——又称入口函数,run函数处理这个线程的工作
  3. 重写run()函数后调用自定义信号sigDoen
  4. 在主线程可以通过实列化后的myThread的对象调用start()函数来启动子线程,也可通过对实列化后线程对象的信号来实现传参。
    !只有run函数是在新线程里面,如果新建的类里有其他函数,他们与ui线程下的其他函数是出于一个线程的

4.7版本后能使用,较灵活

继承QObject类

  1. 写一个继承QObject的类,将需要进行复杂操作的入口函数声明为槽函数,在主线程中new一个对象objThread,不指定父对象,因为有父对象后不能move
  2. 声明一个QThread对象newThread
  3. 通过moveToThread方法把新建的objThread对象转移到新线程objThread.moveToThread(newThread)
  4. 把线程的finished信号和obj的deleteLater槽函数连接,必须连接不然会内存泄漏
  5. 正常连接其他信号与槽(在连接之前调用moveToThread)
  6. 调用QThread::start()启动线程
  7. 处理完逻辑,调用QThread::quit()退出事件循环,一般会调用QThread::wait()函数等线程结束,再释放内存。
    (个人理解:新建一个object类写好子线程功能,然后创建对象,然后将该对象move到一个新建的线程Qthread对象中,之后启动新建的线程(只启动了线程,未调用函数功能),再通过自定义信号和槽的方式调用函数功能,最后通过quit()退出新建的进程)
    **怎么理解基于QObiect类的多线程:
    将一个普通obj对象move到一个子线程中,之后直接链接主线程的信号,即可实现对obj中的
    子线程槽函数的处理,并在槽函数处理完后发出一个子线程处理结束信号,通过子线程处理结束信号,让子线程对象退出,并在子线程对象退出后删除obj对象
connect(myThread,&QThread::finished,obj,&QObject::deleteLater);//子线程处理结束后,删除obj对象,因为obj没有继承窗口,不手动删除会导致内存泄漏
connect(ui->begin,&QPushButton::clicked,obj,&objThread::somethingTodo);//通过主线程控制子线程中的复杂操作开始
connect(obj,&objThread::end,this,[=]()
{
    myThread->quit();
    myThread->wait();
});//子线程处理结束,退出子线程
myThread->start();//启动子线程

实战训练

1.计算器

  • 通过lamda表达式来调用槽函数OnClick(type _type, QString _btn)。type为按钮类型(数字,操作运算符,等于,清除,退格,小数点),_btn为按钮的数。
  • OnClick(type _type, QString _btn)通过switch来判断类型,处理对应逻辑。
  • 在进行显示时,用字符串操作,只有在进行运算时将字符串转为double,并在最后转回字符串
  • 显示栏实时显示textEdit->setText(num1 + op + num2)

需要用到的函数:data.chop(1) 删除字符串最后一位
链接:https://github.com/dawsoncaime/QT_EXAM

2.推箱子游戏

  • 放置按钮并将加载图片至按钮
  • 添加开始,重新开始等按钮控制逻辑
  • 绘制背景地图,并读取地图文件lv1.txt,通过QString和QStringList实现对地图文件的读取,并通过QPainter绘制。
      //void Widget::paintEvent(QPaintEvent *)
      QPainter painter(this);
      //绘制背景
      painter.drawPixmap(rect(), QPixmap(":/Image/ground.png"));
      ...
    
  • 处理人物移动逻辑,监听键盘事件
      //void Widget::keyPressEvent(QKeyEvent * event)
      switch (event->key()) {
          case Qt::Key_W:{
              pos_next = pos + QPoint(0, -1);
              if(!pos_wall.contains(pos_next)){
                  pos = pos_next;
              }
              break;
          }
          ...
      }
    
  • 添加其他函数,如判断箱子是否进入洞里等
    扩展:考虑如何添加提示按钮,在玩家不知道如何走的时候告知玩家下一步走哪
    链接:https://github.com/dawsoncaime/QT_EXAM

发布程序

  1. 在开始菜单中找到Qt 5.3 for Desktop (MinGW 4.8 32 bit)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ssgdWztt-1626187970135)(https://i.loli.net/2021/07/13/SUFjnY964Hkzucy.jpg)]
  2. 将写好的工程以release的方式编译,会生成一个名字带release的文件夹在工程目录下
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7e2H9chL-1626187970136)(https://i.loli.net/2021/07/13/1DRI2UNrdLnvsTu.png)]
  3. 可以将其中的exe程序拷贝至其他位置,打开第一步中的Qt 5.3 for Desktop (MinGW 4.8 32 bit),运行windeployqt sokoban.exe
    其中sokoban.exe为你的程序名字
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DpJpZI4R-1626187970137)(https://i.loli.net/2021/07/13/wYThUq9o82edlau.png)]
    这样就配置好了对应的库文件

你可能感兴趣的:(C++,qt,c++)