哔哩哔哩上的视频学习地址最新QT从入门到实战完整版
视频对应下载安装QT方法QT下载安装
Day1
1 Qt的概述
1.1 什么是Qt
Qt是一个跨平台的C++图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形界面所需的所有功能。它是完全面向对象的,很容易扩展,并且允许真正的组件编程。
1.2 Qt的发展史
1991年 Qt最早由奇趣科技开发
1996年 进入商业领域,它也是目前流行的Linux桌面环境KDE的基础
2008年 奇趣科技被诺基亚公司收购,Qt称为诺基亚旗下的编程语言
2012年 Qt又被Digia公司收购
2014年4月 跨平台的集成开发环境Qt Creator3.1.0发布,同年5月20日配发了Qt5.3正式版,至此Qt实现了对iOS、Android、WP等各平台的全面支持。
当前Qt最新版本为 5.8.0
1.3 版本
Qt按照不同的版本发行,分为商业版和开源版
-
商业版
为商业软件提供开发,他们提供传统商业软件发行版,并且提供在商业有效期内的免费升级和技术支持服务。
-
开源的LGPL版本:
为了开发自有而设计的开放源码软件,它提供了和商业版本同样的功能,在GNU通用公共许可下,它是免费的。
1.4 Qt的下载与安装
Qt 5.9 之后的安装包与之前相比,不再区分 VS 版本和 MinGW 版本,而是全都整合到了一个安装包中。因此,与之前的安装包相比,体积也是大了不少。官网下载很慢,所以建议找一些国内镜像站和迅雷下载,具体的下载参考下面的博客。
QT的安装过程下面这个博客写的很详细,贴一下大佬的博客。
1.5 Qt的优点
-
跨平台,几乎支持所有的平台
-
一定程度上简化了内存回收机制
-
可以进行嵌入式开发。
1.6 成功案例
-
Linux桌面环境KDE
-
WPS Office 办公软件
-
Skype 网络电话
-
Google Earth 谷歌地图
-
VLC多媒体播放器
-
VirtualBox虚拟机软件
2 创建Qt程序
略。。。。。。。。
项目文件结构
main.cpp文件
#include "mywidget.h"
#include <QApplication>// 包含一个应用程序类的头文件
//main程序入口 argc命令行变量的数量 argv命令行变量的数组
int main(int argc, char *argv[])
{
//a应用程序对象,在Qt中,应用程序对象 有且仅有一个
QApplication a(argc, argv);
//窗口对象 myWidget父类 -> QWidget
myWidget w;
//窗口对象 默认不会显示,必须要调用show方法显示窗口
w.show();
//让应用程序对象进入消息循环
//当代码阻塞到这行
return a.exec();
//相当于下面
// while(true)
// {
// if(点击叉子)
// {
// break;
// }
// }
}
mywidget.h文件
#ifndef MYWIDGET_H //如果没有宏定义就创建这个宏
#define MYWIDGET_H
#include <QWidget> //包含头文件 QWidget 窗口类
class myWidget : public QWidget
{
Q_OBJECT // Q_OBJECT宏,允许类中使用信号和槽的机制
public:
myWidget(QWidget *parent = 0); //构造函数
~myWidget(); //析构函数
};
#endif // MYWIDGET_H
mywidget.cpp
#include "mywidget.h"
myWidget::myWidget(QWidget *parent)
: QWidget(parent) //初始化队列
{
}
myWidget::~myWidget()
{
}
.pro文件
QT += core gui Qt包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于4版本以上 包含 widget模块
TARGET = 01_FirstProject //目标 生成的.exe程序的名称
TEMPLATE = app //模板 应用程序模板 Application
SOURCES += main.cpp //源文件
mywidget.cpp
HEADERS += mywidget.h //头文件
一些快捷键:
注释:Ctrl + /
整行移动:Ctrl + shift + ↑或↓
帮助文档:F1
自动对齐:Ctrl + i
同名之间.h和.cpp的切换 F4
3 Qt程序
3.1 按钮的创建
//1.创建一个按钮
QPushButton * btn = new QPushButton;
//btn->show(); //show以顶层方式弹出窗口控件
//让btn对象 依赖在 myWidget窗口中
btn->setParent(this);
//显示文本
btn->setText("第一个按钮");
//2.创建第二个按钮 按照控件的大小创建窗口
QPushButton * btn2 = new QPushButton("第二个按钮",this);
//移动btn2按钮
btn2->move(100,100);
//按钮可不可以 重新制定大小 可以!
btn2->resize(50,50);
//重置窗口大小
resize(600,400);
//设置固定窗口大小
setFixedSize(600,400);
//设置窗口标题
setWindowTitle("第一个窗口");
3.2 对象模型树
在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。
-
QObject是以对象树的形式组织起来的。
-
当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。
-
这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。
-
当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。
-
-
QWidget是能够在屏幕上显示的一切组件的父类。
- QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
- 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。
Qt 引入对象树的概念,在一定程度上解决了内存问题。
-
当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
-
任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。
如果QObject在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:
{
QWidget window;
QPushButton quit("Quit", &window);
}
作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidget是QObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。
但是,如果我们使用下面的代码:
{
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
}
情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。
由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。
4 信号和槽
4.1系统自定义信号和槽
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
代码如下(点击按钮关闭窗口)
connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);
connect(sender, signal, receiver, slot);
-
sender:发出信号的对象
-
signal:发送对象发出的信号
-
receiver:接收信号的对象
-
slot:接收对象在接收到信号之后所需要调用的函数(槽函数)
4.2 自定义信号和槽
例子:下课了,(信号)老师饿了,学生(槽)请吃饭
Teacher类定义信号
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = 0);
signals:
//自定义信号 写到signals下
//返回值是void ,只需要声明,不需要实现
//可以有参数,可以重载
void hungry();
public slots:
};
学生类中定义槽
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = 0);
signals:
public slots:
//早期Qt版本 必须要写到public slots,高级版本可以写到 public或者全局下
//返回值 void ,需要声明,也需要实现
//可以有参数,可以发生重载
void treat();
};
void Student::treat()
{
qDebug()<< "请老师吃饭";
}
定义一个ClassIsOver的函数来触发相应的信号
//创建一个老师对象
this->zt = new Teacher(this);
//创建一个学生对象
this->st = new Student(this);
// //老师饿了 学生请客的连接
connect(zt,&Teacher::hungry,st,&Student::treat);
// //调用下课函数
classIsOver();
void Widget::classIsOver()
{
//下课函数,调用后 触发老师饿了的信号
//emit zt->hungry();
//使用emit来触发
emit zt->hungry("宫保鸡丁");
}
自定义信号槽需要注意的事项:
-
发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
-
信号和槽函数返回值是 void
-
信号只需要声明,不需要实现
-
槽函数需要声明也需要实现
-
槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
-
使用 emit 在恰当的位置发送信号;
-
使用connect()函数连接信号和槽。
-
任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
-
信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
-
如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。
4.3 自定义信号和槽出现重载
在写connect时需要利用函数指针明确指向函数的地址,如下:
(函数指针定义方式:函数返回类型(*指针变量名)(函数参数列表))
//connect(ls,&Teacher::hungry,st,&Student::treat);
void(Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
void(Student:: *studengSlot)(QString) = &Student::treat;
connect(ls,teacherSignal,st,studengSlot);
4.4 QString转为Char *
先.ToUtf8()转为QByteArray
再.Data()转为char *
4.5信号可以连接信号
按钮的点击出发了老师的信号,老师的信号触发了学生的信号
注意这里是无参函数,如果把void改成QString会报错,因为第二个connect的信号和槽的参数不一致,(在帮助文档中Signal的信号参数是布尔值)信号参数要大于等于槽的参数个数。
//无参信号和槽连接
void(Teacher:: *teacherSignal2)(void) = &Teacher::hungry;
void(Student:: *studentSlot2)(void) = &Student::treat;
connect(zt,teacherSignal2,st,studentSlot2);
//信号连接信号
connect(btn,&QPushButton::clicked, zt, teacherSignal2);
4.6断开信号操作
disconnect(zt,teacherSignal2,st,studentSlot2);
4.7信号和槽的扩展
-
一个信号可以和多个槽相连
如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
-
多个信号可以连接到一个槽
只要任意一个信号发出,这个槽就会被调用。
-
一个信号可以连接到另外的一个信号
当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
-
槽可以被取消链接
这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
-
信号槽可以断开
利用disconnect关键字是可以断开信号槽的
-
使用Lambda 表达式
在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。
在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。后面我们会详细介绍什么是Lambda表达式
4.8 Lambda表达式
C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。首先看一下Lambda表达式的基本构成:
[capture](parameters) mutable ->return-type
{
statement
}
[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}
1.函数对象参数:
[],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:
- 空。没有使用任何函数对象参数。
- =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
- &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
- this。函数体内可以使用Lambda所在类中的成员变量。
- a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
- &a。将a按引用进行传递。
- a, &b。将a按值进行传递,b按引用进行传递。
- =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
- &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
2.操作符重载函数参数
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
3.可修改标示符;
mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
QPushButton * myBtn = new QPushButton (this);
QPushButton * myBtn2 = new QPushButton (this);
myBtn2->move(100,100);
int m = 10;
connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });
connect(myBtn2,&QPushButton::clicked,this,[=] () { qDebug() << m; });
qDebug() << m;
4.函数返回值;
->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
5.是函数体
{},标识函数的实现,这部分不能省略,但函数体可以为空。
Day1 作业
要求:一个按钮文本是“打开”,点击后打开新窗口,并且按钮文本变为“关闭”,再次点击后,新窗口关闭,并且按钮文本再次变为“打开”。
自己的代码如下。
#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
resize(300,200);
QPushButton * btn = new QPushButton("打开",this);
btn->move(100,100);
//定义是个bool值表示新窗口是否打开,同时控制按钮文本的显示
bool is_open = false;
//定义新窗口
QWidget * new_widget = new QWidget;
//连接信号和槽,按钮点击后,在Lambda表达式中写一个对于是否打开的判断,窗口关闭就打开窗口,窗口打开就关闭
connect(btn,&QPushButton::clicked,new_widget,[=]()mutable{
if(!is_open){
new_widget->show();
btn->setText("关闭");
is_open = true;
}
else{
btn->setText("打开");
new_widget->close();
is_open = false;
}
});
}
Widget::~Widget()
{
}
Day2
1 QMainWindow
QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)及一个中心部件(central widget),是许多应用程序的基础,如文本编辑器,图片编辑器等。
具体看图:
1.1 菜单栏
菜单栏和菜单项的创建
//菜单栏 只能最多有一个
//菜单栏创建
QMenuBar * bar = menuBar();
//将菜单栏放入到窗口中
setMenuBar(bar);
//创建菜单
QMenu * fileMenu = bar->addMenu("文件");
QMenu * editMenu = bar->addMenu("编辑");
//创建菜单项
QAction * newAction = fileMenu->addAction("新建");
//添加分割线
fileMenu->addSeparator();
QAction * openAction = fileMenu->addAction("打开");
1.2 工具栏
//工具栏 可以有多个
QToolBar * toolBar = new QToolBar(this);
addToolBar(Qt::LeftToolBarArea,toolBar);//默认工具栏停靠在左边
//后期设置 只允许 左右停靠
toolBar->setAllowedAreas( Qt::LeftToolBarArea | Qt::RightToolBarArea );
//设置浮动
toolBar->setFloatable(false);
//设置移动 (总开关)
toolBar->setMovable(false);
//工具栏中可以设置内容
toolBar->addAction(newAction);
//添加分割线
toolBar->addSeparator();
toolBar->addAction(openAction);
//工具栏中添加控件
QPushButton * btn = new QPushButton("aa" , this);
toolBar->addWidget(btn);
1.3 状态栏
//状态栏 最多有一个
QStatusBar * stBar = statusBar();
//设置到窗口中
setStatusBar(stBar);
//放标签控件
QLabel * label = new QLabel("提示信息",this);
stBar->addWidget(label);//在左侧提示
QLabel * label2 = new QLabel("右侧提示信息",this);
stBar->addPermanentWidget(label2);//在右侧提示
1.4 铆接部件 (浮动窗口)
QDockWidget * dockWidget = new QDockWidget("浮动",this);
addDockWidget(Qt::BottomDockWidgetArea,dockWidget);//设置默认创建在下面
//设置后期停靠区域,只允许上下
dockWidget->setAllowedAreas( Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea );
2 资源文件
-
将图片文件 拷贝到项目位置下
-
右键项目->添加新文件 –> Qt - > Qt recourse File - >给资源文件起名
-
res 生成 res.qrc
-
open in editor 编辑资源
-
添加前缀 添加文件
-
使用 “ : + 前缀名 + 文件名 ”
3 对话框
3.1基本概念
对话框分为模态对话框和非模态对话框。
-
模态对话框,就是会阻塞同一应用程序中其它窗口的输入。模态对话框很常见,比如“打开文件”功能。你可以尝试一下记事本的打开文件,当打开文件对话框出现时,我们是不能对除此对话框之外的窗口部分进行操作的
-
与此相反的是非模态对话框,例如查找对话框,我们可以在显示着查找对话框的同时,继续对记事本的内容进行编辑。
模块对话框
//模态对话框的创建
connect(ui->actionNew,&QAction::triggered,[=](){
QDialog dlg(this);
dlg.exec();
});
非模块对话框
//创建非模块对话框的时候,如果像下面那个写的话,窗口会弹出一下就消失,因为 这个是在栈上创建的变量,当Lambda中的执行完后就会释放栈中的变量,窗口就消失了,应该在堆上创建
connect(ui->actionNew,&QAction::triggered,[=](){
QDialog dlg2(this);
dlg2.show();
});
//在堆上创建,当你点击x关闭对话框的时候,此时的内存并没有释放,
//所以我们要对这个对话框的属性进行设置,让其点击关闭后释放内存
QDialog * dlg2 = new QDialog(this);
dlg2->show();
dlg2->setAttribute(Qt::WA_DeleteOnClose);
3.2标准对话框
我们要在帮助文档中去看QMessageBox中的内容
我们可以看到其参数类型和返回值,其中通过判断StandarButton的返回值来获取用户点击的类型。
//错误对话框
//QMessageBox::critical(this,"critical","错误");
//信息对话框
//QMessageBox::information(this,"info","信息");
//提问对话框
//参数1 父亲 参数2 标题 参数3 提示内容 参数4 按键类型 参数5 默认关联回车按键
// if (QMessageBox::Save == QMessageBox::question(this,"ques","提问",QMessageBox::Save|QMessageBox::Cancel,QMessageBox::Cancel))
// {
// qDebug() << "选择的是保存";
// }
// else
// {
// qDebug() << "选择的是取消";
// }
//警告对话框
//QMessageBox::warning(this,"warning","警告");
其他标准对话框
//其他标准对话框
//颜色对话框
QColor color = QColorDialog::getColor(QColor(255,0,0));
qDebug() << "r = " << color.red() << " g = " << color.green() << " b = " << color.blue() ;
//文件对话框 参数 1 父亲 参数2 标题 参数3 默认打开路径 参数4 过滤文件格式
//返回值是 选取的路径
QString str = QFileDialog::getOpenFileName(this,"打开文件","C:\Users\zhangtao\Desktop","(*.txt)");
qDebug() << str;
//字体对话框
bool flag;
QFont font = QFontDialog::getFont(&flag,QFont("华文彩云",36));
qDebug() << "字体:" << font.family().toUtf8().data() << " 字号 "<< font.pointSize() << " 是否加粗"<< font.bold() << " 是否倾斜"<<font.italic();
3.3 界面布局
实现登陆窗口
利用布局方式 给窗口进行美化
选取 widget 进行布局 ,水平布局、垂直布局、栅格布局
给用户名、密码、登陆、退出按钮进行布局
默认窗口和控件之间 有9间隙,可以调整 layoutLeftMargin
利用弹簧进行布局
3.4 控件
按钮组
-
QPushButton 常用按钮
-
QToolButton 工具按钮 用于显示图片,如图想显示文字,修改风格:toolButtonStyle , 凸起风格autoRaise
-
radioButton 单选按钮,设置默认 ui->rBtnMan->setChecked(true);
-
checkbox多选按钮,监听状态,2 选中 1 半选 0 未选中
QListWidget 列表容器
//利用listWidget写诗
// QListWidgetItem * item = new QListWidgetItem("锄禾日当午");
// //将一行诗放入到listWidget控件中
// ui->listWidget->addItem(item);
// item->setTextAlignment(Qt::AlignHCenter);
//QStringList QList<QString>
QStringList list ;
list << "锄禾日当午" << "旱地和下土" << "谁知盘中餐"<< "粒粒皆辛苦";
ui->listWidget->addItems(list);
-
QListWidgetItem * item 一行内容
-
ui->listWidget ->addItem ( item )
-
设置居中方式item->setTextAlignment(Qt::AlignHCenter);
-
可以利用addItems一次性添加整个诗内容
QTreeWidget 树控件
//treeWidget树控件使用
//设置水平头
ui->treeWidget->setHeaderLabels(QStringList()<< "英雄"<< "英雄介绍");
QTreeWidgetItem * liItem = new QTreeWidgetItem(QStringList()<< "力量");
QTreeWidgetItem * minItem = new QTreeWidgetItem(QStringList()<< "敏捷");
QTreeWidgetItem * zhiItem = new QTreeWidgetItem(QStringList()<< "智力");
//加载顶层的节点
ui->treeWidget->addTopLevelItem(liItem);
ui->treeWidget->addTopLevelItem(minItem);
ui->treeWidget->addTopLevelItem(zhiItem);
//追加子节点
QStringList heroL1;
heroL1 << "刚被猪" << "前排坦克,能在吸收伤害的同时造成可观的范围输出";
QTreeWidgetItem * l1 = new QTreeWidgetItem(heroL1);
liItem->addChild(l1);
-
设置头
-
ui->treeWidget->setHeaderLabels(QStringList()<< "英雄"<< "英雄介绍");
-
创建根节点
-
QTreeWidgetItem * liItem = new QTreeWidgetItem(QStringList()<< "力量");
-
添加根节点 到 树控件上
-
ui->treeWidget->addTopLevelItem(liItem);
-
添加子节点
-
liItem->addChild(l1);
QTableWidget 表格控件
//TableWidget控件
//设置列数
ui->tableWidget->setColumnCount(3);
//设置水平表头
ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"姓名"<< "性别"<< "年龄");
//设置行数
ui->tableWidget->setRowCount(5);
//设置正文
//ui->tableWidget->setItem(0,0, new QTableWidgetItem("亚瑟"));
QStringList nameList;
nameList<< "亚瑟"<< "赵云"<< "张飞"<< "关羽" << "花木兰";
QList<QString> sexList;
sexList << "男"<< "男"<< "男"<< "男"<< "女";
for(int i = 0 ; i < 5 ;i ++)
{
int col = 0;
ui->tableWidget->setItem(i,col++, new QTableWidgetItem(nameList[i]));
ui->tableWidget->setItem(i,col++, new QTableWidgetItem(sexList.at(i)));
//int 转 QString
ui->tableWidget->setItem(i,col++, new QTableWidgetItem( QString::number(i+18)));
}
stackedWidget栈控件
//栈控件使用
//设置默认定位 scrollArea
ui->stackedWidget->setCurrentIndex(1);
//scrollArea按钮,通过按钮点击切换不同的页面
connect(ui->btn_scrollArea,&QPushButton::clicked,[=](){
ui->stackedWidget->setCurrentIndex(1);
});
下拉框
//下拉框
ui->comboBox->addItem("奔驰");
ui->comboBox->addItem("宝马");
ui->comboBox->addItem("拖拉机");
//点击按钮 选中拖拉机选项
connect(ui->btn_select,&QPushButton::clicked,[=](){
//ui->comboBox->setCurrentIndex(2);
ui->comboBox->setCurrentText("拖拉机");
});
QLabel
//利用QLabel显示图片
ui->lbl_Image->setPixmap(QPixmap(":/Image/butterfly.png"));
//利用QLabel显示 gif动态图片
QMovie * movie = new QMovie(":/Image/mario.gif");
ui->lbl_movie->setMovie(movie);
//播放动图
movie->start();
Day3
1 封装自定义控件
封装一个类似如图的空间,spinBox和horizontalSlider中一个变动,另外一个也会跟着变动,按钮可以对其数值进行设置和获取
1.点击添加新文件 - Qt – 设计师界面类 (.h .cpp .ui),创建要封装的控件名称
2.ui中 设计 QSpinBox和QSlider 两个控件
3.Widget中使用自定义控件,拖拽一个Widget,点击提升为,点击添加封装控件名称,点击提升
4.在封装控件中写信号槽,实现两者value改变的关联
//QSpinBox中的valueChanged有函数的重载,我们需要定义一个匿名函数指针来标识使用函数参数类型
void(QSpinBox:: * spinbox)(int) = &QSpinBox::valueChanged;
connect(ui->spinBox,spinbox,ui->horizontalSlider,&QSlider::setValue);
connect(ui->horizontalSlider,&QSlider::valueChanged,ui->spinBox,&QSpinBox::setValue);
5.在封装类中添加getNum()和setNum()来改变其数值
2 QT中的事件
添加一个类mylabel封装一些鼠标的事件,注意把建好的类修改成
定义鼠标的相应事件
myLabel::myLabel(QWidget *parent) : QLabel(parent)
{
//当设置了这个后,鼠标不用点击,就会输出轨迹的点坐标
setMouseTracking(true);
}
void myLabel::enterEvent(QEvent * event){
//qDebug() << "鼠标进入了";
}
void myLabel::leaveEvent(QEvent*){
//qDebug() << "鼠标离开了";
}
void myLabel::mousePressEvent(QMouseEvent *ev){
if(ev->button()==Qt::LeftButton)
{
//globalX来看你的鼠标在整个屏幕中的,x是看你在这个控件中的坐标
QString str = QString("鼠标按下了x= %1 y=%2 globalx=%3 globaly=%4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
}
}
void myLabel::mouseReleaseEvent(QMouseEvent *ev){
//通过Qt:LeftButton来判断按键的点击类型
if(ev->button()==Qt::LeftButton)
{
QString str = QString("鼠标释放了x= %1 y=%2").arg(ev->x()).arg(ev->y());
qDebug() << str;
}
}
void myLabel::mouseMoveEvent(QMouseEvent *ev){
// if(ev->button()==Qt::LeftButton)
// {
QString str = QString("鼠标移动了x= %1 y=%2").arg(ev->x()).arg(ev->y());
qDebug() << str;
// }
}
Qt中的格式化输出
QString str = QString("鼠标按下了x= %1 y=%2 globalx=%3 globaly=%4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
3 定时器
QTimer * timer = new QTimer(this);
//启动定时器
timer->start(500);
connect(timer,&QTimer::timeout,[=](){
static int num = 1;
//label4 每隔0.5秒+1,括号中将int转为Qstring类型
ui->label_4->setText(QString::number(num++));
});
//点击暂停按钮 实现停止定时器
connect(ui->btn,&QPushButton::clicked,[=](){
timer->stop();
});
4 event事件
用途:用于事件的分发,也可以做拦截(不建议)
所有的事件都会经过event进行分发
bool myLabel::event(QEvent *e){
if(e->type()==QEvent::MouseButtonPress){
//将QEvent类型转为QMouseEvent
QMouseEvent * ev = static_cast<QMouseEvent *>(e);
QString str = QString("Event函数中鼠标按下了x= %1 y=%2 globalx=%3 globaly=%4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
return true;
}
return QLabel::event(e);
}
5 事件过滤器
在程序将事件分发到事件分发器前,可以利用过滤器做拦截
//给label1 安装事件过滤器
// 步骤1 安装事件过滤器
ui->label->installEventFilter(this);
// 步骤2 重写 eventfilter事件
//Qobject参数是看你的触发的空间对象,QEvent是你事件的类型
bool Widget::eventFilter(QObject * obj , QEvent * e)
{
if(obj == ui->label)
{
if(e->type() == QEvent::MouseButtonPress)
{
QMouseEvent * ev = static_cast<QMouseEvent *>(e);
QString str = QString( "事件过滤器中::鼠标按下了 x = %1 y = %2 globalX = %3 globalY = %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
return true; //true代表用户自己处理这个事件,不向下分发
}
}
//其他默认处理
return QWidget::eventFilter(obj,e);
}
6 QPainter绘图
paintEvent是系统会自动调用的函数,可以进行在里写,界面就会显示了
void Widget::paintEvent(QPaintEvent *)
{
// //实例化画家对象 this指定的是绘图设备
// QPainter painter(this);
// //设置画笔
// QPen pen(QColor(255,0,0));
// //设置画笔宽度
// pen.setWidth(3);
// //设置画笔风格
// pen.setStyle(Qt::DotLine);
// //让画家 使用这个笔
// painter.setPen(pen);
// //设置画刷
// QBrush brush(Qt::cyan);
// //设置画刷风格
// brush.setStyle(Qt::Dense7Pattern);
// //让画家使用画刷
// painter.setBrush(brush);
// //画线
// painter.drawLine(QPoint(0,0) , QPoint(100,100));
// //画圆 椭圆
// painter.drawEllipse( QPoint(100,100) , 50,50);
// //画矩形
// painter.drawRect(QRect(20,20,50,50));
// //画文字
// painter.drawText(QRect(10,200,150,50) , "好好学习,天天向上");
//////////////////////////////高级设置 ///////////////////////////////
// QPainter painter(this);
// painter.drawEllipse(QPoint(100,50) , 50,50);
// //设置 抗锯齿能力 效率较低
// painter.setRenderHint(QPainter::Antialiasing);
// painter.drawEllipse(QPoint(200,50) , 50,50);
// 画矩形
// painter.drawRect(QRect(20,20,50,50));
// //移动画家
// painter.translate(100,0);
// //保存画家状态
// painter.save();
// painter.drawRect(QRect(20,20,50,50));
// painter.translate(100,0);
// //还原画家保存状态
// painter.restore();
// painter.drawRect(QRect(20,20,50,50));
}
/////////////////////////////////利用画家 画资源图片 ///////////////////
QPainter painter(this);
QPixmap pix = QPixmap(":/Image/Luffy.png");
//如果超出屏幕 从0开始
if(posX >= this->width())
{
posX = -pix.width();
}
painter.drawPixmap(posX,0,pix);
//点击移动按钮,移动图片
connect(ui->pushButton,&QPushButton::clicked,[=](){
posX+=20;
//如果要手动调用绘图事件 用update更新
update();
});
//和定时器一起,实现图片的滚动
QTimer * timer = new QTimer(this);
timer->start(10);
connect(timer,&QTimer::timeout,[=](){
posX++;
update();
});
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
//显示图片
QPixmap pix = QPixmap(":/Image/Luffy.png");
//如果超出屏幕 从0开始
if(posX >= this->width())
{
posX = -pix.width();
}
painter.drawPixmap(posX,0,pix);
}
7 QPaintDevice绘图设备
QPixmap
// //Pixmap绘图设备 专门为平台做了显示的优化
// QPixmap pix(300,300);
// //填充颜色
// pix.fill(Qt::white);
// //声明画家
// QPainter painter(&pix);
// painter.setPen(QPen(Qt::green));
// painter.drawEllipse(QPoint(150,150) , 100,100);
// //保存
// pix.save("E:\pix.png");
QImage
// QImage 绘图设备 可以对像素进行访问
// QImage img(300,300,QImage::Format_RGB32);
// img.fill(Qt::white);
// QPainter painter(&img);
// painter.setPen(QPen(Qt::blue));
// painter.drawEllipse(QPoint(150,150) , 100,100);
// //保存
// img.save("E:\img.png");
QPicture
QPicture pic;
QPainter painter;
painter.begin(&pic); //开始往pic上画
painter.setPen(QPen(Qt::cyan));
painter.drawEllipse(QPoint(150,150) , 100,100);
painter.end(); //结束画画
//保存到磁盘
pic.save("E:\pic.zt");
8 QFile 对文件进行读写操作
点击按钮,读文件,并显示在textEdit中
connect(ui->pushButton,&QPushButton::clicked,[=](){
QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\Users\zhangtao\Desktop");
//将路径放入到lineEdit中
ui->lineEdit->setText(path);
//编码格式类
//QTextCodec * codec = QTextCodec::codecForName("gbk");
//读取内容 放入到 textEdit中
// QFile默认支持的格式是 utf-8
QFile file(path); //参数就是读取文件的路径
//设置打开方式
file.open(QIODevice::ReadOnly);
//QByteArray array = file.readAll();
QByteArray array;
while( !file.atEnd())
{
array += file.readLine(); //按行读
}
//将读取到的数据 放入textEdit中
ui->textEdit->setText(array);
//ui->textEdit->setText( codec->toUnicode(array) );
//对文件对象进行关闭
file.close();
//进行写文件
// file.open(QIODevice::Append); //用追加方式进行写
// file.write("啊啊啊啊啊");
// file.close();
//QFileInfo 文件信息类
QFileInfo info(path);
qDebug() << "大小:" << info.size() << " 后缀名:" << info.suffix() << " 文件名称:"<<info.fileName() << " 文件路径:"<< info.filePath();
qDebug() << "创建日期:" << info.created().toString("yyyy/MM/dd hh:mm:ss");
qDebug() << "最后修改日期:"<<info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
});
对于读取的文件是GBK还是UTF-8进行先判断,然后再去进行转换
网上一个博客的解决代码如下
QString GetCorrectUnicode(const QByteArray &ba)
{
QTextCodec::ConverterState state;
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QString text = codec->toUnicode( ba.constData(), ba.size(), &state);
if (state.invalidChars > 0)
{
text = QTextCodec::codecForName( "GBK" )->toUnicode(ba);
}
else
{
text = ba;
}
return text;
}