QT入门(自查版)


介绍

Qt是一个跨平台的C++图形用户界面应用程序框架

优点:

  • 接口简单,容易上手
  • 一定程度上简化了内存回收的机制
  • 开发效率高,能快速构建应用程序
  • 社区氛围好
  • 可以进行嵌入式开发

分为商业版(收费)和开源的LGPL协议版本(免费开源版)

Qt的跨平台

QT的跨平台:

原理是一次编码,处处编译(代码一样,分别编译)。

底层封装了针对不同平台的类库,API之类的,上层做了封装,所以接口都一样。不同平台的开发提供了所对应的程序包。

附:区分LinuxMSVCMINGW编译器

#if defined(Q_OS_UNIX)
        qDebug() << "linux环境";
    #elif defined(Q_CC_MSVC)
        qDebug() << "win-MSVC环境";
    #elif defined(Q_CC_MINGW)
        qDebug() << "win-MINGW环境";
    #endif

MSVC编译器

在Windows下,与开发环境以及code编辑器协同更好的还是MSVC(Microsoft Visual C/C++)编译器。对于灵活程度更高的code编辑器,我们可以将Microsoft的Visual C/C++编译器下载并集成到code中。

MINGW

mingw,是Minimalist GNU on Windows 的缩写。它实际上是将经典的开源 C语言 编译器 GCC 移植到了 Windows 下,并且包含了 Win32API ,因此可以将源代码编译生成 Windows 下的可执行程序。

实际上 MinGW 并不是一个 C/C++ 编译器,而是一套 GNU 工具集合。除开 GCC (GNU 编译器集合) 以外,MinGW 还包含有一些其他的 GNU 程序开发工具 (比如 gawk bison 等等)。

Qmake

一、QMake是什么

qmake 是由Qt提供的一个编译打包工具。

二、为什么要学习QMake

学会手动编译,平台移植,快速查找编译错误。

三、QT程序编译经历的步骤

  • 编译pro生成makefile(由qmake完成)。
  • jom或者make编译makefile(生成界面源码,uic.exe widget.ui -o ui_widget.h;生产信号槽代码,moc.exe widget.h moc_widget.cpp)。

四、从代码到执行程序执行经历的步骤

  • 预处理-头文件加载和宏生成cpp。
  • 编译-cpp到.o或者.obj。
  • 链接 so lib o obj res a。(包含静态链接和动态链接)

静态链接:可以把整个隐藏掉,会把库所有用到的函数一个个复制到你的exe当中,造成编译速度非常慢。
动态链接:只是拷贝了一个个函数的地址,并不会拷贝源码。

五、配置vs和qmake环境变量执行qmake生成makefile

  • 创建pro
  • 配置qmake 和 jomx 执行路径
  • 导入vs开发环境

六、qmake引入Qt库创建窗口

  1. 加载QT内部库
    QT += widgets + 空格 + 模块名(库名)
    作用:用来配置Qt系统的库,也包含头文件路径

具体使用哪个库,可通过qt助手查看
加载Qt 内部库 (例如:QT += core gui,去掉对某个库的加载:QT -= gui)

  1. a.exec()的作用

a.exec():所有的消息,通讯,事件,都在此循环处理消息队列中,是阻塞的,直到整个程序退出

  1. 头文件引用
  • INCLUDEPATH += …/…/include + 空格 + 可以加多个路径
message($$PWD)	//可以在项目配置文件中打印内容
INCLUDEPATH += $$PWD/../../include	//相对项目路径
  1. 库引用和库路径指定
  • LIBS += -L"库路径" -l库名(LIBS += -L"…/…/lib" -lopencv_world320)
  • DESTDIR += …/…/bin(输出路径指定)
  • TARGET = 新的可执行文件名(指定输出可执行文件名)

七、QT创建动态库

动态库:Linux或是mac下是 .so,Windows下除了有lib文件还有dll文件,代码中需要导出
静态库:Linux或是mac下是 .a,Windows下只有一个lib文件,代码当中不需要导出

  1. 在Qt Creator中创建动态库步骤
  • 右键新建项目,选择创建C++库

在这里插入图片描述

按照步骤进行下一步即可。

开始操作

看到MSVCMinGW是选择不同的库

MSVC是微软的库

一般是选MinGW

基础文件介绍

创建一个新项目后会看到项目文件如下图所示

点开main.cpp,可以看到

int main(int argc, char *argv[])
{
    //创建一个应用程序对象
    //维护qt应用程序生命的一个对象,每个qt有且仅有一个对象
    QApplication a(argc, argv);

    //窗口类的一个对象
    Widget w;
    //把窗口显示出来
    w.show();
    return a.exec();
}

QApplication是每个qt有且仅有一个对象

QApplication::exec() 是程序的生命循环、消息循环,可以当作是以下形式

while(1){
    if(点击x按钮) break;
    if(点击最小化按钮) 最小化;
    ...
}

关于widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    //宏,引入qt信号和槽的一个宏
    Q_OBJECT

public:
    //parent窗口指针,父窗口对象的指针
    //如果parent为0或者null,表示当前窗口对象是个顶层窗口
    Widget(QWidget *parent = nullptr);
    ~Widget();
};
#endif // WIDGET_H

关于widget.cpp,就是widget.h的实现

#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
}

Widget::~Widget()
{
}

.pro文件介绍

.pro文件类似于VS里的.sln文件,使qmake会产生的一种文件,以该文件规则去生成makefile

内容如下:

QT += core gui  //引入qt的模块,例如库路径索引等,其中QT是工程模块变量
    
//如果qt版本号大于4,就引入widgets模块
greaterThan(QT_MAJOR_VERSION,4):QT+=widgets

//指定目标,生成可执行程序的名字
TARGET = 01_hello
//模板,生成什么文件,app表示应用程序exe,lib是生成库
TAMPLATE = app
    
//如果用了过时的api,就会报warning
DEFINES += QT_DEPRECATED_WARNINGS

//管理导入的文件,删掉中的一个再刷新下qt的界面就会发现对应文件消失
SOURCES += \
        main.cpp\
        widget.cpp\

HEADERS += \
        widget.h

Qt命名规范

  • 类名:单词首字母大写,单词和单词之间直接连接,无需连接字符

    MyClass,QPushButton;
    class MainWindow;
    
  • Qt中内置的类型,头命名和类命名同名

    #include <QString>
    QString str;
    
  • 函数名字,变量名:首字母小写,之后每个单词首字母大写,单词和单词之间直接连接,无需连接字符

    void connectTheSignal();
    
  • 函数成员变量设置函数使用set+成员变量名,获得成员变量的函数直接用成员变量名(如果是bool类型,有可能会用一些表示状态的术语,如isVisilble)

    //普通成员变量设置和获取
    void setText(QString text);
    QString text()const;
    //bool的成员变量设置和获取
    void setEnabled(bool enabled);
    bool isEnabled()const;
    

常用快捷键

  • 运行 ctrl +R
  • 编译 ctrl +B
  • 帮助文档 F1 ,点击F1两次跳到帮助界面
  • 跳到符号定义 F2 或者ctrl + 鼠标点击
  • 注释 ctrl+/
  • 字体缩放 ctrl + 鼠标滚轮
  • 整行移动代码 ctrl + shift + ↑
  • 自动对齐 ctrl + i
  • 同名之间的.h.cpp文件跳转 F4

父子关系

以按钮的示例来说明

int main(int argc, char *argv[])
{
    //创建一个应用程序对象
    //维护qt应用程序生命的一个对象,每个qt有且仅有一个对象
    QApplication a(argc, argv);

    //窗口类的一个对象
    Widget w;

    //添加按钮
    QPushButton btn;
    btn.setText("按钮1");
    //设置父子关系,不然按钮是一个大窗口直接单独显示,而不是在父窗口内显示
    btn.setParent(&w);
    //显示按钮
    btn.show();

    //添加第二个按钮
    QPushButton btn2;
    btn2.setText("按钮2");
    btn2.setParent(&w);
    btn2.move(100,100);
    btn2.show();

    //把窗口显示出来
    w.show();
    return a.exec();
}

效果展示

在这里插入图片描述

Qt坐标系

在这里插入图片描述

常用API

若设置了窗口对象Widget w;

设置窗口标题

w.setWindowTitle("helloworld");

设置窗口的固定大小

w.setFixSize(w,h);

同时设置窗口对的位置和大小

w.setGeometry(x,y,w,h);

因为是widget,所以是说窗口相对于屏幕出现的地方来说的。如果调用的有父子关系,则是相对于父来说的

如果在控件中加上了layout布局,就会发现发现没有办法使用setGeometry函数了,这是因为布局已经被layout管理,没你啥事了。

但是父控件被layout管理,父控件的子控件却没有啊,所以在创建子控件的时候,需要指定子控件的父控件是谁。这样子控件就可以使用

setGeometry函数,可以自由的调整位置,但是只能在父控件的范围内调整位置

界面调整

(17条消息) 自动调整大小–Qt界面小细节(2)_qt adjustsize_十年之少的博客-CSDN博客

对象树

Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象。(QObject是所有Qt对象的基类)

在前面的setParent函数可知,对象之间可以建立父子关系。每个对象都有一个children列表。当父对象在释放析构的时候,会自动释放子对象。

因此,在父对象类内实例化子对象不用delete子对象

代码规范

  • 当然不是都堆叠在了main.cpp里,一般是分类文件编写

    例如在这个widget.cpp文件中

    #include "widget.h"
    #include <QPushButton>
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
       //发现并没有显示出来,因为这里是构造函数,函数结束栈区申请的对象就被析构掉了
    //    QPushButton btn("按钮1",this);
    //    btn.show();
       //为了让对象生命周期更长,可采用如下做法
    //    new
        QPushButton *btn1 = new QPushButton("按钮",this); //指出父对象为本类
    //    由对象树的知识可知,不需要detele
    }
    
    Widget::~Widget()
    {
    }
    
  • QDebug代替cout

案例:点击按钮关闭窗口

该案例中共有四个关键点

按钮、点击 窗口、关闭

用一个connect就可以解决

Widget::Widget(QWidget *parent)
    : QWidget(parent){
	QPushButton *btn = new QPushButton("按钮",this);
    connect(btn,&QPushButton::clicked,this,&Widget::close);
}
Widget::~Widget()
{
}

信号和槽机制

介绍

当某个事件发生后,如某个按钮被点击后,它就会发出一个被点击的信号(signal)。

某个对象接收到这个信号后,就会做一些相关的处理动作,称为(slot)。

但是Qt对象不会无缘无故收到某个信号,要想让一个对象收到另一个对象发出的信号,需要建立连接(connect)。

信号和槽本质都是函数

connect(信号发送者,信号,信号接收者,槽)

connect函数在main.cpp中写时要加上QObject

QObject::connect()

这里的信号和槽都是函数指针,信号发送者和信号接收者都是指针

connect(btn,&QPushButton::clicked,this,&Widget::hide);

为什么信号和槽一般在前面都保留一个&?

  1. 提高代码可读性。一眼知道它是个地址
  2. 打上&符号后Qt会自动帮忙补全后面的名称

每种类都有定义自己的信号和槽,在帮助文档中即可查看。

自定义信号与槽

自定义信号

建立法则

  1. 函数声明在类头文件的signals域下面

  2. 一般为void类型的函数,返回值在这里没有意义

  3. 可以有参数,也可以重载

  4. 只有声明,没有实现定义(不需要实现,仅仅是作为一个触发点)

  5. 触发信号 emit

自定义槽

  1. 函数声明在类头文件的slots域下面(qt4版本以前需要这样,Qt5后可以声明在类任何位置,还可以是静态成员函数、全局函数、lambda表达式)

  2. void类型的函数,没有返回值

  3. 可以有参数,也可以重载

  4. 不仅有声明,还得有实现

注意事项

  • 一个信号可以连接多个槽

    如果一个信号与多个槽建立了多个connect,当信号被触发时,槽函数的调用顺序是随机

  • 一个槽可以连接多个信号

  • 信号可以连接信号,这样触发信号1也会触发信号2,做连锁触发

  • 可以用disconnect断开连接

    connect填的参数一样

  • 信号和槽函数的参数关系必须满足以下两点

    • 第一种情况是信号和槽函数参数类型必须对应
    • 第二种情况是信号和槽函数参数个数不一致,但必须信号函数参数个数>=槽函数参数个数,且参数类型必须对应

下面示例类Teacher实例化的对象触发信号hungry做出槽eat

首先在teacher.h头文件中定义了相应的信号和槽

并在Teacher.cpp中定义槽的具体动作

在这里插入图片描述

Widget.h中声明触发信号的函数

在这里插入图片描述

Widget.cppwidget的构造函数中实例化Teacher对象,并建立相应连接,接着触发信号。

并实现触发函数的功能

信号与槽的二义性

如果信号与槽是带参的,需要重载原来的无参函数,在信号的槽的连接过程中就会出现传入函数不知是哪一个的问题

在解决多义性有两种方法

  1. 使用static_cast类型转换
  2. 转换成函数指针

其实这两种都是转化成函数指针,相比原来的传入函数名多了参数信息

用上面的案例来继续说明

在饿了的时候希望能清楚吃什么东西,因此重载信号函数和槽函数

在这里插入图片描述

实现如下:

在这里插入图片描述

然后是在widget构造函数中

在这里插入图片描述

Qt4的信号与槽以及问题

Qt4的信号与槽的连接跟后代不太一样

格式如下

connect(信号发送者,SIGNAL(函数原型),信号接收者,SLOT(函数原型))

例如上面的例子中就可以写成

connect(p,SIGNAL(hungry(QString)),p,SLOT(eat(QString)));

这样写的好处就是没有重载二义性

坏处就是期间函数名写错了它在编译期间也不报错,运行时候在命令行提示而已

原因是它是使用宏定义将后面的函数名转换成字符串

QDebug转出输出改良

qDebug输出QString的传入参数时会碰到加双引号的情况

解决方法有以下两种

  • QString转化成char *

    qDebug()<<"eating"<<s1.toUtf8().data();
    
  • 使用qDebug().noquote()

    qDebug().noquote()<<"eating"<<s1;
    

Lambda表达式

C++11 中的Lambda表达式用于定义匿名的函数对象,以简化编程工作

分为四个部分:

[局部变量捕获列表]

(函数参数)

函数额外属性设置opt

函数返回值->retype

以及{…}内的函数主体

[capture](parameters) opt->reType
{
    ...
}

举例

void (*p)() = //建立函数指针,并将lambda表达式地址赋给它
    [](){
    qDebug()<<"hello world";
};
p();//调用函数

//可以写的更简单点
[](){
    qDebug()<<"hello world";
}();

关于[局部变量捕获列表]

1、在函数体内部是用不了父作用域的变量,需要将其导入到[局部变量捕获列表]

在默认情况下值传递进来的变量会加上const,在函数体内不能作修改,如果要改的话需要在后加mutable,但是仍是值传递,改变不了外部的变量

这个mutable就是opt

int a = 10;
int b = 20;
[a,b](){ //值传递
    qDebug() << a << " " << b;
}();

[&a,&b](){ //引用传递
    qDebug() << a << " " << b;
}();

[a,b]() mutable{
    a =20;
    b = 30;
}

2、捕获列表的访问形式

  • 表示不捕获任何变量
  • [=] 表示按值传递的方法捕获父作用域的所有变量
  • [&] 表示按引用传递的方法捕获父作用域的所有变量
  • [=, &a] 表示按值传递的方法捕获父作用域的所有变量,但按引用传递的方法捕获变量a
  • [&, a] 表示按引用传递的方法捕获父作用域的所有变量,但按值传递的方法捕获变量a

建议都用[=]形式,防止访问一些被释放掉的内存空间

注意:值传递,在lambda函数定义时就确定了,不会随lambda函数被调用而改变。

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
QPushButton *btn = new QPushButton("按钮",this);
connect(btn,
        &QPushButton::clicked,
       [&](){
           this->close();
       }); //会发现这里少了信号接收者,这里是重载了,其实信号接收者就是this
}

案例:窗口切换

点击主窗口的按钮跳转到子窗口,通常的操作是把主窗口隐藏了,把子窗口show出来。这样操作的原因是主窗口之后很大概率还要再次出现,并且在主窗口和子窗口之间会有逻辑关联,这样做也更好保留了逻辑关联。

在子窗口点击退出后返回到主窗口时,一般都会使用close操作。但是这个close操作并不会释放资源,

close的机制如下:

顶层窗口调用close时应用程序会销毁该窗口部件及子部件

非顶层窗口close时关闭时只是隐藏,不会被销毁

如果要close并且进行资源销毁,要给窗口增加如下设置

setAttribute(Qt::WA_DeleteOnClose);

可实现窗口在Close()后自动释放内存

关于资源释放

上面案例说了一个不容易注意到的资源释放方法。

在Qt中拥有父子关系的在父对象被释放时子对象也跟着被释放掉。

但如果有要及时释放的或者是new出来时就没指定父子关系的,通常需要自行deleteLater掉。

Qt是事件驱动的,当QObject正在接收事件队列时被销毁掉会出错。就是出现crush的错误。所以不推荐直接使用delete

关于deleteLater:

会在当前对象的所有事件处理完成后再删除对象

我们调用这个函数,并不会直接进行delete,而是向事件循环发送了一个delete事件,也就说当控件返回到事件循环时,这个对象才会被删除。

并且多次调用这个函数是安全的;当传递第一个延迟删除事件时,对象的任何挂起事件都将从事件队列中删除。

QString

首先,标准的c++提供了两种字符串:

c语言风格以 ‘\0’ 字符结尾的字符数组;

另一种是子符串类string,Qt使用的QString更强大:提供丰富的操作,查询和转换函数,以及高效的内存分配策略等多方面的优化

  • QString 提供了 “+”, “+=” 二元操作符

    QString str = "Hello ";
    str = str+ "World!";		// str = "Hello World!"; 
    QString str1 = "Hello, ";
    str1 += "QT!";			// str1 = "Hello, QT!"; 
    //  特意查了一下 其构造函数原型   
    //  const char* 类型的指针被QString::dromAscii()函数转换为Unicode编码。
    QT_ASCII_CASR_WARN_CONSTRUCTOR QString::QString(const char* str);
    //  如果字符串有经过QObject::tr() 进行处理,建议使用 QT_CAST_FROM_ASCII 宏变量进行屏蔽这个构造函数
    
  • 除了上面的 “+=” 操作符,QString::append() 函数一样可以实现在末尾追加一个字符串

    QString str = "Hello, ";
    QStying str1 = "江一";
    str.append(str1);			//str = "Hello, 江一";
    str1.append(",小白");		//str1 = "江一,小白"; 
    
  • QString::arg()函数可以进行对字符串的组合,该函数的重载可以处理很多数据类型,比QString::sprintf() 更好用

    QString str;
    str.sprintf("%s","Hello");				// str = Hello
    str.sprintf("%s,%s","world","hello");	//	str = world,hello ;
    
    QString strArg;
    strArg = QString("%1 have been in Suzhou for %2 years").arg("江一").arg(2021); 
    // strArg = "江一 have been in Suzhou for 2021 years";
    
  • QString 还提供了其他字符串的组合方法

    QString::prepend(); // 在原字符串的开头位置插入另一个字符串
    QString::insert();	// 在原字符串的特定位置插入另一字符串
    QString::replace();	// 用给定的字符串替代原来的字符串的某一些字符
    QString::trimmed(); // 清除字符串的两端空白字符('\n' \r' '\t' ' ' 等)
    QString::simplified();	//  清除字符串两端的空白字符,用单个的空格字符 ' ' 替代字符串里面的空白字符
    
  • QString 查询

    QString str = "Hello, QString";
    // 判断字符串是否以某个字符串开头
    str.startsWith("Hello",Qt::CaseSensitive);	// return true ,这里需要注意:第二个参数确定是否要大小写敏感
    str.startWith("QString",Qt::CaseSensitive);	// return false
    // 判断字符串是否以谋某个字符串结尾的
    str.endsWith("QString",Qt::CaseSensitive);	// return true
    // 判断一个字符串有没有出现过
    str.contains("Hello",Qt::CaseSensitive);	// return true
    
  • 类型转换

    bool ok;					// bool用于以下转换返回的状态,成功为true,否则为false
    QString str = "3579"; 
    int dec = str.toInt(&ok,10);	// ok = true, dec = 3579; 
    int hex = str.toInt(&ok,16);	// ok = true, hex = 13689; 整型 十六进制转十进制
    // 注意:第二个参数指定转换基数设置为0时,将使用的是c语言的转换:以 '0x' 开头,基数为16, '0'开头,基数为8, 其他情况一般为10
    double dDec = str.toDouble(&ok,10);	// ok = true, dDec = 3579.0 (这里我补一个0,具体可能不止补一个0,待验证)
    
    QString::toAscii();	 	 // 返回一个ASCII编码格式的8位字符串
    QString::toLatinl();	 // 返回一个Latin-1(ISO8859-1)编码的8位字符串
    QString::toUtf8();	  	 // 返回一个UTF-8 编码格式的8位字符串(UTF-8支持整个Unicode字符集)
    QString::toLoval8Bit();	 // 返回系统本地编码的8位字符串
    QString::number();       // int、double之类的类型数据转换成字符串
    // 例:
    Qstring str = "Welcome to jiangyi's blog!";
    QByteArray array = str.toAscii();	// 将Unicode编码格式的字符串转换为ASCII字符串,储存在array中
    
  • QString字符串的判空等操作

    //  仔细对比以下代码,大家和我一样容易犯的错 '\0' <空字符串未必是一个NULL的字符串>
    QString str;
    str.isNull();	// return true
    str.isEmpty();	// return true
    
    str.append(""); 
    str.isNul();	// return false
    str.isEmpty();  // return true
    

控件

QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个停靠部件(dock widgets)、一个状态栏(status bar)以及一个中心部件(central widget),是许多应用程序的基础,如文本编辑器,图片编辑器等

在这里插入图片描述

QMainWindow可以实例化多个

菜单栏(Menu bar)

先创建一个继承QMainWindow的类,在类的实现中进行如下操作

菜单项的点击信号是QAction::triggered

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //菜单栏,获取当前窗口的菜单栏,没有的话会自动创建一个
    QMenuBar* mb = this->menuBar();
    //添加菜单元素
    QMenu* menuFile = mb->addMenu("文件");
    QMenu* menuEdit = mb->addMenu("编辑");

    //往菜单元素里添加菜单项
    QAction* actionNew = menuFile->addAction("新建");
    QAction* actionOpen = menuFile->addAction("打开");

    //添加分隔符
    menuFile->addSeparator();
    
    //添加二级菜单
    QMenu* menuRecent = menuFile->addMenu("最近打开文件");
    menuRecent->addAction("1.txt");
}

在这里插入图片描述

工具栏(Tool Bar)

    //工具栏,可以有多个
    QToolBar* toolBar = this->addToolBar("工具栏");
    //工具栏添加工具
    toolBar->addAction(actionNew); //这些是前面菜单栏的项
    toolBar->addAction(actionOpen);

    /*发现刚开始工具栏停靠在上边,并且可以上下左右停靠,还可以浮动在任意位置*/
    //设置默认停靠在左边
    this->addToolBar(Qt::LeftToolBarArea,toolBar);

    //只允许停靠在左边或者右边
    //Qt开头的一般内部是枚举,可以进行或操作囊括多个选项
    toolBar->setAllowedAreas(Qt::LeftToolBarArea|Qt::RightToolBarArea);

    //设置工具栏不可以浮动
    toolBar->setFloatable(false);

    //设置工具栏不可以拖动
    toolBar->setMovable(false);

在这里插入图片描述

状态栏(Status Bar)

状态栏只能有一个

    //获取窗口的状态栏
    QStatusBar* sb = this->statusBar();

    //往状态栏里添加信息
    //添加左侧信息
    QLabel* labelLeft = new QLabel("左侧信息",this);
    sb->addWidget(labelLeft);
    //添加右侧信息
    QLabel* labelRight = new QLabel("右侧信息",this);
    sb->addPermanentWidget(labelRight);

在这里插入图片描述

停靠部件(Dock Widget)

可以有多个

    //建立停靠部件
    QDockWidget* dockWidget = new QDockWidget("停靠部件",this); //this是MainWindow的类
    //默认情况下没有核心部件作为参照物,停靠部件会占满整个窗口(就是Dock Widget Area加Central Widget Area)
	//如果核心区域被用了,那么它会压缩回到自己的Dock Widget Area
    this->addDockWidget(Qt::BottomDockWidgetArea,dockWidget);

效果展示:


核心部件(Central)

    //添加核心部件
    QTextEdit* textEdit = new QTextEdit(this);
    this->setCentralWidget(textEdit);

在这里插入图片描述

ui文件使用

在创建了一个带ui的项目中,可以看到在里面定义了一个ui指针,并且在主程序的构造函数里能翻到这句话

在这里插入图片描述

原理是Qt将ui文件转化成了C++的代码

可以利用ui对象给在ui界面创建了的对象进行语句操作

如果想加个工具栏,一般情况是在构造函数下再加一句

QStatusBar* sb = this->statusBar();
QLabel* labelLeft = new QLabel("左侧信息",this);
sb->addWidget(labelLeft);

有了ui对象可以直接进行如下的操作

先在ui界面创建状态栏,找到状态栏的对象名statusBar

在这里插入图片描述

ui->statusBar->addWidget(new QLabel("左侧信息",this));

资源文件的使用

以下例子来说明

在这里插入图片描述

这是它原来的模样

想要修改new带上图标

在这里插入图片描述

在ui界面找到new的对象名

有两种方法

  • 方法一 绝对路径法

    mainwindow.cpp加入这句

    ui->actionnew->setIcon(QIcon("C:\\Users\\37764\\Desktop\\斋藤飞鸟.png"));
    
  • 方法二 使用资源路径

    • 第一步 在项目中创建新文件

    • 创建完会在目录中看到它,右键它,点击Open in Editor

    在这里插入图片描述

    • 点击红框,输入前缀,这里前缀就是存放图片的路径前缀

      在这里插入图片描述

    • 点击add files,把图片存到弹出的框里所示的路径下(在这里目的是将工程文件和图片路径作为整体)

      在这里插入图片描述

    • 这样就是可以了,记得保存

      (这里文件名要英文,我这个是错误示范,不要模仿)

    • 接下来就是在mainwindow.cpp加入设置语句

          //路径格式
          //冒号: + 前缀 / + 目录文件名
          ui->actionnew->setIcon(QIcon(":/new/feiniao.png"));
      

成果展示

在这里插入图片描述

快速建槽函数方法

在ui编辑界面中,建立菜单项

在这里插入图片描述

在这里需要创建delete的槽函数

在下面找到对应delete,选择转到槽

在这里插入图片描述

选择相应的触发信号

在这里插入图片描述

选择完后返回QMainWindow.cpp就会发现已经建好了

在这里插入图片描述

并且connect连接也帮你建立好了!



对话框(QDialog)

对话框分为模态对话框非模态对话框


模态对话框

模态对话框类似于警告窗口,当它弹出来时不点确定则不能点其他窗口

非模态对话框则没有这么苛刻,当它存在时,还是可以点其他窗口

创建模态对话框:

找到菜单栏元素中的一个菜单项,找到它的对象名actionnew

connect(ui->actionnew,&QAction::triggered,[=](){
    //创建一个模态对话框
    QDialog dig(this);
    dig.exec(); 
    qDebug()<<"dialog创建";
});

创建非模态对话框:

connect(ui->actionnew,&QAction::triggered,[=](){
    //创建一个模态对话框
    QDialog *dig = new QDialog(this);
    //让它关闭的时候自动释放
    dig->setAttribute(Qt::WA_DeleteOnClose);
    dig->show();
    qDebug()<<"dialog创建";
});

创建在堆区是因为show函数是非阻塞函数,会导致整条语句很快结束,dig直接被释放,窗口闪一下就没了

但创建在堆区后又没有将其释放,就会导致即使关闭了窗口,对象还留在堆区,导致内存泄漏

解决办法就是加入setAttribute语句,让它关闭的时候自动释放



系统标准对话框

所谓标准对话框,是 Qt 内置的一系列对话框,用于简化开发。事实上,有很多对话框都是通用的,比如打开文件、设置颜色、打印设置等。这些对话框在所有程序中几乎相同,因此没有必要在每一个程序中都自己实现这么一个对话框。

Qt 的内置对话框大致分为以下几类:

  • QColorDialog: 选择颜色;
  • QFileDialog: 选择文件或者目录;
  • QFontDialog: 选择字体;
  • QInputDialog: 允许用户输入一个值,并将其值返回;
  • QMessageBox: 模态对话框,用于显示信息、询问问题等;
  • QPageSetupDialog: 为打印机提供纸张相关的选项;
  • QPrintDialog: 打印机配置;
  • QPrintPreviewDialog:打印预览;
  • QProgressDialog: 显示操作过程。


消息对话框

QMessageBox用于显示消息提示。

  • QMessageBox::critical
  • QMessageBox::information
  • QMessageBox::warning
  • QMessageBox::question

其中quetion比较特殊,它的第三个参数可以指定按钮名称,并且有bool返回值,分别对应两个不同的按钮选择

使用快速建槽函数方法建立槽函数delete

在里面加入警告对话框

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r4Tds3dm-1690907960553)(QT.assets/image-20230211204912452.png)]

效果:

在这里插入图片描述



文件对话框

QFileDialog,也就是文件对话框。

例如下面这个就是一个文件对话框

在这里插入图片描述

这是一个使用该对话框的例子

在这里插入图片描述

其中第三个参数是直接跳到指定的路径位置

第四个参数是过滤打开的文件格式,如下图所示的地方

在这里插入图片描述




布局

所谓 GUI 界面,归根结底,就是一堆组件的叠加。我们创建一个窗口,把按钮放上面,把图标放上面,这样就成了一个界面。在放置时,组件的位置尤其重要。我们必须要指定组件放在哪里,以便窗口能够按照我们需要的方式进行渲染。这就涉及到组件布局定位的机制。

在ui编辑界面可以看到布局的格式

在这里插入图片描述

一般是把表单部件放入widget后加layout来控制效果

并且可以巧用layout下面的弹簧,来控制一些间距的相对与绝对的问题

在这里插入图片描述



按钮

单选按钮遇到互斥域的问题,例如有两个原型选择按钮,分别是男和女,他们是互斥的不嫩同时选,这时就需要用到容器将它们隔离,一般用Group Box



Qt文件操作

用项目来说明


## 读文件

该项目是通过导入txt文件来查看txt文件内容

在这里插入图片描述

在ui界面点击按钮 转到槽

在槽函数中如下编写

void MainWindow::on_pushButton_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,"打开一个txt","./","TXT (*.txt)");
    //对filename做判断,如果没有选择文件,则是空串
    if(fileName.isEmpty()) return;
    //不为空串,得到文件,将它显示到lineEdit
    this->ui->lineEdit->setText(fileName);
    //使用qfile来读取文件
    QFile file(fileName);
    //打开文件
    file.open(QIODevice::ReadOnly);
    //读取文件所有内容
    //全部读取方法
    QByteArray array = file.readAll();
    //按行读写方法
//    do{
//        //单行读取
//        array += file.readLine();
//    }while(!file.atEnd());
    //转化编码成utf-8格式
    QTextCodec *codec = QTextCodec::codecForName("utf-8");
    //将QByteArray转换成QString
    QString content = QString(array);
    //输出到下面的textbrowser上
    this->ui->plainTextEdit->setPlainText(content);
    //关闭文件
    file.close();
}

效果展示

在这里插入图片描述

写文件

    QFile file("./text.txt");
    file.open(QIODevice::ReadOnly|QIODevice::Append);
    file.write("hello");
    file.close();

文件信息获取

相关类名字:QFileInfo

    QFileInfo info(fileName);
    qDebug()<<"文件名:"<<info.fileName();
    qDebug()<<"不带文件类型后缀的文件名:"<<info.baseName();
    qDebug()<<"最后修改时间"<<info.lastModified();

还有很多做法,不多赘述

事件

​ 事件(event)是由系统或者 Qt 应用程序本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。

在帮助文档中搜寻Widget下的Protected Functions,会找到很多后缀为"Event"的虚函数

鼠标事件

示例:显示鼠标操作信息

UI创了一个Label,为了让它能自定义代码修改,于是让它归属一个自定义类

操作如下,先在创建一个自定义的继承QLabel的类MyLabel

之后在UI编辑界面右侧找到Label,右键提升类

在这里插入图片描述

把自己添加的类添加进去,然后点提升

接下来开始写MyLabel

#include "mylabel.h"
#include <QMouseEvent>

MyLabel::MyLabel(QWidget *parent)
    : QLabel{parent}
{
    //默认情况下,窗口不会主动跟踪鼠标
    //只有当某个鼠标按键按下时的情况下才会开始跟踪
    //如果想一开始跟踪,就要使用以下函数
    this->setMouseTracking(true);
}

//记录单个发出的鼠标事件
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
    //输出鼠标事件一些信息
    //获取坐标
    int x = ev->x();
    int y = ev->y();

    //获取鼠标按键
    Qt::MouseButton btn = ev->button();
    QString strButton ="";
    if(btn == Qt::LeftButton) strButton = "LeftButton";
    if(btn == Qt::RightButton) strButton = "RightButton";
    if(btn == Qt::MiddleButton) strButton = "MiddleButton";


    //把坐标信息转换成字符串输出(其中含了html的语句)
    QString str = QString("<h1><center>press[%1,%2][%3]</center></h1>").arg(x).arg(y).arg(strButton);
    this->setText(str);

}

//记录移动的鼠标情况
void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
    //输出鼠标事件一些信息
    //获取坐标
    int x = ev->x();
    int y = ev->y();

    //获取鼠标按键
    //使用buttons可以记录多个状态
    Qt::MouseButtons btns = ev->buttons();
    QString strButton ="";
    //直接与,因为返回状态不同对应二进制的不同位置,合起来才变成btns
    if(btns & Qt::LeftButton) strButton = "LeftButton";
    if(btns & Qt::RightButton) strButton = "RightButton";
    if(btns & Qt::MiddleButton) strButton = "MiddleButton";


    //把坐标信息转换成字符串输出(其中含了html的语句)
    QString str = QString("<h1><center>press[%1,%2][%3]</center></h1>").arg(x).arg(y).arg(strButton);
    this->setText(str);
}

事件分发机制

当某个事件(鼠标、键盘)发生的时候,窗口就会收到这个事件,并且调用相应的事件处理函数,事件处理函数的命名都是以Event结尾的。

这里续上鼠标事件的例子继续写

bool MyLabel::event(QEvent *e)
{
    //返回值:true表示事件得到处理  false则是没被处理,事件会继续传递到父窗口
    //QEvent是所有Event类的父类
    //判断event的类型
    if(e->type()==QEvent::MouseMove){
        this->mouseMoveEvent(static_cast<QMouseEvent*>(e));
        return true;
    }
    //仅处理了MouseMove的情况,其他丢给父类取处理
    return QLabel::event(e);
}

当我把this->mouseMoveEvent(static_cast<QMouseEvent*>(e));注释掉时mouseMoveEvent就不会运行了,因为这个分发过来的MouseMove被简单的return true 了(被拦截了)

如果我直接丢给父类去执行(或者干脆不写这个函数)那么事件都会正常处理

事件过滤器

在这里插入图片描述

事件过滤器的使用分两步:

  1. 窗口调用installEventFilter来安装一个事件过滤器

    (对象可以使用自己作为自己的过滤器,只要类有重写eventFilter

    在这里插入图片描述

  2. 参数是一个事件过滤器对象QObject,该对象的类重写eventFilter的函数

    事件过滤的时候,事件会先到达事件过滤器的eventFilter函数

    返回值:true表示拦截,false表示不拦截,不拦截的情况下事件会继续到达窗口

   //重写eventFilter
   bool MyLabel::eventFilter(QObject *watched, QEvent *event)
   {
       //true表示拦截该事件
       if(event->type()==QEvent::MouseMove) return true;
       return false;
   }


定时器事件

区分定时器和定时器事件,相当于闹钟和闹钟响了

定时器类型1

示例

创建一个类似下图的计时器

在这里插入图片描述

在下面代码中,通过startTimer来启动一个定时器,参数是毫秒,每隔相应的事件就会触发一个定时器事件,返回值就是定时器的id

通过killTimer来杀死一个定时器,参数就是定时器的id

timeEvent定时器事件处理函数中通过event参数获取到当前事件是哪一个定时器发出的event->timeId()

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //定时器id需要进行初始归零,否则跑的时候会出问题
    this->mTimerId1 = 0;
    this->mTimerId2 = 0;
}

Widget::~Widget()
{
    delete ui;
}

void Widget::timerEvent(QTimerEvent *event)
{
    //区分两个定时器
    if(event->timerId()==this->mTimerId1)
    {
        static int num = 1;
        this->ui->lcdNumber->display(num++);
    }
    if(event->timerId()==this->mTimerId2)
    {
        static int num2 = 1;
        this->ui->lcdNumber_2->display(num2++);
    }

}


void Widget::on_pushButton_start_clicked()
{
    //设置定时器会返回id
    this->mTimerId1 = startTimer(1000);//毫秒
}


void Widget::on_pushButton_stop_clicked()
{
    //由于这里传入的是startimer返回的id,所以需要将id计入类成员变量
    killTimer(this->mTimerId1);
    //结束也需要归零
    this->mTimerId1 = 0;
}


void Widget::on_pushButton_start_2_clicked()
{
    this->mTimerId2 = startTimer(100);
}


void Widget::on_pushButton_stop_2_clicked()
{
    killTimer(this->mTimerId2);
    this->mTimerId2 = 0;
}

定时器类型2

这种相对简单很多,通过信号与槽进行连接,并且写在一个按钮槽函数就可以了

void Widget::on_pushButton_start_3_clicked(){
    //创建定时器
    QTimer *timer = new QTimer(this);
    //通过关注信号timeout来接收定时器 到达时间 的信号
    connect(timer,&QTimer::timeout,[=]{
        static int num = 1;
        this->ui->lcdNumber_3->display(num++);
    });
    //启动定时器,参数是触发间隔的毫秒
    timer.start(10);
}

void Widget::on_pushButton_stop_3_clicked(){
    //停止定时器
    timer->stop();
}

绘图事件

来自多个类的函数paintEvent

窗口需要重新显示的时候,就会收到一个绘图事件。收到绘图事件之后,窗口就要将自己画出来,并将画的内容储存到缓冲区,之后展现到屏幕上。

以一个例子来说明。定义了一个继承QPushButtonMyButton类,并重写paintEvent,加入累计的静态变量num,每调用一次paintEvent就自加一次num

之后会发现,每缩放一次界面或是进行按钮点击,都会调用paintEvent,说明每次进行这些操作,按钮都被重新画了一次。

怎么手动调用绘图事件?

有两种方法

方法一:

this->repaint();

方法二:

this->update();

这两个的区别:

repaint会直接触发,如果并行写了很多句repaint也同样会执行

update遇到这种情况会优化,只执行一次

手动调用绘图事件不能写到重写的paintEvent里面去!

画画

了解了绘图事件后,就可以使用绘画类来作图了,只需要重写到对应的paintEvent就可以了

void Widget::paintEvent(QPaintEvent *event)
{
    //实例化相关对象,并指定画画的地方(相当于实例化出一个画家)
    QPainter painter(this);
    //创建一支画笔
    QPen pen;
    pen.setColor(QColor(255,0,0));
    //画家设置画笔
    painter.setPen(pen);
    //画直线
    painter.drawLine(0,0,100,100);//x,y,x2,y2
    //画矩形
    painter.drawRect(20,20,50,50);//x,y,w,h
    //画圆形,实际上是椭圆,然后指定圆心和两个不同的半径
    painter.drawEllipse(QPoint(100,100),50,50);   
}

其他的在帮助文档中查看

绘图设备

绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmapQBitmapQImageQPicture。其中,

  • QPixmap专门为图像在屏幕上的显示做了优化
  • QBitmapQPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap
  • QImage专门为图像的像素级访问做了优化。
  • QPicture则可以记录和重现QPainter的各条命令。

QPixmap继承了QPaintDevice,因此,你可以使用QPainter直接在上面绘制图形。QPixmap也可以接受一个字符串作为一个文件的路径来显示这个文件,比如你想在程序之中打开pngjpeg之类的文件,就可以使用QPixmap。使用QPainterdrawPixmap()函数可以把这个文件绘制到一个QLabelQPushButton或者其他的设备上面。QPixmap是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。注意,这里说的显示设备并不是硬件,而是操作系统提供的原生的绘图引擎。所以,在不同的操作系统平台下,QPixmap的显示可能会有所差别。

QBitmap继承自QPixmap,因此具有QPixmap的所有特性,提供单色图像。QBitmap的色深始终为1. 色深这个概念来自计算机图形学,是指用于表现颜色的二进制的位数。我们知道,计算机里面的数据都是使用二进制表示的。为了表示一种颜色,我们也会使用二进制。比如我们要表示8种颜色,需要用3个二进制位,这时我们就说色深是3. 因此,所谓色深为1,也就是使用1个二进制位表示颜色。1个位只有两种状态:0和1,因此它所表示的颜色就有两种,黑和白。所以说,QBitmap实际上是只有黑白两色的图像数据。

由于QBitmap色深小,因此只占用很少的存储空间,所以适合做光标文件和笔刷。

这里举个例子说一下QPixmap

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    
    QPixmap pix(300,300);
    QPainter painter(&pix);
    painter.drawEllipse(QPoint(150,150),100,100);
    pix.save("./pix.png");
}

效果如下展示

在这里插入图片描述



音效

qt5中用的是QSound,在Qt6中被删除了,用的是QSoundEffect

在这里插入图片描述

在添加音效可以看到下面还有一行qmake开头的,意思是+=后面的要放到.pro文件里去

在这里插入图片描述




打包发布

打包文件提取

1、以release方式编译生成exe文件

在这里插入图片描述

2、将build-…文件夹中的release文件夹中的exe文件复制到将要打包的项目文件夹中

在这里插入图片描述

点击进入

点击进入,将exe文件复制到一个空文件夹中

在这里插入图片描述

在开始菜单Qt中找到Qt 对应的cmd命令行窗口,打开

在这里插入图片描述

需要注意的是:

相关的环境变量配置了没有
选择的编译方式是否匹配
路径是否有中文名
如果上述没有注意,那么容易出错

打开命令行后先定位到打包项目所在的文件夹,然后用windeployqt + 文件名命令将所需要的dll依赖文件加到该文件夹中
在这里插入图片描述

此时该文件夹中就有如下图所示的一些dll文件了,然后再将源项目文件夹中的资源文件复制到该文件夹中,然后就可以直接打包发给他人使用了,双击exe即可使用

如果还报错:

到Qt目录下对应的编译器路径的bin找到缺少的动态库文件,复制到打包文件夹内


整合成压缩包

  • VS打包工具
  • nsis


执行所用时间测量——QElapsedTimer

QElapsedTimer类通常用于快速计算两个事件之间经过了多长时间。它的API与QTime类似,因此正在使用的代码可以快速移植到新类。通常我们计算的时间是用一个 QTime的两次减法,来计算差值,这个类也完全可以胜任。

相关函数

开始计时

void QElapsedTimer::start()

结束计时(并返回时间跨度)

qint64 QElapsedTimer::elapsed() const
//单位毫秒ms


注册QRegisterMetaType

如果要在Qt信号槽中使用自定义类型,需要注意使用qRegisterMetaType对自定义类型进行注册,当然在不跨线程时使用自定义类型signal/slot来传递,可能不会出现什么问题;一旦涉及跨线程就很容易出错。因为是自定义类型,使用qRegisterMetaType来注册,告诉Qt怎么去构造对象。

使用方法:

在main函数里加入 qRegisterMetaType<MyClass>("Myclass");

例如

qRegisterMetaType<QVector<int>>("QVector<int>");



Qt中的线程

在进行桌面应用程序开发的时候, 假设应用程序在某些情况下需要处理比较复杂的逻辑,如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。这种情况下就需要使用多线程,其中一个线程处理窗口事件,其他线程进行逻辑运算,多个线程各司其职,不仅可以提高用户体验还可以提升程序的执行效率。

线程注意事项

  • 默认的线程在Qt中称之为窗口线程,也叫主线程,负责窗口事件处理或者窗口控件数据的更新

  • 子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理(例如修改Label文字之类)

  • 主线程和子线程之间如果要进行数据的传递,需要使用Qt中的信号槽机制


线程类QThread

现在之前说一下怎么查看当前线程

可以调用QObject::thread()查看当前线程,用qDebug把它显示出来

qDebug() << "Current thread:" << thread();

Qt 中提供了一个线程类,通过这个类就可以创建子线程了,Qt 中一共提供了两种创建子线程的方式,后边会依次介绍其使用方式。先来看一下这个类中提供的一些常用 API 函数:

构造函数

QThread::QThread(QObject *parent = Q_NULLPTR);

判断线程中的任务是不是处理完毕

bool QThread::isFinished() const;

判断子线程是不是在执行任务

bool QThread::isRunning() const;

关于优先级

//查看当前线程优先级
Priority QThread::priority() const;
//设置优先级
void QThread::setPriority(Priority priority);
/*----------------------------------------*/
    QThread::IdlePriority         --> 最低的优先级
    QThread::LowestPriority
    QThread::LowPriority
    QThread::NormalPriority
    QThread::HighPriority
    QThread::HighestPriority
    QThread::TimeCriticalPriority --> 最高的优先级
    QThread::InheritPriority      --> 子线程和其父线程的优先级相同, 默认是这个
/*----------------------------------------*/

退出线程(???)

void QThread::exit(int returnCode = 0);
// 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是
// 等待任务完成, 然后退出线程, 一般情况下会在 exit() 后边调用这个函数
bool QThread::wait(unsigned long time = ULONG_MAX);

信号和槽

// 和调用 exit() 效果是一样的
// 代用这个函数之后, 再调用 wait() 函数
[slot] void QThread::quit();
// 启动子线程
[slot] void QThread::start(Priority priority = InheritPriority);
// 线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数
[slot] void QThread::terminate();

/************示范安全退出**************/
/*
Thread->quit();
Thread->wait();
*/
/*************************************/

// 线程中执行的任务完成了, 发出该信号
// 任务函数中的处理逻辑执行完毕了
[signal] void QThread::finished();
// 开始工作之前发出这个信号, 一般不使用
[signal] void QThread::started();

静态函数

// 返回一个指向管理当前执行线程的QThread的指针
[static] QThread *QThread::currentThread();
// 返回可以在系统上运行的理想线程数 == 和当前电脑的 CPU 核心数相同
[static] int QThread::idealThreadCount();
// 线程休眠函数
[static] void QThread::msleep(unsigned long msecs);	// 单位: 毫秒
[static] void QThread::sleep(unsigned long secs);	// 单位: 秒
[static] void QThread::usleep(unsigned long usecs);	// 单位: 微秒

任务处理函数

// 子线程要处理什么任务, 需要写到 run() 中
[virtual protected] void QThread::run();

这个 run() 是一个虚函数,如果想让创建的子线程执行某个任务,需要写一个子类让其继承 QThread,并且在子类中重写父类的 run() 方法,函数体就是对应的任务处理流程。另外,这个函数是一个受保护的成员函数,不能够在类的外部调用,如果想要让线程执行这个函数中的业务流程,需要通过当前线程对象调用槽函数 start() 启动子线程,当子线程被启动,这个 run() 函数也就在线程内部被调用了。




QML


介绍

Qt Quick是Qt5中用户技术界面的涵盖,Qt Quick自身包含如下几种技术:

  • QML - 使用于用户界面的标识语言
  • JavaScript - 动态脚本语言
  • Qt C++ - 具有高度移植性的C++库

重要的一点是,QML帮助Qt实现了前后端分离

并且QML可以在网页中显示,也可以自己为一个应用,也可以在widget中显示,QML也可以显示内嵌网页

QML还能够与JavaScript无缝整合一起开发使用,即在QML代码中可以直接使用JavaScript文件。


创建一个QML项目

注意!是选Qt Quick

在这里插入图片描述


QML语法

  • 每一个QML文件都需要一个根元素(例如Window)
  • 元素拥有属性,他们按照name:value的格式来赋值
  • 任何QML文档中的元素都可以使用它们的id进行访问(id可以是任意的标识符)
  • 元素可以嵌套
  • 通过访问parent关键字来访问它们的父元素

基本元素

  • 元素可以被分为可视化元素和非可视化元素,一个可视化元素(例如Rectangle)有着几何形状并且可以在屏幕上显示。一个非可视化元素(例如计时器Timer)提供了常用的功能,通常用于操作可视化元素
  • 基础的可视化元素有Item(基础元素操作对象),Rectangle(矩形框),Text(文本),Image(图像),MouseArea(鼠标区域)

Item

Item类型是所有可视化元素的基本类型,所有可视化元素都继承自Item。尽管Item对象没有视觉外观,但它定义了所有视觉项的通用属性。Item类型可做为根元素包含视觉项目。

Item用于布局以及组合其他元素。


组件

一个组件是可以重复的元素。一个以文件为基础的组件在文件中创建了一个QML元素,并且将文件以元素类型来命名(例如Button.qml)。你可以像任何其他的QtQuick模块中使用元素一样使用这个组件。
minate();

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值