QT
引言:
定义
Qt是一个跨平台的C++图形用户界面应用程序框架
优势
- 跨平台,几乎支持所有的平台
- 接口简单,容易上手,学习QT框架对学习其他框架有参考意义。
- 一定程度上简化了内存回收机制
- 开发效率高,能够快速的构建应用程序。
- 有很好的社区氛围,市场份额在缓慢上升。
- 可以进行嵌入式开发。
QT默认基类
默认的基类有QMainWindow、QWidget以及QDialog三个,QWidget(类似于空窗口)
0. C + + 部分知识点 \textcolor{red}{C++部分知识点} C++部分知识点
(1)双冒号::
- 第一种,类作用域,用来标明类的变量、函数
Human::setName(char* name);
&QPushButton::clicked;
- 第二种,命名空间作用域,用来注明所使用的类、函数属于哪一个命名空间的
std::cout << "Hello World" << std::endl;
(2)Qt中指针的用处
在Qt中使用指针是非常常见的,因为Qt是一个C++框架,而C++中使用指针是一种常用的编程模式。下面是在Qt中使用指针的主要原因:
- 动态内存分配:Qt中的许多对象需要在堆上进行动态内存分配,这样可以确保对象的生存期与程序逻辑相匹配,并且可以在需要时创建和销毁对象。指针提供了一种方便的方式来管理动态分配的对象。
- 对象生存期管理:Qt中的UI元素(如QPushButton、QLabel等)通常需要在程序的生命周期内保持活动状态。通过使用指针,可以在需要时创建这些UI元素,并在不再需要时进行销毁,从而正确地管理它们的生存期。
- 父子关系:在Qt中,对象之间可以建立父子关系。当一个对象被设置为另一个对象的父对象时,父对象负责管理子对象的内存,确保在父对象被销毁时同时销毁子对象。指针在这种情况下非常有用,可以轻松地指定父子关系。
- 信号与槽机制:Qt的信号与槽机制是一种强大的机制,用于在对象之间进行通信。指针允许轻松地连接信号和槽,从而实现对象之间的交互和响应。
- 多态性(Polymorphism):Qt中使用了多态性,通过基类指针指向派生类对象,可以实现对派生类的统一管理和操作。
- 数据共享:在Qt中,一些数据对象需要在多个类之间共享。使用指针可以避免复制数据,提高程序的性能。
- 函数传递:指针允许将对象传递给函数,而不会复制整个对象。这样可以提高程序的性能,特别是在处理大型对象时。
总的来说,Qt使用指针是为了更好地管理对象的生存期、构建复杂的对象关系、实现信号与槽的连接以及提供更高效的数据共享。虽然使用指针需要小心处理,以避免内存泄漏和悬挂指针等问题,但它们在Qt中是非常有用的编程工具。
(3).和->
当有一个指向对象的指针时,你可以使用 ->
来访问该对象的成员变量和成员函数。
.
运算符用于直接访问对象的成员
(4)堆和栈的生命周期
- 堆(Heap):
- 堆是程序运行时动态分配的内存区域,用于存储动态分配的对象和数据结构。
- 堆中的内存由程序员手动分配和释放,通常使用类似于
new
和delete
(或者malloc
和free
在C语言中)的操作来进行分配和释放。 - 堆上分配的对象在程序的生命周期内一直存在,直到显式地由程序员释放,否则会导致内存泄漏。
- 堆上的内存空间不会自动被回收,需要开发人员负责管理其生命周期。
- 使用堆时需要注意避免悬挂指针和内存泄漏等问题。
- 栈(Stack):
- 栈是一种自动分配和释放的内存区域,用于存储局部变量和函数调用的上下文。
- 当函数被调用时,其局部变量会在栈上自动分配内存,当函数返回时,栈上的局部变量会自动释放。
- 栈上的内存分配和释放是按照“先进后出”的原则进行的,因此称为“后进先出”(LIFO)数据结构。
- 栈上的内存管理是由编译器自动处理的,程序员无需显式地分配或释放栈上的内存。
- 由于栈上的内存自动分配和释放,因此栈上的对象生命周期通常比较短暂,仅限于函数调用的范围内。
- 堆是手动管理内存的区域,对象在堆上分配,需要程序员负责手动释放内存。
- 栈是自动管理内存的区域,局部变量在栈上分配,函数返回时自动释放。
- 堆上的对象生命周期由程序员控制,栈上的局部变量生命周期由编译器控制。
(5)静态成员函数可以直接通过类名来使用
QMessageBox::critical();
(6)c++匿名函数/匿名对象
1)匿名函数
-
定义
没有名字的函数。使用匿名函数,可以免去函数的声明和定义。这样匿名函数仅在调用函数的时候才会创建函数对象,而调用结束后立即释放,所以匿名函数比非匿名函数更节省空间。
-
lambda 表达式可以在需要函数对象的地方使用,例如作为函数参数、存储在容器中或者在其他函数中返回。
// 定义并使用 lambda 表达式 auto add = [](int a, int b) -> int { return a + b; }; int result = add(2, 3); std::cout << "Result: " << result << std::endl;
2)匿名对象
也称临时对象
Cat(); —> 生成了一个匿名对象,执行完Cat( )代码后,此匿名对象就此消失。这就是匿名对象的生命周期。
Cat cc = Cat(); —>首先生成了一个匿名对象,然后将此匿名对象变为了cc对象,其生命周期就变成了cc对象的生命周期。
(7)Java中多态
-
多态是面向对象程序设计(OOP)的一个重要特征,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。(同一件事情,发生在不同对象身上,就会产生不同的结果。)
动物有很多种类,狗、猫等等,在吃这个条件下;猫吃猫粮,狗吃狗粮。这就是多态的具体体现。
-
多态的前提1:是继承
多态的前提2:要有方法的重写
父类引用指向子类对象,如:Animal a = new Cat();
多态中,编译看左边,运行看右边
//7.创建多态对象进行测试 /*3.口诀1:父类引用指向子类对象 * 解释:创建出来的子类对象的地址值,交给父类类型的引用类型变量来保存*/ Animal a2 = new Cat();//Cat类对象的地址值交给父类型变量a2来保存 Animal a3 = new Dog();//Dog类对象的地址值交给父类型变量a3来保存 //8.测试多态对象 /*4.口诀2:编译看左边,运行看右边 * 解释:必须要在父类定义这个方法,才能通过编译,把多态对象看作是父类类型 * 必须要在子类重写这个方法,才能满足多态,实际干活的是子类*/ a2.eat();//小猫爱吃小鱼干~,多态对象使用的是父类的定义,子类的方法体
(8)c++类型转换
1)C++层次转换规则
C++中层次类型转换中无非两种:上行转换和下行转换
C++规定自定义类型数据如果发生了继承关系,编译器允许进行类型转换(向上转型、向下转型),否则不能进行类型转换
2)静态转换(static_cast)
static_cast < T > ( expression ),该运算符把expression转换为T类型
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的(也可以)
QMouseEvent * ev = static_cast<QMouseEvent *>(e);//QEvent的e转为子类QMouseEvent的ev
3)用于基本数据类型之间的转换,如把int转换成char,把char转换成int
4)动态转换(dynamic_cast)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200729233942527.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzOTE1MzU2,size_16,color_FFFFFF,t_70)
在类层次间进行上行转换时(安全的转换),dynamic_cast和static_cast的效果是一样的
在进行下行转换时(不安全,子类指针可能会超出基类对象),dynamic_cast具有类型检查的功能,比static_cast更安全
dynamic_cast不能作用于基本数据类型
(9)Java与c++创建对象的区别
-
c++
在栈上创建对象
QPainter painter(this);
在堆上创建对象(需要手动delete)
MyClass * obj = new MyClass();
-
Java
// 在栈上创建对象引用,并在堆上实例化对象 MyClass obj1 = new MyClass(42);
obj1
是在栈上创建的对象引用,它们分别指向堆上的不同MyClass
对象实例。实际的MyClass
对象实例本身是在堆上分配的,而栈上的引用仅保存了对象的地址(或者说引用)。总结:在 Java 中,对象本身总是在堆上分配,而对象引用(reference)可以在栈上创建。
(10)static关键字
本质就是修改了其作用域和生命周期
1.int main(int argc, char *argv[])
在C++中,特别是在控制台程序中,int main(int argc, char *argv[])
是程序的入口点(main函数)
int argc
:argc
是一个整数(int)类型的变量,代表命令行参数的数量。- 当你运行一个C++控制台程序时,在命令行中输入的参数都会被传递给这个程序。
argc
就是用来记录这些参数的数量。 - 至少会有一个参数,即程序的名称本身。例如,运行命令
./my_program
,argc
的值将是1。
char *argv[]
:argv
是一个指向字符指针(char pointer)的数组。- 每个元素
argv[i]
存储着一个命令行参数的字符串。 argv[0]
存储的是程序的名称(也就是可执行文件的名称)。argv[1]
存储的是第一个传递给程序的参数,argv[2]
存储的是第二个参数,以此类推。
综合起来,int main(int argc, char *argv[])
这个函数签名的作用是,当你运行程序时,可以通过命令行传递一些参数给程序。例如,假设编译并运行了一个名为my_program
的C++程序,命令行输入为:./my_program arg1 arg2 arg3
,那么:
argc
的值将是4,因为有4个参数,包括程序本身的名称。argv[0]
存储的是./my_program
,程序的名称。argv[1]
存储的是arg1
,第一个传递给程序的参数。argv[2]
存储的是arg2
,第二个参数。argv[3]
存储的是arg3
,第三个参数。
2.QT基础函数
(1)主函数
#include "mywidget.h"
#include <QApplication> //包含一个应用程序类的头文件
//argc命令行变量数量 argv[]命令行变量数组
int main(int argc, char *argv[])
{
//a应用程序对象,Qt中应用程序对象有且仅有一个
QApplication a(argc, argv);
//窗口对象 myWidget父类 --> QWidget
myWidget w;
//窗口对象默认不会显示,需要调用show()方法来显示窗口
w.show();
//让应用程序对象进入消息循环(一直接受用户的操作,直到点击X才关闭,否则窗口一直存在)
return a.exec();
}
(2)myWidget类的构造函数的声明
#ifndef MYWIDGET_H //防止头文件重复定义
#define MYWIDGET_H
#include <QWidget> //窗口类的头文件
class myWidget : public QWidget
{
Q_OBJECT //引入Qt信号和槽机制的一个宏
public:
myWidget(QWidget *parent = nullptr);
~myWidget(); //析构函数
};
#endif // MYWIDGET_H
(3)myWidget类的构造函数的实现
#include "mywidget.h"
#include <QPushButton>
myWidget::myWidget(QWidget *parent) //前一个是类名,后一个是构造函数名
: QWidget(parent) //初始化列表
// QWidget(parent) 表示调用 QWidget 类的构造函数,
// 并将传入的 parent 指针作为参数传递给它。
// 这样可以确保 myWidget 类的对象在初始化时,
// 也会调用 QWidget 类的构造函数,
// 以便在创建 myWidget 对象时执行父类 QWidget 的初始化操作
{
//第一种创建按钮方法
QPushButton* button1 = new QPushButton;
button1->setParent(this); //this 当前对象的指针(地址),设置按钮的父亲,让按钮在窗口中
button1->setText("开始游戏");
//第二种创建按钮方法 弊端:按照按钮大小创建窗口 可用resize重置窗口大小
QPushButton* button2 = new QPushButton("开始播放",this);
//移动按钮
button2->move(80,0);
//重置窗口大小
resize(600,400);
//设置固定窗口大小
// setFixedSize(600,400);
//设置窗口标题
setWindowTitle("MyFirstWindow");
}
myWidget::~myWidget()
{
}
3.按钮控件API(纯代码)
(1)创建按钮
QPushButton* button1 = new QPushButton;
QPushButton* button2 = new QPushButton("开始播放",this);
这行代码是用C++语言创建了一个指向QPushButton对象的指针变量button1,并使用new运算符在堆上动态地分配内存来实例化一个QPushButton对象。
(2)设置父亲,让按钮在窗口里
button1->setParent(this);
(3)设置按钮文字
button1->setText("开始游戏");
(4)移动按钮
button2->move(80,0);
(5)重置窗口/按钮大小
resize(600,400);
button1->resize(60,40);
(6)固定窗口大小
setFixedSize(600,400);
(7)设置窗口标题
setWindowTitle("MyFirstWindow");
4.对象树
当创建的对象在堆区的时候,如果指定的父亲是QObject派生下来的类或者QObject子类派生下来的类,就不需要管理释放操作,通过setParent函数将对象间确定父子关系,之后子对象会进入父对象的children列表
当析构时,会先从父对象开始,先走父对象析构函数(注意,只是析构没有真正释放),然后依旧children列表析构子对象,直到走到叶子对象后,释放叶子对象,再返回父对象释放,直到回到一开始的父对象。
5.QT坐标系
-
左上角为(0,0)点
-
x向右为正方向
-
y向下为正方向
6. 信号和槽 \textcolor{red}{6.信号和槽} 6.信号和槽
(1)基础(点击按钮关闭窗口)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nm3KICbj-1691596113872)(C:\Users\ROG\AppData\Roaming\Typora\typora-user-images\image-20230719163551718.png)]
-
松散耦合(点击按钮和窗口关闭本无关,connect连接后有关系)
-
连接函数 connect
-
参数1 信号发送者
-
参数2 发送的信号(函数地址)
-
参数3 信号的接收者
-
参数4 处理的槽函数(函数地址)
connect(button2,&QPushButton::clicked,this,&QWidget::close);
(2)自定义信号和槽
自定义信号
-
在头文件中定义
-
返回值void
-
需要声明,不需要实现
-
可以有参数、重载
自定义槽函数
-
在头文件中定义
-
返回值void
-
需要声明,也需要实现
-
可以有参数,可以重载
-
emit触发自定义信号
(3)自定义信号和槽的重载
函数指针
指向函数入口地址的指针称为函数指针
一般形式 : 数据类型 (*指针变量名) (参数表);
有如下的函数:int fn1(int x, int y); int fn2(int x);
定义如下的函数指针:int (*p1)(int a, int b); int (*p2)(int a);
,则
p1 = fn1; //正确
p2 = fn2; //正确
p1 = fn2; //产生编译错误
重载
//信号重载
signals:
void hungry();
void hungry(QString foodName);
//槽重载
void treat();
void treat(QString foodName);
void Student::treat() //槽函数的实现
{
qDebug() << "请老师吃饭";
}
void Student::treat(QString foodName)
{
qDebug() << "打包一份" << foodName;
}
//连接
//需要使用函数指针确定到底是哪个信号 若是无参QString改为void
void (Teacher:: *teacherSignal)(QString)=&Teacher::hungry;
void (Student:: *studentSlot)(QString)=&Student::treat;
connect(teacher,teacherSignal,student,studentSlot);
**QString转char ***
.toUtf8().data()
(4)信号连接信号
QPushButton * btn = new QPushButton("下课",this);
void (Teacher:: *teacherSignal2)(void)=&Teacher::hungry;
void (Student:: *studentSlot2)(void)=&Student::treat;
//信号连接信号(点击的信号连接老师饿了的信号)
connect(btn,&QPushButton::clicked,teacher,teacherSignal2);
//无参信号连接槽
connect(teacher,teacherSignal2,student,studentSlot2);
(5)拓展
-
一个信号可以连接多个槽函数
-
多个信号可以连接同一个槽函数
-
信号和槽的参数类型必须一一对应
-
信号的参数个数可以多于槽的参数个数,反之不成立
7.Lambda表达式
(1)匿名函数形式
[ ]( )->返回值类型{ }; //引用该匿名函数 [ ]( )->返回值类型{ }();
-
(1)[]标识符
[=] 值传递(最常用) [&] 引用传递
= &都可以使用Lambda所在范围内所有可见的局部变量
-
(2)()参数
-
(3){}函数体
-
(4)->返回值类型
-
(5)mutable关键字
用于修改传递的变量,修改的是拷贝,而不是本体
int m=10;
connect(newbtn,&QPushButton::clicked,this,[m]()mutable{m=100+10;qDebug()<<m;});//输出110
connect(newbtn1,&QPushButton::clicked,this,[=](){qDebug()<<m;});//输出10
QWidget总结
(1)newWindow->show()
假如要点击一个按钮创建一个带按钮的新窗口,必须先new新窗口和新按钮再show()
connect(btn1,&QPushButton::clicked,[=](){
QWidget * newWindow = new QWidget;
newWindow->setFixedSize(600,400);
QPushButton * newbtn = new QPushButton;
newbtn->setParent(newWindow);
newbtn->resize(100,100);
newWindow->show();//最后show()
});
---------------------------------------------------
8.QMainWindow
(1)菜单栏 QMenuBar
1)创建菜单
菜单栏最多只有一个
QMenuBar * bar = menuBar();//创建,默认不会放在窗口中
setMenuBar(bar);//将新建的菜单放入窗口
2)添加菜单
QMenu * fileMenu = bar->addMenu("文件");
3)添加菜单项
QAction * newAction = fileMenu->addAction("新建");
4)添加分割线
fileMenu->addSeparator();
5)菜单项点击信号
&QAction::triggered
(2)工具栏 QToolBar
1)创建工具栏
工具栏可以有多个
QToolBar * toolbar = new QToolBar(this);//默认不会放入窗口,与菜单栏区别!!这需要放入this对象树中
// 将工具栏放入窗口中
addToolBar(Qt::TopToolBarArea,toolbar);//(默认停靠位置,工具栏指针)
// 指定工具栏默认位置
// LeftToolBarArea 左
// RightToolBarArea 右
// TopToolBarArea 上
// BottomToolBarArea 下
2)设置后期停靠区域,是否浮动,是否可移动
//设置工具栏只允许停靠上下
toolbar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
//设置工具栏是true否false浮动
toolbar->setFloatable(false);
//设置工具栏是否移动
toolbar->setMovable(false);
3)添加工具栏内容,控件
//工具栏设置内容
toolbar->addAction(newAction);//上面的菜单项QAction * newAction = fileMenu->addAction("新建");
toolbar->addAction(openAction);
//工具栏中添加控件
QPushButton * button = new QPushButton("ab",this);
toolbar->addWidget(button);
(3)状态栏 QStatusBar
1)创建状态栏
最多有一个
QStatusBar * stBar = statusBar();
//设置到窗口中
setStatusBar(stBar);
2)添加标签控件(放信息)
QLabel * label = new QLabel("提示信息",this);
stBar->addWidget(label);//左侧
QLabel * label2 = new QLabel("右侧提示信息",this);
stBar->addPermanentWidget(label2);//右侧
(4)铆接部件(浮动窗口) QDockWidget
1)创建铆接部件
可以有多个
QDockWidget * dock = new QDockWidget("浮动",this);
//放入窗口
addDockWidget(Qt::BottomDockWidgetArea,dock);//(默认停靠区域,浮动窗口指针)
2)设置停靠位置
//设置允许停靠位置 只允许上下停靠
dock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
(5)中心部件
1)创建中心部件
QTextEdit * edit = new QTextEdit(this);//文本编辑器
//将中心部件放入窗口
setCentralWidget(edit);
9.资源文件
(1)步骤
- 将图片文件拷贝到项目目录下
- 右键项目 => 新建文件 => Qt => Qt Resource File => 给资源文件起名字
- myResource生成myResource.qrc
- open in editor添加文件,先添加前缀(/),再添加文件
- 使用Qt资源 “: + 前缀名 + 文件名”
//使用Qt资源 ": + 前缀名 + 文件名"
ui->actionNew->setIcon(QIcon(":/myPicture/bmwi7.jpeg"));
ui->actionopen->setIcon(QIcon(":/myPicture/cat.jpg"));
10.对话框
(1)分类
- 模态对话框(不可以对其他窗口进行操作)
- 非模态对话框(可以对其他窗口进行操作)
(2)创建
-
模态对话框创建
QDialog dlg(this); dlg.resize(200,100);//设置大小不会弹出警告 dlg.exec();//阻塞
-
非模态对话框创建
QDialog * dlg2 = new QDialog(this); dlg2->resize(200,100); dlg2->setAttribute(Qt::WA_DeleteOnClose); //55号属性,让对话框关闭后释放对象 dlg2->show();
- 为什么模态在栈上创建,非模态在堆上创建? \textcolor{red}{为什么模态在栈上创建,非模态在堆上创建?} 为什么模态在栈上创建,非模态在堆上创建?
模态没有在堆上new出来而是在栈上创建,并且在栈上创建没有被释放是因为.exec()阻塞了,而下面非模块对话框如果不new在堆上,show()不会阻塞,匿名函数一结束就会被立马释放,就看不见对话框,为了防止一闪而过,所以非模态要在堆上创建,上面可以在栈上创建
(3)标准对话框(消息对话框)
-
QMessageBox静态成员函数 创建对话框
QMessageBox::warning(this,"Warning","警告");
-
错误,信息,提问,警告对话框
参数(父亲 , 标题 , 提示内容 , 按键类型 , 默认关联回车按键)
//错误对话框 QMessageBox::critical(this,"Critical","妈的你错误了"); //信息对话框 QMessageBox::information(this,"Information","信息"); //提问对话框 //QMessageBox::question()和QMessageBox::Save的返回值为StandardButton,利用返回值判断用户输入,用于点击后的效果 if(QMessageBox::Save == QMessageBox::question(this,"Question","你要走了吗",QMessageBox::Save | QMessageBox::Cancel)) { qDebug()<<"保存成功!"; }else { qDebug()<<"取消成功!"; } //警告对话框 QMessageBox::warning(this,"Warning","警告");
-
按键类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-920NfvKx-1691596113873)(C:\Users\ROG\AppData\Roaming\Typora\typora-user-images\image-20230724234836863.png)]
(4)其他对话框
-
颜色对话框
QColor color = QColorDialog::getColor(QColor(255,0,0)); qDebug() << "r=" << color.red() << "g=" << color.green() << "b=" << color.blue();
-
文件对话框
//参数(父亲,标题,默认打开路径,过滤文件格式) QString str = QFileDialog::getOpenFileName(this,"打开文件","C:/Users/ROG/Desktop","(*.txt)"); //返回值是选取的文件路径
-
字体对话框
bool flag; QFont font = QFontDialog::getFont(&flag,QFont("华文彩云",36)); qDebug()<< "字体:" << font.family().toUtf8().data() << "字号:" << font.pointSize() << "是否加粗:" << font.bold() << "是否斜体:" << font.italic();
11.界面布局(UI)
- 实现登陆窗口
- 利用布局方式,将窗口美化
- 选取 Widget 进行布局(水平布局,垂直布局,栅格布局
- 给用户名、密码、登录、退出按钮布局
- 利用弹簧布局
- 默认窗口与控件之间有间隙,layoutLeftMargin…改
- 框和控件一样大,选中框,在sizePolicy中垂直策略改为Fixed
- 输入密码不显示(echoMode选择password)
12.控件(UI)
(1)按钮组
-
QPushButton
常用按钮 -
QToolButton
工具按钮 修改toolButtonStyle
风格可以图片和文字一起显示,凸起风格autoRaise
-
radioButton
单选按钮 设置默认选择ui->rBman->setChecked(true);
-
checkBox
多选按钮,监听状态//stateChanged的参数 2是选中 0是未选中 connect(ui->bmwBox,&QCheckBox::stateChanged,[=](int state){ qDebug() << state; });
(2)QListWidget列表容器
-
QListWidgetItem * item
一行内容 -
ui->listWidget->addItem(item);
将内容放入容器 -
item->setTextAlignment(Qt::AlignHCenter);
设置水平居中 -
利用
addItem
添加多行内容,但无法居中//QStringList QString<List> QStringList list;//list链表 list << "床前明月光" << "疑似地上霜" << "举头望明月" << "低头思故乡";//内容放入链表 ui->listWidget->addItems(list);
QStringList使用
QStringList list;
list << "床前明月光" << "疑似地上霜" << "举头望明月" << "低头思故乡";
//等同于
QStringList() << "床前明月光" << "疑似地上霜" << "举头望明月" << "低头思故乡";//QStringList()是创建一个空的QStringList对象(匿名对象),重载了<<运算符
(3)QTreeWidget树控件
-
设置水平头
ui->treeWidget->setHeaderLabels(QStringList() << "英雄" << "英雄介绍");
-
创建根节点
QTreeWidgetItem * powerItem = new QTreeWidgetItem(QStringList() << "力量");
-
添加根节点
ui->treeWidget->addTopLevelItem(powerItem);
-
添加子节点
QStringList heroL1; heroL1 << "阿诺" << "神一般的抗药性"; QTreeWidgetItem * L1 = new QTreeWidgetItem(heroL1); powerItem->addChild(L1);
(4)QTableWidget表格控件
-
设置列数
ui->tableWidget->setColumnCount(3);
-
设置水平表头
ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "姓名" << "性别" << "年龄");
-
设置行数
ui->tableWidget->setRowCount(5);
-
设置内容
ui->tableWidget->setItem(0,0,new QTableWidgetItem("阿诺")); QStringList nameList; nameList << "阿诺" << "麦寇" << "脚强" << "cBumStead" << "Nick Walker"; QStringList sexList; sexList << "男" << "男" << "男" << "男" << "男"; for(int i=0;i<5;i++) { int col=0; ui->tableWidget->setItem(i,col++,new QTableWidgetItem(nameList[i]));//第i行第0列 ui->tableWidget->setItem(i,col++,new QTableWidgetItem(sexList.at(i)));//第i行第1列 ui->tableWidget->setItem(i,col++,new QTableWidgetItem(QString::number(18+i)));//第i行第2列 }
(5)其他控件
-
利用QLabel显示GIF动画
QMovie * movie = new Qmovie("GIF路径") ui->my_label->setMovie(movie); movie->start();//必须开始
13.自定义控件封装
-
添加新文件,选择Qt-设计师界面类 (.h .cpp .ui)
-
新的.ui中设计,例如QSpinBox和QSlider两个控件
-
在widget.ui中使用自定义控件,在widget.ui拖拽一个widget,提升为SmallWidget,添加,提升
-
实现功能,改变SpinBox数字,Slider跟着滑动,信号槽监听
-
提供getNum和setNum对外接口
//smallWidget.h中 public: explicit SmallWidget(QWidget *parent = nullptr); ~SmallWidget(); //设置 void setNum(int num); //获取 int getNum(); //smallWidget.cpp中 void SmallWidget::setNum(int num){ui->spinBox->setValue(num);}//SmallWidget::作用域 int SmallWidget::getNum(){return ui->spinBox->value();} //widget.cpp中使用 connect(ui->getv,&QPushButton::clicked,[=](){ qDebug() << "当前值为" << ui->widget->getNum(); //这里的widget之前被提升过,是SmallWidget实例化的一个对象 }); connect(ui->setv,&QPushButton::clicked,[=](){ ui->widget->setNum(75); });
14.Qt中的事件
(1)鼠标事件
1)鼠标进入事件
void myLabel::enterEvent(QEnterEvent *event)
2)鼠标离开事件
void myLabel::leaveEvent(QEvent *)
3)鼠标按下事件
void myLabel::mousePressEvent(QMouseEvent *ev)
4)鼠标松开事件
void myLabel::mouseReleaseEvent(QMouseEvent *ev)
5)鼠标移动事件
void myLabel::mouseMoveEvent(QMouseEvent *ev)
6)获取坐标
//获取x,y坐标
ev->x(),ev->y();
//qt格式化字符串 类似printf
QString str = QString("鼠标按下了,在 x= %1 y= %2 处").arg(ev->x()).arg(ev->y());
//globalX(),globalY()是基于电脑屏幕的坐标
7)Qt格式化字符串
%1,%2…参数1,参数2 .arg(参数1).arg(参数2)…
QString str = QString(“鼠标按下了,在 x= %1 y= %2 处”).arg(ev->x()).arg(ev->y());
8)判断按键
ev->button()
ev->button()==Qt::LeftButton
判断左键
9)判断组合按键
判断move时候的左右键,结合&运算
void myLabel::mouseMoveEvent(QMouseEvent *ev)
{
//按下左键并移动才输出结果
if(ev->buttons() & Qt::LeftButton) //Qt::LeftButton的二进制值为00...01,
//如果按下左键00...01 & 00...01 =1 执行if,如果是别的,&出来就是0,不执行if
{
QString str = QString("鼠标移动了,在 x= %1 y= %2 处").arg(ev->x()).arg(ev->y());
qDebug() << str;
}
}
10)button和buttons区别
假设你的鼠标左键已经按下。
如果移动鼠标,会发生的move事件,button返回Qt::NoButton,buttons返回LeftButton。
再按下了右键,会发生press事件,button返回RightButton,buttons返回LeftButton|RightButton
再移动鼠标,会发生move事件,button返回Qt::NoButton,buttons返回LeftButton|RightButton
再松开左键,会发生Release事件,button返回LeftButton,buttons返回RightButton
也就是说,button返回“那个按钮发生了此事件”,buttons返回"发生事件时哪些按钮还处于按下状态"
15.定时器
(1)方式1
-
利用事件
void timerEvent(QTimerEvent *ev);
-
启动定时器
int id1 = startTimer(500);//参数:间隔ms 返回值:int定时器唯一标识
-
利用上述返回值,可以与
ev->timerId()
比较,判断哪个定时器执行什么操作if(ev->timerId()==id1) { static int num = 1;//设置static 否则一直是1 //label_2每隔500ms加一次 ui->label_2->setText(QString::number(num++)); }
(2)方式2(常用!!!)
-
引入QTimer类
#include <QTimer>
-
创建定时器对象
QTimer * timer = new QTimer(this);
-
启动定时器,输入参数,单位为毫秒,并使用信号槽连接
timer->start(500);//0.5s发出一个信号 connect(timer,&QTimer::timeout,ui->label_4,[=](){ //label_4每隔0.5s加一次 static int num=1; ui->label_4->setText(QString::number(num++)); });
-
点击按钮暂停定时
connect(ui->pushButton,&QPushButton::clicked,[=](){ timer->stop(); });
16.Event事件分发器
APP与事件分发器之间还有一个高级的事件过滤器eventfilter
-
用途:用于分发事件
-
也可以拦截操作,不建议
-
拦截,bool event(QEvent *ev);返回值true代表用户自己处理,不向下分发
拦截示例:
bool myLabel::event(QEvent *e) { //如果是鼠标按下,在event事件分发器中拦截 if(e->type()==QEvent::MouseButtonPress) { QMouseEvent * ev = static_cast<QMouseEvent *>(e);//QEvent类型转化为QMouseEvent QString str = QString("Event函数中::鼠标按下了,在 x= %1 y= %2 处").arg(ev->x()).arg(ev->y()); qDebug() << str; return true;//true代表用户自己处理这个事件 } //其他事件交给父类处理 return QLabel::event(e); }
17.QPainter 绘图
(1)绘图事件
void paintEvent(QPaintEvent *ev);
重写了这个画家事件函数就会执行
(2)设置画家
//实例化画家对象
QPainter painter(this);//在当前this窗口下画画,并非设置父类
(3)设置笔
//设置画笔
QPen pen(QColor(0,255,0));
pen.setWidth(3);
pen.setStyle(Qt::DotLine);
//让画家使用这个笔
painter.setPen(pen);
(4)设置画刷
//设置画刷,给封闭图形上色
QBrush brush(Qt::cyan);//Qt::cyan也是颜色
//设置画刷风格
brush.setStyle(Qt::Dense5Pattern);
painter.setBrush(brush);
(5)绘图
//画线
painter.drawLine(QPoint(0,0),QPoint(200,300));
//画圆(椭圆)
painter.drawEllipse(QPoint(300,200),100,100);//圆心,半长轴,半短轴
//画矩形
painter.drawRect(QRect(QPoint(100,0),QPoint(200,100)));
//画文字
painter.drawText(QRect(10,300,100,50),"黄伟聪");
18.QPainter 高级设置
-
设置抗锯齿,效率低
painter.setRenderHint(QPainter::Antialiasing);
-
对画家进行移动
painter.translate(100,0);
-
保存和恢复画家状态
save()
,restore()
-
手动调用绘图事件
update()
-
利用画家画图片
painter.drawPixmap(posX,0,QPixmap(":/myPicture/linux.jpg"));
19.QPaintDevice 绘图设备
(1)有哪些?
QPixmap、QImage、QPicture、QBitmap、QWidget
(2)QPixmap
-
对不同平台做了显示的优化
-
QPixmap pix(300,300); 设置画布大小
-
pix.fill(Qt::white); 背景颜色
-
**QPainter painter(&pix); **利用画家,往pix上画画
-
painter.setPen(QPen(Qt::gray)); painter.drawEllipse(QPoint(150,150),100,100);
-
pix.save("E:\\pix.png");//路径要用\\
(3)QImage
-
可以对像素进行访问
-
QImage::Format_RGB32
//QImage绘图设备 可以对像素访问 QImage image(300,300,QImage::Format_RGB32);//!!! image.fill(Qt::white); QPainter painter(&image); painter.setPen(QPen(Qt::gray)); painter.drawEllipse(QPoint(150,150),100,100); //保存在E盘 image.save("E:\\img.png");
-
像素的访问(修改像素点颜色)
QPainter painter(this); QImage img; img.load(":/E:/myPicture/linux.jpg"); //利用QImage修改像素点 for(int i=1;i<50;i++){ for(int j=1;j<50;j++){ QRgb value = qRgb(0,0,255);//设置RGB颜色 img.setPixel(i,j,value);//修改像素点 } } painter.drawImage(0,0,img);
(4)QPicture
-
可以记录和重现绘图指令
-
需要用到
begin(&pix)
,end()
组合QPicture pic; QPainter painter; painter.begin(&pic); painter.setPen(QPen(Qt::blue)); painter.drawEllipse(QPoint(150,150),100,100); painter.end(); //保存到磁盘 pic.save("E:\\pic.hwc");//后缀名未知,无法打开,在3.中重现
-
重现
QPainter painter(this); //重现QPicture绘图指令,用于打开一些后缀名未知的图片 QPicture pic; pic.load("E:\\pic.hwc"); painter.drawPicture(0,0,pic);
20.QFile 文件读写
-
QFile进行读写操作,默认支持utf-8
-
QString path = QFileDialog::getOpenFileName(this,"打开文件","C:/Users/ROG/Desktop","(*.txt)"); QFile file(path);//参数就是要读取的文件的路径
-
file.open(打开方式)
打开方式:QIODevice::ReadOnly、QIODevice::Append
-
读文件
file.readAll(); 全部读
file.readLine(); 读一行
file.atEnd() 判断是否到达文件尾
//一行一行读完全部 QByteArray arr; while(!file.atEnd())//只要没有到文件尾 { arr += file.readLine();//读一行 }
-
写文件
file.open(QIODevice::Append);//追加方式写 file.write("君不见黄河之水天上来,奔流到海不复回!");
-
最后记得** 关闭文件对象 \textcolor{red}{关闭文件对象} 关闭文件对象**
file.close()
21.QFileInfo 读取文件信息
-
QFileInfo info(文件路径);
-
获取信息
qDebug() << "文件大小:" << info.size() << "文件路径:" << info.path() << "文件名:" << info.fileName() << "文件后缀名:" << info.suffix() << "最后修改时间:" << info.lastModified().toString("yyyy-MM-dd hh:mm:ss") << "创建时间:" << info.birthTime().toString("yyyy-MM-dd hh:mm:ss");
-
时间信息
常用 :
yyyy-MM-dd hh:mm:ss
表达式 输出 d 天 1-31 dd 天 01-31 ddd 星期 ‘Mon’ to ‘Sun’ dddd 星期 ‘Monday’ to ‘Sunday’ M 月份 1-12 MM 月份 01-12 MMM 月 ‘Jan’ to ‘Dec’ MMMM 月 ‘January’ to ‘December’ yyyy 年份 2023