一.前言
今天就要开始暑假短学期的实训了,本来课堂也要求记笔记,这里我就开始分享我将来五天的学习过程吧。
二.QT安装过程
首先,提供一下我的链接:
本来上传到我的阿里云盘了的,结果压缩包不给分享,抱歉啊,大家可以私信我,找我要安装包。
安装步骤:
-
1.在安装文件夹的界面,建议大家不要将 Qt 安装在系统盘 C 盘(比如我在此作了更改,将其安装到 D 盘。当然,你安装在 C 盘也是可以的),其他保持默认即可。继续点击“下一步”。
如果Skip(像我图中next的位置)按钮为灰色,请断开网络进行安装。
-
2.一直点下一步,并且选择安装路径。和平时安装大多数软件一样,不解释。
-
3.下面,就到了重点了。在选择组件界面,请务必要选对安装 Qt 所需要的组件,否则你安装以后是无法正常运行的。首先,点击各项前面的 > 箭头展开子项的内容。(以5.11为例)
选择完成后,继续点击“下一步”。
- 4.接着执行下一步,直到显示安装完成。
三.摸索软件
1.建立项目
我今天刚安装完成后,发现桌面竟然没有快捷方式,我懵逼了,查了点资料,终于在安装目录了找到了.exe可执行文件,开始今天的主题。
我的.exe文件在 D:
\QT\Tools\QtCreator\bin
里,名字叫qtcreator.exe
,这里注意你发现在你的安装目录下有很多.exe文件,只有叫qtcreator.exe
才是我们今天实验的主角。
软件打开就是中文的啦,这样我们感觉更熟悉。可以在“工具-选项”里修改环境、字体、亮度什么的,可以自己尝试。
-
1.右键文件,选择“新建文件或项目”。
-
2.在最左侧的列表框中单击“Application”,在图显示的对话框中选择项目类型为
Qt Widgets Application
后,单击“Choose…”按钮
这里中间栏有五个选项,上课老师也是直接说第一个,我查了一下资料:
- Qt Widgets Application,支持桌面平台的有图形用户界面(Graphic User Interface,GUI) 界面的应用程序。GUI 的设计完全基于 C++ 语言,采用 Qt 提供的一套 C++ 类库。
- Qt Console Application,控制台应用程序,无 GUI 界面,一般用于学习 C/C++ 语言,只需要简单的输入输出操作时可创建此类项目。
- Qt Quick Application,创建可部署的 Qt Quick 2 应用程序。Qt Quick 是 Qt 支持的一套 GUI 开发架构,其界面设计采用 QML 语言,程序架构采用 C++ 语言。利用 Qt Quick 可以设计非常炫的用户界面,一般用于移动设备或嵌入式设备上无边框的应用程序的设计。
- Qt Quick Controls 2 Application,创建基于 Qt Quick Controls 2 组件的可部署的 Qt Quick 2 应用程序。Qt Quick Controls 2 组件只有 Qt 5.7 及以后版本才有。
- Qt Canvas 3D Application,创建 Qt Canvas 3D QML 项目,也是基于 QML 语言的界面设计,支持 3D 画布。
查了这些也不太懂,这里我们还是按要求来选择1吧。
-
3.在弹出来的窗口设置项目名和项目路径,接下来选择编译器,我们刚刚只下载了一个,没得选。
-
4.在显示的界面中单击“Next”按钮,在此界面中选择需要创建界面的基类(base class)。有 3 种基类可以选择:
- QMainWindow 是主窗口类,主窗口具有主菜单栏、工具栏和状态栏,类似于一般的应用程序的主窗口;
- QWidget 是所有具有可视界面类的基类,选择 QWidget 创建的界面对各种界面组件都可以 支持;
- QDialog 是对话框类,可建立一个基于对话框的界面;
-
5.然后单击“Next”按钮,出现一个页面,总结了需要创建的文件和文件保存目录,单击“完成”按钮就可以完成项目的创建。
2.了解界面
在项目名称节点下面,分组管理着项目内的各种源文件,几个文件及分组分别为以下几项:
- Demo.pro 是项目管理文件,包括一些对项目的设置项。
- Headers 分组,该节点下是项目内的所有头文件(.h),项目有一个头文件 mainwindow.h,是主窗口类的头文件。
- Sources 分组:该节点下是项目内的所有 C++源文件(.cpp),项目有两个 C++ 源文件,mainwindow.cpp 是主窗口类的实现文件,与 mainwindow.h 文件对应。main.cpp 是主函数文件,也是应用程序的入口。
- Forms 分组:该节点下是项目内的所有界面文件(.ui)。图 5 中所示项目有一个界面文件mainwindow.ui,是主窗口的界面文件。界面文件是文本文件,使用 XML 语言描述界面的组成。
左侧上下两个子窗口的显示内容可以通过其上方的一个下拉列表框进行选择,可以选择的显示内容包括项目、打开文档、书签、文件系统、类视图、大纲等。上方的子窗口显示了项目的文件目录树,下方显示打开的文件列表。可以在下方选择显示类视图,这样下方则显示项目内所有的类的结构,便于程序浏览和快速切换到需要的代码位置。
3.项目的编译、调试和运行
单击主窗口左侧工具栏上的“项目”按钮,会出现项目编译设置界面。
界面左侧一栏的“Build & Run”下面显示了本项目中可用的编译器工具,要使用哪一个编译器用于项目编译,单击其名称即可,选择的编译器名称会用粗体字表示。这里选择使用 MinGW 32bit 编译器(我们也只有安装了这个)。
每个编译器又有 Build 和 Run 两个设置界面。在 Build 设置界面上,有一个“Shadow build” 复选框。如果勾选此项,编译后将在项目的同级目录下建立一个编译后的文件目录,目录名称包含编译器信息,这种方式一般用于使用不同编译器创建不同版本的可执行文件。如果不勾选此项,编译后将在项目的目录下建立“Debug”和“Release”子目录用于存放编译后的文件。
在设计完 mainwindow.ui 文件,并设置好编译工具之后,就可以对项目进行编译、调试或运行。主窗口左侧工具栏下方有 4 个按钮,其功能如下:
首先对项目进行编译,没有错误后,再运行程序。此时因为我们什么也没有,所有弹出来的窗口应该是个空白窗口。
四.程序框架
1.基础框架
如果我们在刚刚创建的项目过程中,不勾选创建界面选项,那么你的该项目在左侧的文件树目录中,应该没有forms文件夹下的.ui文件,这里看一下我的图:
你可以看出来左侧有两个项目文件,其中03caclu
文件是勾选了创建界面选项,04test
文件是没有勾选创建界面选项,所以少了.ui文件。我们看下面这个项目,.pro
文件指的是我们这个项目文件,就相当于最后的链接文件(注意他不是可执行文件),所以运行该项目还有一种方式,你可以直接选择04test
然后右键运行。
有其他语言基础,我们应该猜的到这个主文件应该是main.cpp
,我们就先看它。
2.main.cpp
文件
有其他语言基础,我们应该猜的到这个主文件应该是main.cpp
,我们就先看它。main.cpp 是主函数文件,内部主要包含应用程序的入口函数,也就是 main() 函数。
我们知道在C语言里(博主没学C++)里main函数的语法格式是固定的:
int main(int argc, char *argv[]){
//填充代码
return 0;
}
那么QT界面程序应该也有固定的格式:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//填充代码
return a.exec();
}
记住:使用 Qt 框架编写带界面的应用程序,main() 函数中必须包含第 3 行和第 5 行代码,否则程序无法正常运行。
但是我们打开我们main.cpp
文件,里面确是这样的:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
我们来注释一下这些代码:
-
1~2 行:由于 main() 函数中分别定义了 QApplication 和 MainWindow 类的对象,因此需要引入 mainwindows.h 和 QApplication 头文件。mainwindow.h 文件是我们自己创建的,引入时用
" "
双引号括起来;QApplication 是 Qt 提供给我们的,引入时用<>
括起来。 -
第 7 行:MainWindow 是自定义的类,继承自 QMainWindow 主窗口类,因此 MainWindow 也是一个主窗口类。w 是 MainWindow 类实例化出的对象,表示一个主窗口。(就是在你
Headers
下面的那个.h文件其实就是描述一个主窗口的头文件,然后再main.cpp里我们把它实例出来)
-
第 8 行:默认情况下,Qt 提供的所有组件(控件、部件)都是隐藏的,不会自动显示。通过调用 MainWindow 类提供的 show() 方法,w 窗口就可以在程序运行后显示出来。
3.mainwindow.h
和mainwindow.cpp
慢慢理解,刚开始我发现写的几个小项目,我们都几乎不会修改main.cpp
文件,全部是在这两个文件里操作。
创建项目时,我们在图所示的对话框中定义了一个继承自 QMainWindow 的主窗口类,并起名为 MianWindow,该类的定义部分位于 mainwindow.h 头文件中,实现部分位于 mainwindow.cpp 源文件中。
把mainwindow.h
打卡应该是这样的:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
};
#endif // MAINWINDOW_H
把mainwindow.cpp
打开应该是这样的:
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
}
MainWindow::~MainWindow()
{
}
初始状态下,MainWindow 类由 Q_OBJECT、构造函数和析构函数组成,这里重点介绍一下 Q_OBJECT 和构造函数:
- Q_OBJECT:本质是一个已定义好的宏,所有需要“信号和槽”功能的组件都必须将 Q_OBJECT 作为 private 属性成员引入到类中。本节设计的界面程序不会用到“信号和槽”,因此可以删除 Q_OBJECT。
- 带参的构造函数:QWidget 是所有组件的基类,借助 parent 指针,可以为当前窗口指定父窗口。例如图 1 中,QLabel 文本框位于主窗口中,主窗口就是它的父窗口。当父窗口被删除时,所有子窗口也会随之一起删除。当然也可以不指定父窗口,那么当前窗口就会作为一个独立的窗口,不会受到其它窗口的影响。
接下来解释一下这一块:
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
构造函数和析构函数是C++中特殊的成员函数,用于在对象的创建和销毁过程中执行一些操作。
构造函数是在对象被创建时自动调用的函数。构造函数的主要作用是完成对象的初始化,包括对成员变量进行赋值、分配内存等操作。每个类都至少有一个构造函数,如果没有显式定义,则会有一个默认构造函数。
析构函数是在对象被销毁时自动调用的函数。析构函数的主要作用是释放对象所占用的资源,例如释放动态分配的内存、关闭文件等。每个类都至少有一个析构函数,如果没有显式定义,则会有一个默认析构函数。
构造函数和析构函数的命名与类名相同,但在前面加上了一个~号来表示析构函数。它们不需要返回类型,也不能被重载。
在Q_OBJECT宏下,构造函数与析构函数只能用public声明。
再看这一行:
class MainWindow : public QMainWindow
这是一个C++类的声明,采用了类的继承。其中MainWindow
是该类的类名,冒号后面的部分public QMainWindow
表示该类公开继承自QMainWindow
类。在C++中,类可以通过继承来拓展其功能,继承自其他类的类称为派生类。在这种情况下,MainWindow
是一个派生类,它继承并拓展了QMainWindow
类的功能。关于访问控制符public
,它表示从父类继承的所有公有成员在子类中仍然是公有的,可以被其他代码访问。而私有和保护成员则不能被其他代码直接访问,只能在类内部使用。因此,在这里,MainWindow
类对外暴露与QMainWindow
类相同的公有接口,使得其他代码可以像使用QMainWindow
一样使用MainWindow
。
没学过C++,靠着仅存的C语言知识来理解。
另外还需要补充一个知识点:
在 C/C++ 代码中,当我们需要使用某个头文件时,我们可以使用 #include
指令将它包含进来。但是,在大型项目中,可能会有多个源文件需要包含同一个头文件,如果不加控制,就容易出现头文件被重复包含的问题,这样会导致编译错误,影响程序的正常运行。
为了避免这个问题,我们可以在头文件开头加上以下代码:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
// 头文件内容
#endif // MAINWINDOW_H
其中,#ifndef
检查 MAINWINDOW_H
是否已经被定义过了,如果没有,则执行下面的 #define
将其定义为宏。这个宏可以理解为一个标记,用来标志这个头文件是否已经被包含进来了。
接着,我们在头文件的最后添加 #endif
,表示条件编译结束。
这样,当编译器处理到另外一个需要包含这个头文件的源文件时,由于 MAINWINDOW_H
已经被定义为宏了,再次包含头文件时就会被忽略掉。这样就可以避免头文件被重复包含的问题。
4.mainwindow.cpp
理解
先看这一句:
MainWindow::~MainWindow()
{
}
我们现在已经知道~MainWindow
是析构函数,而前面两个冒号又是什么呢?它的作用是什么,前面右面一般是什么?这两个冒号是作用域解析运算符,通常用于访问类的成员函数和静态成员变量。在这里,MainWindow::~MainWindow()
表示 MainWindow 类的析构函数。
析构函数是一个类的特殊函数,其名称以波浪线 ~
开头,后面跟着类名,用于在对象被销毁时释放资源。析构函数没有返回值,也不能有参数。如果不显式定义析构函数,编译器会自动生成一个默认析构函数。
在 C++ 中,成员函数可以是静态或非静态的。对于非静态成员函数,需要通过对象来调用;而对于静态成员函数,则可以使用作用域解析运算符来直接调用,如 ClassName::functionName()
。在这里,~MainWindow()
是某个类 MainWindow
的析构函数,因此使用了作用域解析运算符。
需要注意的是,在类定义中声明的成员函数默认情况下都是非静态的。在实现文件中定义成员函数时,需要指定该函数属于哪个类,因此需要使用作用域解析运算符来表示从属关系,如 MainWindow::~MainWindow()
。
再来看这一句:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
}
哪怕不看下文,我们搭配这里应该也猜得到,这应该是定义MainWindow
的构造函数,只是后面那一部分是什么不知道,()里面应该可以往函数参数方面去猜。这里我们先了解两个基类的关系,QWidget
和 QMainWindow
是两种不同的 Qt 类,它们都是 Qt 库中的基类。
QWidget
类是一个基本的用户界面元素类,用于创建和管理窗口、按钮、标签、编辑框等控件。QMainWindow
类则是一个特殊的窗口类,用于创建主窗口,并提供了一些常见操作和布局方式。因为 QMainWindow
继承自 QWidget
,所以可以使用 QWidget
的所有功能,同时还有自己特有的一些功能。
在 Qt 中,每个窗口都必须要有一个父窗口。通常情况下,子窗口会嵌套在父窗口内,而父窗口就是主窗口。因此,在创建 Qt 应用程序时,通常先创建一个 QMainWindow
对象作为主窗口,然后在该窗口上添加其他控件和子窗口。
需要注意的是,QWidget
和 QMainWindow
都是抽象类,不能直接实例化。通常情况下,我们需要继承这些类并重写其中的一些虚函数来实现自己的功能。在继承之前,需要先将对应的头文件包含进来,如 #include <QWidget>
或者 #include <QMainWindow>
。
他们两个都是一个基类(你创建项目时应该看到过),但是
QMainWindow
继承自QWidget
,所以可以使用QWidget
的所有功能,同时还有自己特有的一些功能。这才是他们两的关系。
了解这个之后再看这句代码,这是 MainWindow
类的构造函数的实现。
- 函数名:
MainWindow::MainWindow
是MainWindow
类的构造函数,用于在创建对象时对其进行初始化。 - 参数:
(QWidget *parent)
表示构造函数有一个 QWidget 类型的指针参数,该参数表示父窗口的指针,默认值为nullptr
。 - 初始化列表:
QMainWindow(parent)
表示调用基类QMainWindow
的构造函数,并将参数parent
传递给它。该语句通过初始化列表来完成,在构造函数体之前执行。 - 构造函数体:大括号内的代码块是构造函数体,用于完成其他初始化工作或执行其他操作。
总体来说,该构造函数的功能是调用基类的构造函数进行初始化,并完成其他初始化工作。由于 Qt 中的窗口类通常都是继承自 QWidget
或者 QMainWindow
,所以在构造函数中需要调用基类的构造函数来进行初始化。
这个单独的冒号和紧随其后的内容是构造函数的初始化列表(initializer list),用于对类成员变量进行初始化。
在 C++ 中,成员变量通常需要在构造函数中进行初始化。为了方便起见,C++ 提供了初始化列表的语法来完成这个工作。初始化列表由冒号 :
开始,后面跟着逗号分隔的成员变量名和它们的初始化值。每个成员变量的初始化值都用一个圆括号括起来,并用逗号分隔。
在这里,QMainWindow(parent)
就是初始化列表的一部分。它表示调用基类 QMainWindow
的构造函数,并将参数 parent
传递给它,用于进行初始化。由于 Qt 中的窗口类通常都是继承自 QWidget
或者 QMainWindow
,所以需要在构造函数中调用基类的构造函数来进行初始化。
五.实现简单计算器
接下里,开始我们的第一个程序------实现简单计算器。
1.使用.ui程序
QT程序在我们使用.ui文件后,有很大程度上帮我们解决一部分问题,其中原因就是可以在.ui文件里进行拖动,节省了我们定义组件的时间。这里我们新建一个项目后,在.ui文件里,手动拖成如下画面:
这里简单介绍以下,第一个、第三个、第五个控件是lineEdit
,中文名字叫文本编辑行,而第二个控件是comboBox
,中文名字叫复选框,第四个控件是pushButton
,中文名字叫按钮。这些都可以找左边的工具栏可以找到的,自己摸索以下应该就行了。
接下来,打开我们的.h文件,此时应该是这样的:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
main.cpp
文件我们暂时不管,在另一个.cpp文件里,输入以下代码:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
//右键=按钮转到槽,选择clicked()信号,功能:计算器
void MainWindow::on_pushButton_clicked()
{
double a = ui->lineEdit->text().toDouble();
double b = ui->lineEdit_2->text().toDouble();
double c;
int index = ui->comboBox->currentIndex();
if(index==0)
c=a+b;
else if(index==1)
c=a-b;
else if(index==2)
c=a*b;
else
c=a/b;
ui->lineEdit_3->setText(QString::number(c)); //double--->string
}
接下来我们可以开始运行这个项目了。
运行结果是:
2.不使用.ui文件
新建一个项目,基类选择QWidget,这样防止我们使用了QMainWindow,然后在.h文件里输入下面代码:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtWidgets> //1.所有部件的头文件
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private slots:
void caculator(); //自定义的槽函数声明
private :
QPushButton *pushButton;
QComboBox *comboBox;
QLineEdit *lineEdit1;
QLineEdit *lineEdit2;
QLineEdit *lineEdit3;
};
#endif // WIDGET_H
在widget.cpp输入下面代码:
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//构造函数:1.界面初始化 2.信号与槽
pushButton = new QPushButton(this);
comboBox = new QComboBox(this);
lineEdit1 = new QLineEdit(this);
lineEdit2 = new QLineEdit(this);
lineEdit3 = new QLineEdit(this);
//水平布局
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(lineEdit1);
hbox->addWidget(comboBox);
hbox->addWidget(lineEdit2);
hbox->addWidget(pushButton);
hbox->addWidget(lineEdit3);
setLayout(hbox); //让水平布局生效
//设置部件的属性
pushButton->setText("=");
comboBox->addItem("+");
comboBox->addItem("-");
comboBox->addItem("*");
comboBox->addItem("/");
//信号与槽
connect(pushButton,SIGNAL(clicked(bool)),this,SLOT(caculator()));
}
Widget::~Widget()
{
}
//槽函数:实现计算器功能
void Widget::caculator()
{
double a = lineEdit1->text().toDouble();
double b = lineEdit2->text().toDouble();
double c;
int index = comboBox->currentIndex();
if(index==0)
c=a+b;
else if(index==1)
c=a-b;
else if(index==2)
c=a*b;
else
c=a/b;
lineEdit3->setText(QString::number(c)); //double--->string
}
运行结果依然是:
六.实现一个图片查看器
1.修改.ui文件
2.编辑mainwindow.h文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
private:
Ui::Widget *ui;
QStringList filenames;
int count;//图片的总数
int num;//图片的下标
};
#endif // WIDGET_H
3.编辑mainwindow.cpp文件
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//设置label2
ui->label_2->setMinimumSize(501,331);
ui->label_2->setScaledContents(true); //自动调整
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//实现显示个图片
filenames = QFileDialog::getOpenFileNames();
count = filenames.size(); //图片的总数
ui->label_2->setPixmap(QPixmap(filenames.at(0)));
num =0;
}
void Widget::on_pushButton_2_clicked()
{
//上一张
if(num==0)
num=count-1;
else
num--;
ui->label_2->setPixmap(QPixmap(filenames.at(num)));
}
void Widget::on_pushButton_3_clicked()
{
//下一张
if(num==count-1)
num=0;
else
num++;
ui->label_2->setPixmap(QPixmap(filenames.at(num)));
}
4.运行结果
此时我们可以通过三个按钮来控制实现图片的上下查看。
这两个程序代码实在实训老师带领下写的,没办法,这种五天的课上四天课周五就要交项目答辩,说着从0开始,代码都读不懂,我也是服了,今天到这里,明天努力看懂这两个程序代码。