文章目录
跟535一起从零开始学QT-安装与环境配置
QT?啥是QT?
Qt(/ˈkjuːt/,发音同“cute”[4][5][6])是一个跨平台的C++应用程序开发框架。广泛用于开发GUI程序,这种情况下又被称为部件工具箱。也可用于开发非GUI程序,比如控制台工具和服务器。Qt被用于OPIE、Skype、VLC media player、Adobe Photoshop Elements、VirtualBox与Mathematica[7]以及被Autodesk [8][9]、欧洲空间局[10]、梦工厂[11][12]、Google、HP[13]、KDE、卢卡斯影业[14]、西门子公司[15]、沃尔沃集团[16], 华特迪士尼动画制作公司[17]、三星集团[18]、飞利浦[19]、Panasonic [20]所使用。
它是Digia公司的产品。Qt使用标准的C++和特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏。通过语言绑定,其他的编程语言也可以使用Qt。
Qt是自由且开放源代码的软件,在GNU宽通用公共许可证(LGPL)条款下发布。所有版本都支持广泛的编译器,包括GCC的C++编译器和Visual Studio。
–摘自维基百科
通俗来讲,QT就是让我们的程序告别黑框框,变成一个真正的有脸面的程序。如此有用有排面的东西,该怎么去开始学习呢?
QT的安装
QT当然要从官网上安装了!QT官网:https://www.qt.io/cn
然后这里给一个简单的链接:QT下载:http://download.qt.io/archive/qt/
选择上,根据需要选择吧!不过这里我们要选择5.X,例如5.12。在5.12子界面,选择最高的版本。然后根据你的电脑系统进行选择。
比如你是windows,就选择那个windows-x86的版本即可。
QT安装的细节
最前面的是协议,读一下然后点已阅读就可以。
然后确定安装位置,注意最下面那个框框,意思是对已有文件后缀自动QT打开,通俗来讲,如果你选了,那么以后.cpp
,.h
这种文件默认打开方式都是QT Creator
下一步选择组件。Developer and Designer Tools这个建议选,有一些实用的工具。但是上面那个Qt 5.XX.X 一定要打开选择一个!,不然你就没有编译器,没法编译文件。这里以Qt 5.11.3举例(图源网络,侵权删~~,其实是忘了截~~)
这里上面的选择框中各名称含义:
- MinGW,编译器模块之一,建议选这个!
- UWP,编译器模块之一。
- MSVC,编译器模块之一。
- ARM和Android是用于Android平台的模块。
- Sources,QT的源码
- Qt Charts,二维画图模块
- Qt Data Visualization,三位画图模块。
- 其他都是拓展模块
怎么选?编译器模块至少选择一个,其他根据需要选择(任选)
下面的含义:
- Qt Creator,Qt自己的IDE。Debugger(调试用的)
- MinGW,MinGW编译工具链
- Strawberry,一个Perl语言工具。
怎么选?我选的是Qt Creator(包含Debugger)与MinGW。
QT安装完之后
当你下载完成,安装完成之后,你的开始菜单会多这些东西:
为啥这么多?这些都是啥?
- Qt Creator 4.11.2 这个是一个IDE(集成开发环境),可以认为是Qt官方给的写代码软件。
- 剩下的,每一个相同名的(例如Assistant 5.12.8)分为好几个C/C++的编译器,即MinGW与MSVC,编译器又分了好几个版本,按需选择?(我选择的是MSVC的最新版本)
如果你没有Kits,这边建议你重装QT。或者可以按照https://doc.qt.io/qtcreator/creator-targets.html,QT,Adding Kits查找手动安装Kits的方法。
*QT的VS环境搭建
为什么不用QT Creator?
非必选,Qt Creator挺好用的,VS如果你有自己配置过,坚持要用VS,可以按照以下步骤搭建。
- 安装插件:VS,拓展->管理拓展->联机->搜索:“Qt Visual Studio Tools”,然后下载(用梯子更丝滑)。下载完成后要重启VS才能安装的。
- 配置插件:VS,拓展->Qt VS Tools->Qt Options->Add,然后填写上你的版本,选择你的路径(注意,此处要选择到MSVC2017_64这个文件夹级别)
- 使用Qt:记得把对应的项目中“属性/链接器/输入”里面输入加上一个“shell32.lib”
然后我们就做好了QT的安装。可以根据Qt Creator上面的教程学习,同时更欢迎大家与我们535一起从零开始学QT!
Qt手册:https://wiki.qt.io/Main/zh
跟535一起从零开始学QT-我的第一个QT程序
本文将会做一个文本显示框,点击按钮之后会更改文本框中的内容。
如何简单地做一个QT项目
新建一个项目
文件-新建文件或项目。 或 Ctrl + N。
选择模板
直接选Qt Widgets Application. 右下角 choose
相关配置
Location:自定,存文件的地方
Build System: qmake,暂时不需要研究这东西
Details: 可以直接默认走起。然后各个的简单介绍在下图。
Translation: 多语言翻译,暂时没用,默认即可。
Kits: 你选择的编译系统,具体有MSVC、MinGW两种,选一个前面的图标是小电脑的即可,带叹号的是不可用。
Summary: 直接默认走即可。
文件结构
.pro: 这个是Qt编译器保存的项目信息,例如你的项目的文档目录之类的。如果要打开一个下载的Qt项目,只需要打开其相应的 .pro 文件即可。
Headers: 通常存放 .h 文件,文件内写相关类的声明,函数声明(但不实现),变量声明etc. 作用:简化函数的查找与结构的查找,避免打开一个类翻半天翻不到下一个函数。
Sources: 源文件,一般为 .cpp 文件。其中要有对应 .h 的 .cpp 文件,对应实现 .h 中的一些函数。main.cpp 一般包含 main() 函数,为程序执行的入口函数。
补,Sources 文件中的 include 关系:同名 .cpp 要包含 同名 .h 文件。例如mainwindow.cpp 中需要加 #include "mainwindow.h"
,注意要用双引号包含(双引号会优先搜索同目录下的文件,而尖括号搜索系统基础库)。如果想要在其他文件中用mainwindow的类与函数,需要#include "mianwindow.h"
。
Forms: Ui文件,也就是界面布局设计文件。如果你直接点它的话,会用Qt Designer打开一个页面文件,支持拖动设计。如果你用文本方式打开的话,会是xml文件。
补,Ui文件与对应的.h/.cpp文件引用方法: 每个ui界面文件都会有个名称,例如上述的mainwindow.ui,在需要用的时候要在 .cpp 文件中#include "ui_mainwindow.h"
,并且在对应 .h 文件中写
namespace Ui{
class MainWindow;
//这里名称对应你要用这个ui的类名,这个案例中是MainWindow
}
如果不这么写的话,你将无法正确绑定类与ui。原理这里不再细究(关系到.ui文件的编译过程)。
拖动设计Ui
点开ui文件,然后在这个页面(指左边侧边栏第三个,“设计”)显示
然后我们需要啥部件,只需要拖动到指定位置,然后用鼠标拉到合适大小即可。如果需要设计该部件的其他属性,可以找到图片的右下方,“属性”一栏,将其对应的值更改即可。
如果想要通过代码来达到更改的效果,一般每个可以更改的属性会对应一个
set....()
函数来更改,使用参数详见IDE中的补全提示。当然,也可以放在对应的函数上,按下’ ‘F2’ 寻找函数定义,详细了解该函数的声明与实现
这里,我们设计一个点击更改文本的界面。
第一步,把界面中的模块填充完整。
选中Line Edit,在属性一栏找到QWidget属性中font,该属性为显示字体的属性。调成你需要的(我这里调的为“楷体”,“20”号)。
然后对应调Label组件的属性。这里你会发现他们有相同的QObject,QWidget属性,原因是类的继承。找到 QLabel 属性中的 text,即该TextLabel默认显示的文本,这里改成“Hello World!”
选中PushButton,调字体大小,然后找到 QAbstractButton 属性中的 text,可以改变按钮上的字,这里我设置为“更改”。
最后的界面为这样的:
设计信号与槽
通俗来讲,信号是部件状态改变时,系统会调用的函数,槽为程序员定义,并且当系统调用该槽对应的信号函数时,自动调用的函数。
这里,我们需要实现的功能为:当按下 “更改” 时,将 LineEdit中的内容显示到Label中。我们需要设计 “更改” 的响应函数,使得当他被点击时,做出相应的动作。
最简单的创建方式
右键你所相应的模块,然后选择 “转到槽…” ,选择一个你所需要它相应的信号函数(点击的信号函数为clicked()与clicked(bool))。这时,系统就自动为你创建好了槽函数。这里我们选择创建一个clicked()的槽函数。
这里说明一下两个信号函数的区别。调用上,他们没有区别,即当“更改”按钮被点击时,两个信号函数都会被调用。参数传递上,他们有区别,后一个带一个参数,也就是
bool checked
,这个参数的值为当前按钮的状态。
然后会自动转到mainwindow.cpp文件里,你会发现多了一个函数
这个就是对应的槽函数。实现的功能为:当你点击那个“更改”按钮的时候,clicked()信号函数会调用,继而on_pushButton_clicked()会被系统联动调用。
*使用代码创建
这里只给出代码,不做深入讲解(可能下节讲)。
mainwindow.h在类中增添了
class MainWindow : public QMainWindow
{
//......
private slots:
void labelChangeText();
//......
}
mainwindow.cpp中增加了
MainWindow::MainWindow(QWidget *parent):
:QMainWindow(parent),ui(new Ui::MainWindow)
{
//...
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(labelChangeText()));
//...
}
注:有多种connect写法,这里只放一种。
设计槽
这里给出按照右键创建的方式的代码。
void MainWindow::on_pushButton_clicked()
{
QString qs = ui->lineEdit->text();
ui->label->setText(qs);
//等价于ui->label->setText(ui->lineEdit->text());
}
tips:如何知道ui->lineEdit->text()
的类型为QString?先把这个语句写出来,补全中会提示QString text() const
,那么你就获得了这个text()
的返回值QString
。
运行你的代码
左下角四个框框,如果你的有两个没亮(也就是点不了那个三角号),你需要点从上到下第一个图标去重新配置该项目的编译器(MSVC/MinGW)这样的。
然后三角号是按照你配置的编译器来编译。从上到下第三个带个甲壳虫的三角是以Debug模式编译(允许设置断点)。
And then,大功告成。
跟535一起从零开始学QT-信号与槽机制
QT中信号与槽机制简化了编程的过程。最简单的使用方式:右键点击操作部件,然后点击转到槽,就可以设计相关的槽函数了。
但是但是,这个过程是怎么来的呢?
这里就需要涉及到信号与槽了。
简单的引入
上述的这个过程如果用代码来写出,应该添加的代码为:
mainwindow.h
class MainWindow:public QMainWindow
{
//....
private:
void initSignalSlot();
private slots:
//这里的private与普通类里的private作用相同,都是限制只能内部访问。
void btnCalculate_clicked();
//命名规则,前面是部件的名称,后面是信号函数名
//....
}
mainwindow.cpp
//下面这个是构造函数
MainWindow::MainWindow(QWeidget *parent):
QMainWindow(parent),ui(new Ui::MainWindow)
{
ui->setupUi(this);
initSignalSlot();
//我们自己写的初始化 “信号与槽关系” 的函数
}
void MainWindow::initSignalSlot(){
connect(ui->btnCalculate,SIGNAL(clicked()),
this,SLOT(btnCalculate_clicked()));
//connect((指针类型)sender,SIGNAL(信号函数,参数用类型代替),(指针类型)reciever,SLOT(reciever的相应函数,参数用类型代替));
}
void MainWindow::btnCalculate_clicked(){
//.........你想要的响应
}
这里给出的connect函数形式为QT4语法,但是QT5兼容QT4,所以此处给出的这种可以在QT5里面写。
那么,如果你用右键转到槽的方式创建,你找不到这个connect函数,它内部实现原理是系统在编译的过程中,帮你创建了connect,规则是按照函数的名称。
虚拟的例子
案例:假设你遇到了一个陌生的异性,然后你亲了TA一下;这时候TA打了你一巴掌。
换成QT的信号与槽而讲,上述过程可以描述为如下的情况:
- 你发出信号:亲了TA一下
- TA做出相应:打了你一巴掌
这时候,假设你是变量you,TA是变量ta,亲是信号函数mua,打你一巴掌是槽函数hit。那么以下句子就可以完成该事件之间的连接
connect(&you,SIGNAL(mua()),&ta,SLOT(hit()));
创建信号函数与槽函数
-
槽函数
槽函数可以看作是类的一个普通成员函数,它也有
public, private, protected
这三种属性,对应的含义与成员函数相同。不过创建时需要使用public slots / private slots / protected slots
。以下是几种例子//mainwindow.h class MainWindow:public QMainWindow { //... public slots: void hit(); void publicSlot(); void publicSlot1(int x); void publicSlot2(int x,int y); private slots: void privateSlot(); protected slots: void protectedSlot(); //... }
不过槽函数除了定义之外,还需要在对应的
.cpp
文件中进行具体的实现。就类似于写那个函数。 -
信号函数
信号函数不是一个普通的成员函数,它不需要你进行具体实现,只需要定义即可。实现是QT编译的事情。定义一个信号函数,需要使用
signals
标识符,用法类似public
。而且,所有信号函数必须是void
类型。以下是一个例子//mainwindow.h class MainWindow:public QMainWindow { //... signals: void mua(); void mySiganl(); void mySignal1(int x); void mySignal2(int x,int y); //... }
将信号与槽关联起来
有很多方式,有建立关联,也有取消关联。常见的是使用connect, disconnect
这两个函数进行这项工作。由于取消关联并不是很必要,在结构体析构函数被调用的时候,所有东西就都没了,所以这里只给出如何建立关联。
正如最开始所引入的东西,connect
的最简单用法如下
connect((指针类型)sender,SIGNAL(信号函数,参数用类型代替),(指针类型)reciever,SLOT(reciever的相应函数,参数用类型代替));
这里需要注意的是,如果你想连接到定义的地方,需要使用this
指针。
以下实例表示我想从mua()
连接到hit()
的实现方式
//mainwindow.h
class MainWindow:public QMainWindow
{
//...
public:
MainWindow();
signals:
void mua();
void mua1(int x);
public slots:
void hit();
//...
}
//mainwindow.cpp
MainWindow::MainWindow(){
//...
connect(this,SIGNAL(mua()),this,SLOT(hit()));
connect(this,SIGNAL(mua1()),this,SLOT(hit()));
//...
}
void MainWindow::hit(){
bool have_been_hit = 0;
have_been_hit = 1;
return;
//随便写的
}
其中一个信号连接到多个槽是可以的。作用是:当这个信号被发送的时候,所有槽都会执行,但顺序不可控。
多个信号连接到一个槽也是可以的。作用是:任何一个信号产生都会调用这个槽函数。也就是并联的关系。
有参数的时候只需要写那个参数的类型,而不需要写具体的名。
调用信号函数、槽函数
由于槽函数QT说它可以像一般的成员函数一样,所以它的调用就不细讲了。需要用的时候直接hit()
就完事了。
信号函数:最标准的调用方法是emit mua()
。当你有参数的时候需要在括号内填上相应的参数,也就是emit mySignal1(2)
这样的。当然也可以用变量代替那个位置,就跟普通的传参一样。
信号与槽之间的参数传递
你看我们上面定义的那些信号函数和槽函数,它的参数是干啥的呢?
当我们使用这个参数的时候,需要有一个条件:connect
的槽的参数要小于等于对应的信号的参数。
- 信号参数与槽参数对等(数据类型和个数均相同,名字不用)。则槽函数的那些参数就是信号函数对应的那些。
- 信号参数多于槽参数,但从前往后看都对等。则槽函数会依次接受信号函数的前面的参数。
举个例子:
//mainwindow.h
class MainWindow:public QMainWindow
{
//...
signals:
void mua0();
void mua1(int x);
void mua2(int x,int y);
public slots:
void hit0();
void hit1(int x);
void hit2(int x,int y);
//...
}
下面开始分析其connect
关系与参数传递情况。假设以下写的位置都合适。
connect(this,SIGNAL(mua0()),this,SLOT(hit0()));
connect(this,SIGNAL(mua1(int)),this,SLOT(hit1(int)));
connect(this,SIGNAL(mua2(int,int)),this,SLOT(hit2(int,int)));
//上面三个对应传参,就是信号函数第一个参数进槽函数第一个参数,第二个进第二个参数
connect(this,SIGNAL(mua0()),this,SLOT(hit1(int)));
connect(this,SIGNAL(mua0()),this,SLOT(hit2(int,int)));
connect(this,SIGNAL(mua1(int)),this,SLOT(hit2(int,int)));
//上面都是不合适的,因为槽函数参数多于信号函数了。
connect(this,SIGNAL(mua1(int)),this,SLOT(hit0()));
connect(this,SIGNAL(mua2(int,int)),this,SLOT(hit0()));
connect(this,SIGNAL(mua2(int,int)),this,SLOT(hit1(int)));
//上面三个是合适的,前两个并不会接受到参数。第三个hit1会接受到mua2的第一个int参数。
拓展阅读
Qt之信号signals和槽slots详解:(https://blog.csdn.net/bruce_0712/article/details/53694374)
Qt信号与槽机制详解:(http://c.biancheng.net/view/1823.html)