QT模块
QT信号和槽
信号和槽用于多个对象之间的通信
信号和槽为什么问题而诞生?
当一个部件更改时,希望另一个部件知道并相应的做一些事?
其他软件工具包或框架可能使用回调机制实现这种通信机制。一个回调函数是一个指向一个函数的指针,所以如果想让一个处理函数通知一些事件,可以向处理函数传递一个指向另一个函数(回调函数)的指针,然后处理函数在适当的时候调用回调函数。
在QT里是,前一个部件有信号函数,后一个有一个槽函数,我们把前者的信号和后者的槽相连接,就实现了2个对象的通信。
优势
信号和槽机制是类型安全的,信号的参数必须与槽函数的参数相匹配。
信号和槽函数是松耦合的,发送者不关心谁会接受,接收者不关心来自谁
class MyStr :public QObject
{
Q_OBJECT //必须包含的宏
public:
MyStr (){m_value = "zero";}
QString value() const {return m_value;}
public slots :
void setValue(QString value );
signals: //信号
void valueChanged(QString newValue);
private:
QString m_value;
};
特点
信号和槽的参数相匹配,信号的参数可以比槽的参数多,但不能少
它们的参数必须具有相同的顺序和相同的类型
slots来表示槽,而使用signals来表示信号
信号函数不能由开发者实现,它应该由moc自动生成
槽函数需要自己实现
连接
QObject::connect( 发送方, SIGNAL(...), 接收方, SLOT(..) );
MyStr a;
MyStr b; QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString)));
a.setValue("this is A");
一个信号可以连接多个槽
多个信号可以连接同一个槽
一个信号可以和另外一个信号相连接
连接可以被移除
//移除b 与 c之间的连接
QObject::disconnect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
使用
重载
5种不同的连接类型
Qt::AutoConnection
智能设置连接类型
如果接收者和发送者在同一线程,Qt::DirectConnection
否则,Qt::QueuedConnection
Qt::DirectConnection
信号发出时,槽函数立即调用,在发送者线程中执行
Qt::QueuedConnection
函数在接收方的线程中执行。
Qt::BlockingQueuedConnection
与Qt::QueuedConnection相同,但在槽函数返回之前线程会阻塞。如果接收方存在发送方的线程中,不能使用此连接
信号的处理
信号和槽函数机制是完全独立于GUI事件循环的,也并不会干扰GUI的事件循环
emit
语句
emit
语句之后的代码将在所有槽函数都返回之后才执行。如果使用排队连接(queued connections),情况略有不同,在这种情况下,emit关键字后面的代码将立即继续,槽函数将在后续执行。
接触链接
Qt 操作JSON文件
Qt 操作Json格式文件(创建、插入、解析、修改、删除)_qt 创建json-CSDN博客
相关JSON包
#include <QJsonObject> // { }
#include <QJsonArray> // [ ]
#include <QJsonDocument> // 解析Json
#include <QJsonValue> // int float double bool null { } [ ]
#include <QJsonParseError>
封装JSON
// {}对象
QJsonObject catObject;
catObject.insert("name","kity");
catObject.insert("type","haebai");
// []对象
QjsonArray loveArray;
loveArray.append("black");
loveArray.append("asda");
// 创建Json对象的根对象
QJsonObject rootObject;
rootObject.insert("name","chaixing");
rootObject.insert("sex","nan");
rootObject.insert("cat",catObject);
rootObject.insert("loveArray",loveArrayObject);
/*
* "like": [
* { "game": "三国杀", "price": 58.5 },
* { "game": "海岛奇兵", "price": 66.65 }
* ],
*/
// 定义 { } 对象
QJsonObject likeObject1;
likeObject1.insert("game", "三国杀");
likeObject1.insert("price", 58.5);
QJsonObject likeObject2;
likeObject2.insert("game", "海岛奇兵");
likeObject2.insert("price", 66.65);
// 定义 [ ] 对象
QJsonArray likeArray;
likeArray.append(likeObject1);
likeArray.append(likeObject2);
// 创建Json文件对象
QJsonDocument doc;
doc.setObject(rootObject);
// 存储到json文件里
QFile file("../doc/demo1.json");
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)){
print(/....)
}
// 根据该文件生成文本流
QtextStream stream(&file);
stream::setCodec("UTF-8");
stream << doc.toJson();
解析Json
// 创建file对象
Qfile file;
if(!file.open(QFile::READ | QFile::Text)){
...
}
// 创建文件流读取到字符串
QTextStream stream(&file);
stream.setcode("utf-8");
QString str=readAll();
// 报错对象
QjsonParseError jsonError;
// 文档对象
QJsonDocument doc=QJsonDocument::fromJson(str.toUtf8(),&jsonError);
if(jsonError.error!=QjsonParseError::NOErroe && !doc.isNull){
...
}
// 获取根对象
QJsonObdject root=doc.object();
QJsonObject name=root.value("name");
qDebug() << "name = " << name.toString();
// 解析对象
QJsonObject catObject=root.value("cat");
if(catObject.type()==QJsonValue::Object)
视频
【《Qt6 C++开发指南 》2023(上册,完整版)】 https://www.bilibili.com/video/BV1km4y1k7CW/?p=5&share_source=copy_web&vd_source=34cfd49097bfd1017e4a49484def32bb
宏
QT的环境变量在哪?
2.4.2使用CMakeGUI生成VS工程_哔哩哔哩_bilibili
QT模块
Qt Essential
Qt add-on
最常用的:
Qt core :核心,定义了元对象系统
Qt gui :GUI基础类、文字处理、事件处理
Qt Widgets :各种界面组件
Qt D-Bus : 进程间通信(IPC) 和远程过程调用(RPC)
模块需要手动添加
类型
全局常用函数
常用宏
元对象系统
由于C++的RTTI机制只能提供有限的类型信息,于是Qt构建了自己的元对象系统(Meta-Object)。
使用该系统的基类QObject所创建的派生类对象
可以在运行期获取该对象的类名、父类名、枚举类型以及有哪些成员变量、有哪些成员函数等信息。
Qt实现了强大的信号槽机制。
为什么Qt要自己的元对象系统?
为什么需要RTTI?
多态性机制并不能解决这样的场景。假设用一个容器保存这三个指针,此时想更改容器中“圆形”对象的颜色。但是容器中的每一个元素都是Shape *类型的数据,从表面上看是无法判断所指对象的类型,自然就找不到哪个元素是Circle类型的。
由于C++是静态类型语言,有关类的信息只在编译期被使用,编译后就不再保留,因此程序运行时无法获取类的信息。这时就需要使用「运行期类型信息」,即RTTI(Run-Time Type Information)
什么是RTTI?
拥有程序运行时保存对象类型信息能力的语言,我们就称该语言支持RTTI
c++中有限的RTTI
dynamic_cast
dynamic_cast的作用是将指向基类对象的指针转换为指向派生类对象的指针,如果转换失败则返回NULL。
为了判断p所指对象是否具有Son的类型,可以使用语句Son son = dynamic_cast<Son>(p)。如果返回的son不为NULL,则p所指的对象具有Son类型。
typeid
typeid的作用是返回类型的名字。因此,typeid的功能就是在dynamic_cast的范围内进一步的确定指针所指对象的实际类型。
不足
强大的Qt元对象系统
2. 强大的Qt元对象系统
Qt元对象系统的强大在于“即使编译器不支持RTTI,我们也能动态获取类型信息”。例如在任何时候调用QMetaObject::className()函数都会返回类的名称。由于程序运行时保留了类型信息,那么自然就可以进行父子类之间的动态转换。qobject_cast()相比dynamic_cast()强制转换安全得多,而且速度更快。因此,对于QObject派生类之间的转换,推荐使用qobject_cast()。
QObject *obj = new QWidget();
QWidget *widget = qobject_cast<Qwidget *>(obj);
动态属性系统
用元对象系统所提供的动态类型信息,Qt还构建了一套强大的属性系统。由于元对象系统的特点,这就保证了Qt属性系统是独立于编译器和平台的。不仅如此,我们还可以使用Q_PROPERTY()宏来定义编译期的静态属性,使用setProperty()函数动态添加属性
C++中是没有属性概念的,只有成员变量。因为面向对象的思想是抽象封装,属性是类给外部展示的特性。而成员变量属于类的内部信息,直接暴漏出去就破坏了封装性,因为使用者可以对类特性进行直接修改。而属性将取值、赋值的细节进行了封装,外部只能使用它而不能控制它。
使用元对象的前提
只有QObject派生类才有该特性
类的声明要用Q_OBJECT()宏来开启元对象功能。
使用Moc工具为每个QObject派生类提供实现代码(元对象编译器(Meta-Object Compiler) (
简写:moc
),其实是预处理器,最终还是输出标准的c++程序)
功能
QObject::metaObject() 函数返回类的关联 元对象(meta-object) .
QMetaObject::className() 在运行时以字符串形式返回类名,而无需通过C++编译器提供本机运行时类型信息 (RTTI) 支持。
QObject::inherits() 函数返回对象是否是在 QObject 继承树中继承指定类名为参数className的实例
QObject::tr() 翻译国际化的字符串.
QObject::setProperty() 和 QObject::property() ,动态设置属性,和按名称获取属性。
QMetaObject::newInstance() 创建类的新实例。
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
qDebug()<<w.metaObject()->className();
qDebug()<<w.inherits("QObject");
qDebug()<<w.inherits("QApplication");
qDebug()<<w.inherits("aaa");
qDebug()<<w.tr("hello");
// w.show();
auto tmp=w.metaObject();
qDebug()<< tmp->className();
w.setProperty("name","chaixiang");
qDebug()<<w.property("name");
return a.exec();
}
QMetaObject::newInstance()
QMetaObject::newInstance()
函数创建的新实例与直接创建实例的区别在于:
1. 动态创建:`QMetaObject::newInstance()` 允许在运行时根据元对象动态创建实例。这意味着你可以在编译时未知具体类型的情况下创建对象。而直接创建实例需要在编译时指定具体的类类型。
2. 元对象信息:`QMetaObject::newInstance()` 使用类的元对象来创建实例。元对象包含了关于类的信息,包括类名、属性、信号和槽等。因此,通过 QMetaObject::newInstance()
创建的实例会继承类的元对象信息。
3. 默认构造函数:`QMetaObject::newInstance()` 只能创建类的默认构造函数的实例。如果类没有提供默认构造函数,将无法使用 QMetaObject::newInstance()
创建对象。而直接创建实例可以使用不同的构造函数来创建对象。
通常情况下,使用 QMetaObject::newInstance()
可以帮助我们在运行时根据类的元对象创建对象,适用于以下情况:
1. 动态插件系统:当你的应用程序需要支持动态加载插件,并且插件的具体类型在编译时未知时,可以使用 QMetaObject::newInstance()
来创建插件实例。
2. 反射和动态类型创建:当你需要在运行时根据类的元对象动态创建对象,并且不知道类的具体类型时,可以使用 QMetaObject::newInstance()
。这在某些反射和动态类型系统中很有用。
需要注意的是,使用 QMetaObject::newInstance()
创建实例时,你需要确保类已经注册到元对象系统中,即在类的头文件中使用 Q_OBJECT
宏进行声明,并在源文件中使用 Q_DECLARE_METATYPE
宏进行注册。另外,为了避免内存泄漏,确保在不再需要对象时及时释放内存。
findChild函数
男生女生
QT容器
重写了标准库的容器,线程安全!
隐式共享
可能出现2个地方同时对这个容器进行操作,扩大容器可能导致地址改变,导致工作中出错
当不进行写入操作时,使用浅拷贝,发生写入操作后立即深拷贝一份之前的备份
for语法糖
QVariant(c++ union)
可以装任意类型的数据,但是使用该数据时,需要《类型》,QT核心的类型有直接的toXXX函数
QFlag类(枚举)
QRandomGen 随机种子
使用
常用界面组件的使用
布局管理
一组组件最好放进容器里,这样容器变大时,组件会自动变化
0代表够用就好,1代表1份
QString
QString小程序
模式/视图结构
Qt事件处理
Qt 的事件处理3重方式
重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
重写event()函数。event()函数是所有对象的事件入口,QObject和QWidget中的实现,默认是把事件传递给特定的事件处理函数。
在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
事件是如何被分发的
对象的event()方法
创建一个Qevent对象,通过调用event()把事件分发给目标
事件类型
大多数事件有特定的类,尤其是QResizeEvent,QPaintEvent,QKeyEvent,和QCloseEvent.每个类都继承了QEvent并且增加了事件特定的函数。
每个事件都有一个关联的类型,由QEvent::Type定义,这可以被用来作为快速定位给定的事件对象构造的是哪一种子类的运行时类型信息来源。
事件处理器
分发一个事件的常规方法是调用一个虚函数.如果你不在你的虚函数实现中执行必要的工作,你也许需要调用基类的实现。
void MyCheckBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
// handle left mouse button here
} else {
// pass on other buttons to base class
QCheckBox::mousePressEvent(event);
}
}
重写even()
可能发生没有事件特殊处理函数,或者事件特殊处理函数不能满足需求的情况。最常见的例子是Tab键按下。一般地,QWidget会拦截这些来转移键盘焦点,但一小部分部件自己需要Tab键的事件。
bool MyWidget::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if (ke->key() == Qt::Key_Tab) {
// special tab handling here
return true;
}
} else if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
// custom event handling here
return true;
}
return QWidget::event(event);
}
事件过滤器
有时候一个对象需要查看甚至拦截发往另一个对象的事件。例如,对话框一般要为某些部件过滤按键;例如,修改Return键的处理。
当过滤器对象的eventFilter()被调用,它可以接受或拒绝事件,也可以允许或禁止进一步对事件的处理。如果所有的事件处理器都允许进一步处理事件(通过返回false),这个事件就会被发往目标对象自身。如果其中一个过滤器停止了进一步处理(返回true),那么目标对象和后面的过滤器压根就不会收到这个事件。
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if (object == target && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Tab) {
// Special tab handling
return true;
} else
return false;
}
return false;
}
常用于对话框,比如点击删除按钮,可以在对话框里取消,拦截该事件
发送事件
许多应用想要创建和发送他们自己的事件。你可以和Qt自己的事件循环一样去发送事件,通过构建合适的事件对象并且用QCoreApplication::sendEvent()和QCoreApplication::postEvent()来发送事件。
sendEvent()
会立即处理事件。许多的事件类里都有一个函数isAccepted(),它会告诉你事件是被接收处理了还是被最后调用的事件处理器拒绝了。
postEvent()
会把事件传入队列中稍后调度。在下一次Qt事件主循环时,有优化算法把多个事件压缩成一个,例如多次设置大小.
也被用在对象初始化中,因为早对象初始化完成后传递的事件一般将很快被调度
【精选】Qt 事件机制,底层实现原理_qt 事件原理-CSDN博客
事件系统和 信号和槽的区别?
Qt中事件和信号的本质区别_请描述qt的信号和事件,之间有什么区别_王 雄的博客-CSDN博客
Qt的事件系统和信号和槽是 Qt 中两种不同的机制,用于处理程序中发生的特定事件和信号。
### 事件系统
- 事件系统是 Qt 中处理用户输入、操作系统事件等的机制。
- 事件系统基于事件过滤器和事件分发器,它允许对象接收和处理各种事件,如鼠标事件、键盘事件、定时器事件等。
- 事件系统通常用于处理用户交互,例如在用户界面中处理鼠标点击、键盘输入等事件。
### 信号和槽
- 信号和槽是 Qt 中的一种通信机制,用于对象之间的异步通信。
- 信号和槽允许一个对象(信号发出者)发出信号,而另一个对象(槽接收者)在信号被发出时相应地执行槽函数。
- 信号和槽通常用于处理对象之间的通信,例如在界面元素之间传递用户交互、数据改变等。
### 使用场景
- 当需要处理用户交互、外部输入等事件时,可以使用事件系统。例如,处理鼠标点击、键盘输入、定时器事件等。
- 当需要实现对象之间的通信、异步操作,或者处理界面元素之间的交互时,可以使用信号和槽。例如,当一个按钮被点击时,触发另一个对象的某个方法执行。
总的来说,事件系统适合用于处理底层的用户交互和操作系统事件,而信号和槽则适合用于实现对象之间的松耦合通信和交互。在实际应用中,通常会根据具体需求来选择使用事件系统或信号和槽机制。