本文主要是介绍QT入门(自查版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 介绍
- Qt的跨平台
- MSVC编译器
- MINGW
- Qmake
- 开始操作
- 基础文件介绍
- .pro文件介绍
- Qt命名规范
- 常用快捷键
- 父子关系
- Qt坐标系
- 常用API
- 设置窗口标题
- 设置窗口的固定大小
- 同时设置窗口对的位置和大小
- 界面调整
- 对象树
- 代码规范
- 案例:点击按钮关闭窗口
- 信号和槽机制
- 介绍
- 自定义信号与槽
- 自定义信号
- 自定义槽
- 信号与槽的二义性
- Qt4的信号与槽以及问题
- QDebug转出输出改良
- Lambda表达式
- 案例:窗口切换
- 关于资源释放
- QString
- 控件
- 菜单栏(Menu bar)
- 工具栏(Tool Bar)
- 状态栏(Status Bar)
- 停靠部件(Dock Widget)
- 核心部件(Central)
- ui文件使用
- 资源文件的使用
- 快速建槽函数方法
- 对话框(`QDialog`)
- 模态对话框
- 系统标准对话框
- 消息对话框
- 文件对话框
- 布局
- 按钮
- Qt文件操作
- 写文件
- 文件信息获取
- 事件
- 鼠标事件
- 事件分发机制
- 事件过滤器
- 定时器事件
- 绘图事件
- 画画
- 绘图设备
- 音效
- 打包发布
- 打包文件提取
- 整合成压缩包
- 执行所用时间测量——QElapsedTimer
- 注册QRegisterMetaType
- Qt中的线程
- 线程类QThread
- QML
- 介绍
- 创建一个QML项目
- QML语法
- 基本元素
- Item
- 组件
介绍
Qt是一个跨平台的C++图形用户界面应用程序框架
优点:
- 接口简单,容易上手
- 一定程度上简化了内存回收的机制
- 开发效率高,能快速构建应用程序
- 社区氛围好
- 可以进行嵌入式开发
分为商业版(收费)和开源的LGPL
协议版本(免费开源版)
Qt的跨平台
QT的跨平台:
原理是一次编码,处处编译(代码一样,分别编译)。
底层封装了针对不同平台的类库,API之类的,上层做了封装,所以接口都一样。不同平台的开发提供了所对应的程序包。
附:区分Linux
、MSVC
、MINGW
编译器
#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库创建窗口
- 加载QT内部库
QT += widgets + 空格 + 模块名(库名)
作用:用来配置Qt系统的库,也包含头文件路径
具体使用哪个库,可通过qt助手查看
加载Qt 内部库 (例如:QT += core gui,去掉对某个库的加载:QT -= gui)
- a.exec()的作用
a.exec():所有的消息,通讯,事件,都在此循环处理消息队列中,是阻塞的,直到整个程序退出
- 头文件引用
- INCLUDEPATH += …/…/include + 空格 + 可以加多个路径
message($$PWD) //可以在项目配置文件中打印内容
INCLUDEPATH += $$PWD/../../include //相对项目路径
- 库引用和库路径指定
- LIBS += -L"库路径" -l库名(LIBS += -L"…/…/lib" -lopencv_world320)
- DESTDIR += …/…/bin(输出路径指定)
- TARGET = 新的可执行文件名(指定输出可执行文件名)
七、QT创建动态库
动态库:Linux或是mac下是 .so,Windows下除了有lib文件还有dll文件,代码中需要导出
静态库:Linux或是mac下是 .a,Windows下只有一个lib文件,代码当中不需要导出
- 在Qt Creator中创建动态库步骤
- 右键新建项目,选择创建C++库
按照步骤进行下一步即可。
开始操作
看到MSVC
和MinGW
是选择不同的库
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_OBJECTpublic://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();//为了让对象生命周期更长,可采用如下做法 // newQPushButton *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);
为什么信号和槽一般在前面都保留一个&?
- 提高代码可读性。一眼知道它是个地址
- 打上&符号后Qt会自动帮忙补全后面的名称
每种类都有定义自己的信号和槽,在帮助文档中即可查看。
自定义信号与槽
自定义信号
建立法则
-
函数声明在类头文件的signals域下面
-
一般为void类型的函数,返回值在这里没有意义
-
可以有参数,也可以重载
-
只有声明,没有实现定义(不需要实现,仅仅是作为一个触发点)
-
触发信号 emit
自定义槽
-
函数声明在类头文件的
slots
域下面(qt4
版本以前需要这样,Qt5后可以声明在类任何位置,还可以是静态成员函数、全局函数、lambda表达式) -
void类型的函数,没有返回值
-
可以有参数,也可以重载
-
不仅有声明,还得有实现
注意事项
-
一个信号可以连接多个槽
如果一个信号与多个槽建立了多个connect,当信号被触发时,槽函数的调用顺序是随机的
-
一个槽可以连接多个信号
-
信号可以连接信号,这样触发信号1也会触发信号2,做连锁触发
-
可以用
disconnect
断开连接和
connect
填的参数一样 -
信号和槽函数的参数关系必须满足以下两点
- 第一种情况是信号和槽函数参数类型必须对应
- 第二种情况是信号和槽函数参数个数不一致,但必须信号函数参数个数>=槽函数参数个数,且参数类型必须对应
下面示例类Teacher
实例化的对象触发信号hungry
做出槽eat
首先在teacher.h
头文件中定义了相应的信号和槽
并在Teacher.cpp
中定义槽的具体动作
在Widget.h
中声明触发信号的函数
在Widget.cpp
中widget
的构造函数中实例化Teacher
对象,并建立相应连接,接着触发信号。
并实现触发函数的功能
信号与槽的二义性
如果信号与槽是带参的,需要重载原来的无参函数,在信号的槽的连接过程中就会出现传入函数不知是哪一个的问题
在解决多义性有两种方法
- 使用
static_cast
类型转换 - 转换成函数指针
其实这两种都是转化成函数指针,相比原来的传入函数名多了参数信息
用上面的案例来继续说明
在饿了的时候希望能清楚吃什么东西,因此重载信号函数和槽函数
实现如下:
然后是在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 truestr.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 Areathis->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;//不为空串,得到文件,将它显示到lineEditthis->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转换成QStringQString 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 ="";//直接与,因为返回状态不同对应二进制的不同位置,合起来才变成btnsif(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
了(被拦截了)
如果我直接丢给父类去执行(或者干脆不写这个函数)那么事件都会正常处理
事件过滤器
事件过滤器的使用分两步:
-
窗口调用
installEventFilter
来安装一个事件过滤器(对象可以使用自己作为自己的过滤器,只要类有重写
eventFilter
) -
参数是一个事件过滤器对象
QObject
,该对象的类重写eventFilter
的函数事件过滤的时候,事件会先到达事件过滤器的
eventFilter
函数返回值:true表示拦截,false表示不拦截,不拦截的情况下事件会继续到达窗口
//重写eventFilterbool 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()
{//设置定时器会返回idthis->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
窗口需要重新显示的时候,就会收到一个绘图事件。收到绘图事件之后,窗口就要将自己画出来,并将画的内容储存到缓冲区,之后展现到屏幕上。
以一个例子来说明。定义了一个继承QPushButton
的MyButton
类,并重写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
一共提供了四个这样的类,分别是QPixmap
、QBitmap
、QImage
和 QPicture
。其中,
QPixmap
专门为图像在屏幕上的显示做了优化QBitmap
是QPixmap
的一个子类,它的色深限定为1,可以使用QPixmap的isQBitmap()
函数来确定这个QPixmap
是不是一个QBitmap
。QImage
专门为图像的像素级访问做了优化。QPicture
则可以记录和重现QPainter
的各条命令。
QPixmap
继承了QPaintDevice
,因此,你可以使用QPainter
直接在上面绘制图形。QPixmap
也可以接受一个字符串作为一个文件的路径来显示这个文件,比如你想在程序之中打开png
、jpeg
之类的文件,就可以使用QPixmap
。使用QPainter
的drawPixmap()
函数可以把这个文件绘制到一个QLabel
、QPushButton
或者其他的设备上面。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::LowestPriorityQThread::LowPriorityQThread::NormalPriorityQThread::HighPriorityQThread::HighestPriorityQThread::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();
这篇关于QT入门(自查版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!