【Qt】项目的创建和各个控件的使用
一、项目的创建
🔍然后点击新建项目。
📖注意:路径不要带有中文,不然运行不了代码。
📖qmake是一个构建工具,在 Qt 写的程序,设计的到一系列的 "元编程" 技术,什么是元编程技术?就是说 Qt 框架会在编译的时候自动调用一系列的生成工具,当然是基于你写的代码生成一系列的代码,最终编译的代码也是这些最后生成的这些代码。我们经常使用的qmake。
1.qmake是老牌的 Qt 工具
2.CMake 并非 Qt 专属,很多的开源项目也会使用。
3.Qbs 是新一代的 Qt 构建工具(实际上用的人非常少)
使用 Qt Creator 创建的项目会自动生成一些代码出来,生成的代码就包含一个类,此处就是要选择这个自动生成的类的父类是谁。我们选择的是 QWidget。
1.QMainWindows 完整的应用程序窗口(包含菜单栏、工具栏、状态栏......)
2.QWidget 表示一个控件(窗口上的一个具体的元素,输入栏,按钮,下拉框,单选按钮,复选按钮.......)
3.QDialog 表示一个对话框。
注意:form file 非常关键,在 Qt 中创建图形化界面的程序主要有两种:
1)直接通过C++代码的方式来创建界面。
2)通过 form file,以图形化界面的方式来生成界面。这时候就可以使用 Qt Designer 或者直接使用Qt Creator 来编辑这个 ui 文件从而以图形化的方式来快速方便的生成图形化界面。
✍从这个名字就知道这是一个翻译性文件,我们可以在这里选择翻译文件(对应的语言),语言和国际化有关,这里不过多关注。
✍选择基于哪个编译器的 Qt SDK 来构建后续代码。
二、各个文件代码解释
注意:exec是让程序执行起来。它和 Linux 的 exec 不一样。
✍当我们双击 ui 文件时,此时Qt Creator 就会自动调用 Qt Designer ,打开 ui 文件(图形化界面编辑器)
三、Hello world 的实现
1)拖拽控件实现
2)代码实现
#include "widget.h"
#include "ui_widget.h"
#include<QLabel>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QLabel* label = new QLabel(this);label->setText("Hello world!");
}Widget::~Widget()
{delete ui;
}
注意:Qlabel(this) 中的 this 是给当前的label对象指定一个父对象。我们既然new了一个对象出来,为什么不手动释放呢?这样不就造成内存泄漏了吗?答案是不会因为label会在合适的时候被析构释放,类似于C++的智能指针,之所以能被释放,我们把对象挂到了对象树上,主要靠 this 实现。当我们的 label 定义在栈上的时候,我们是需要手动释放的。
✍注意:创建的QLabel默认是在左上角。在前端开发(网页开发)也涉及到类似的对象树(DOM),它的本质也是一个树状结构(N叉树),通过树形结构把界面上的各种元素组织起来。
小贴士:使用快捷键:F4可以快速切换对应的头文件和.cpp文件。写完函数的声明之后可以按下 alt + enter 就可以在对应的 .cpp文件添加函数的定义了。
✍补充知识:我们在Qt中输入的汉字占几个字节?由于编码的不同,一个汉字占的空间也不一样,目前表示汉字字符集主要有两种方式:
1、GBK(中国大陆)使用2个字节表示一个汉字。例如:Windows简体中文版默认是GBK
2、UTF-8/UTF8,变长编码,表示一个符号使用的字节数有变化,范围(2,4);一般来说UTF8默认是一个3个字节。例如:Linux默认使用的UTF8。
✍我们在Qt Creator 中使用编写代码时,如果 std::cout 出现乱码,代表着编码出现了问题;什么是乱码呢?
如果你的字符串本身是 UTF8 编码的,都是终端控制台是按照 GBK 来解析显示的,此时就会出现乱码,解决编码问题直接到出现编码的文件打开对应的文档,改变编码方式,如果出现UTF8声明这个文件就是UTF8编码,出现 ANSI 就是GBK编码。但是当前主流的表示中文编码方式是UTF8,使用这里不好改;所以我们使用 Qt 的QString,它可以帮我们自动处理编码问题,当然也提供了专门的日志工具也能处理编码问题。例如:qDebug()工具;
#include "widget.h"
#include "ui_widget.h"
#include<QLabel>
#include<QDebug>
#include<iostream>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QLabel* label = new QLabel(this);label->setText("Hello world!");
}Widget::~Widget()
{//std::cout << "析构" << std::endl;qDebug() << "析构";delete ui;
}
♐注意:QDebug 文件是 Qt 中的类,但是又不直接使用这个类;qDebug()一个宏,这个宏封装了QDebug 对象,直接使用 qDebug()可以当做 cout 使用。使用qDebug()输出信息,他只能在开发阶段(debug版本)能看到,发给用户的 release 版本用户是看不到的。
3)使用编辑框实现
♐也可以使用代码实现:
#include "widget.h"
#include "ui_widget.h"
#include<QLabel>
#include<QDebug>
#include<iostream>
#include<QLineEdit>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QLabel* label = new QLabel(this);label->setText("Hello world!");QLineEdit* edit = new QLineEdit(this);edit->setText("拜拜了您勒!!");}Widget::~Widget()
{//std::cout << "析构" << std::endl;qDebug() << "析构";delete ui;
}
♐也可以通过按钮来实现:
Push Button 是一个按钮,既然是按钮就可以单击,这就说到 Qt 中的信号槽机制,什么是信号槽呢?信号槽的本质是给一个按钮的点击操作关联上一个处理函数,当用户点击的时就会执行这个处理函数,这个处理函数就是 connect ,他是QObject 这个类提供的静态函数,它的作用就是连接信号和槽,但是他和 TCP 的建立没有任何关系。
♐信号的接收和处理:
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void handleClick();//声明private:Ui::Widget *ui;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include<QLabel>
#include<QDebug>
#include<iostream>
#include<QLineEdit>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
// QLabel* label = new QLabel(this);
// label->setText("Hello world!");// QLineEdit* edit = new QLineEdit(this);
// edit->setText("拜拜了您勒!!");connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);//从界面文件中访问到了按钮,记住一定要拖拽按钮之后再写代码//第二参数就是用户点击这个按钮之后触发处理函数//第一个参数是谁发出的信号//第二个参数发出了个啥信号,点击按钮的时候就会自动触发这个信号//第三个参数,谁来处理这个参数//第四个参数,具体怎么处理}Widget::~Widget()
{//std::cout << "析构" << std::endl;qDebug() << "析构";delete ui;
}void Widget::handleClick()
{//当按钮被点击之后,就把按钮中的文本进行切换if(ui->pushButton->text() == QString("hello world"))ui->pushButton->setText("hello QT");elseui->pushButton->setText("hello world");
}
注意:ui -> pushButton 访问到 form file(ui 文件)中创建的控件。在Qt Designer 中创建一个控件的时候,此时就会给这个控件分配一个 ojectName 属性,这个属性的值(可以修改),要求是界面中得是唯一的(不能重复);qmake 在预处理 .ui 文件时就会根据这里的 objectName 生成对应的C++ 代码,C++代码中该 QPushButton 对象的变量名字就是这里的 objectName,该变量是 ui 属性中的成员变量。
♐纯代码实现按钮:
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include<QPushButton>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void handleClick();private:Ui::Widget *ui;QPushButton* myButton;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);myButton = new QPushButton(this);myButton->setText("hello world!");connect(myButton,&QPushButton::clicked,this,&Widget::handleClick);
}Widget::~Widget()
{delete ui;
}void Widget::handleClick()
{if(myButton->text() == QString("hello world")){myButton->setText("hello qt");}else{myButton->setText("hello world");}
}
四、Qt 中的命名规范和快捷键
1)命名规范
在C++代码中我们一般使用蛇形命名法,什么是蛇形命名法?例如:student_count,priority_queue.......
在Qt 中我们使用的是驼峰命名法,例如:studentCount,QWidget......首字母是大写的是大驼峰,一般是给变量和函数命名,首字母是小写的是小驼峰,一般是给类命名。
2)快捷键
注释:ctrl+/
运行:ctrl+R
编译:ctrl+B
字体缩放:ctrl+鼠标滑轮
查找:ctrl+F
整行移动:ctrl +shift+向上的箭头或向下的箭头
帮助文档:F1
自动对齐:ctrl+i
同名之间的.h和.cpp的切换:F4
生成函数声明的对应定义:alt +enter
五、帮助文档
打开帮助文档有三种方式.实际编程中使用哪种都可以。
1、光标放到要查询的类名/方法名上,直接按F1
2、Qt Creator 左侧边栏中直接用鼠标单击"帮助"按钮:
六、Qt 窗口坐标体系
♐坐标体系:以左上角为原点(0,0),X向右增加,Y向下增加。
坐标系的原点(0,0)是窗口的或者屏幕的左上角,因为给 Qt 的某个控件设置位置的时候就需要指定坐标,对于这个控件来说坐标原点就是相对于父窗口/父控件的。
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");
}Widget::~Widget()
{delete ui;
}
注意:除了红色箭头的部分是 Widget 的范围,其他的是系统自动生成的,不在 Widget 范围之内。
♐使用 move 设置按钮的位置:
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");button->move(500,400);//单位是像素
}Widget::~Widget()
{delete ui;
}
♐设置 Widget 的位置:
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");button->move(500,500);//控件的位置this->move(1000,200);//设置Widget的位置
}Widget::~Widget()
{delete ui;
}
七、信号和槽
🔍 Linux 信号 signal 是系统内部的通知机制,是进程之间的通信方式。
那么我们可以知道:
1)信号源:谁发的信号。
2)信号的类型:哪种类别的信号。
3)信号的处理方式:注册信号处理函数,在信号被触发的时候自动执行。
显然Qt 中的信号和 Linux 中的信号虽然不是一样的概念但是确实有相似之处。
🔍 在 Qt 中也是涉及到三个元素:
信号源:是哪个控件发出从信号。
信号的类型:用户进行不同的操作,就可能触发不同的信号。
信号的处理方式:槽(slot) 函数,Qt 中使用 connect 这样的函数把一个信号和槽关联起来,只要用户触发信号 Qt 就会自动执行槽函数。槽函数本质上是一种回调函数(callback)
在 Qt 中,用户和控件的每次交互过程称为一个事件。比如 "用户点击按钮" 是一个事件,“用户关闭窗口“ 也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出"按钮被点击”的信号,用户关闭窗口会发出“窗口被关闭”的信号。
Qt 中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如:按钮所在的窗口接收到 "按钮被点击" 的信号后,会做出"关闭自己"的响应动作;再比如输入框自己接收到 "输入框被点击" 的信号后,会做出 "显示闪烁的光标,等待用户输入数据" 的响应动作。在 Qt 中,对信号做出的响应动作就称之为槽。
信号和槽是 Qt 特有的消息传输机制,它能将相互独立的控件关联起来。比如:"按钮" 和" 窗口" 本身是两个独立的控件,点击 "按钮" 并不会对 "窗口" 造成任何影响。通过信号和槽机制,可以将 "按钮" 和 "窗口" 关联起来,实现”点击按钮会使窗口关闭”的效果。
🔍信号的本质:
信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时Qt对应的窗口类会发出某个信号,以此对用户的操作做出反应。因此,信号的本质就是事件。如:
1)按钮单击、双击
2)窗囗刷新鼠标移动、鼠标按下、鼠标释放
3)键盘输入
📖那么在Qt中信号是通过什么形式呈现给使用者的呢?
1)我们对哪个窗口进行操作,哪个窗口就可以捕捉到这些被触发的事件。
2)对于使用者来说触发了一个事件我们就可以得到 Qt 框架给我们发出的某个特定信号。
3)信号的呈现形式就是函数,也就是说某个事件产生了,Qt框架就会调用某个对应的信号函数,通知使用者。
在Qt中信号的发出者是某个实例化的类对象。
槽的本质:
📖 槽(Slot)就是对信号响应的函数。槽就是一个函数,与一般的C++函数是一样的,可以定义在类的任何位置(public、protected或private),可以具有任何参数,可以被重载,也可以被直接调用(但是不能有默认参数)。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
🔍说明:
(1)信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号数;每个槽也可以用函数表示,称为槽函数。例如:"按钮被按下”这个信号可以用clicked()函数表示,“窗口关闭”这个槽可以用close()函数表示,假如使用信号和槽机制一实现:"点击按钮会关闭窗口”的功能,其实就是clicked()函数调用close()函数的效果。
(2)信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:信号函数用 signals 关键字修饰,槽函数用 public slots、protected slots 或者 private slots 修饰。signals和slots 是 Qt 在 C++ 的基础上扩展的关键字,专门用来指明信号函数和槽函数;
信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)。
1、信号和槽的使用
Qt 中提供的这些类,本身存在一定的继承关系,例如:QWedget 继承 QObject ,QPushbutton 继承 QWedget 。
🔍 connect 的原型:
// 通用原型(适用于任意信号-槽关联)
template <typename Func1, typename Func2>
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, // 发送者对象(指针)Func1 signal, // 发送者的信号(函数指针)const typename QtPrivate::FunctionPointer<Func2>::Object* receiver,// 接收者对象(指针,可 nullptr)Func2 slot, // 接收者的槽/ Lambda(函数指针)Qt::ConnectionType type = Qt::AutoConnection // 连接类型(默认自动选择)
);
🔍实例:点击按钮,Widget 窗口关闭:
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("关闭");button->move(200,200);//控件的位置connect(button,&QPushButton::clicked,this,&Widget::close);//click 是一个 slot 函数,作用是调用是时候相当于点击了一下按钮;//clicked 过去分词,点完了之后,才是要触发的点击信号。//close 是 QWidget 内置的槽函数,Widget 继承 QWidget,也就继承了父亲的槽函数//close 功能是关闭当前的窗口/控件
}Widget::~Widget()
{delete ui;
}
connect 要求:类型如果是QPushButton* 此时第二个参数必须是 QPushButton 内置的信号(父类的信号),不能是一个其他的类例如:QLineEdit 信号。
🔍各位由于信号和槽的知识点篇幅过大,所以后文留到下篇的博客详写!
🍁🍁请各位道友,敬请期待下文,完!!