QT属性系统,简单属性功能快速实现 QT属性的简单理解 属性学习如此简单 一文就能读懂QT属性 QT属性最简单的学习
4.4 属性系统
Qt 元对象系统最主要的功能是实现信号和槽机制,当然也有其他功能,就是支持属性系统。有些高级语言通过编译器的 __property 或者 [property] 等关键字实现属性系统,用于提供对成员变量的访问权限,Qt 则通过自己的元对象系统支持属性访问,Qt 是基于标准 C++ 的,不需要底层编译器支持属性,Qt 本身提供了通用的跨平台的属性系统。关于属性系统可以在 Qt 助手索引里面输入“The Property System”,找到相应的主题文档。Qt 类库大量使用属性,通常开发基于 Qt 的类库时,也会用到属性系统。下面我们介绍简化版的属性系统,只列举了属性系统里面几个基本的条目。
4.4.1 属性系统简介
为了保持类的封装特性,通常成员变量需要保持私有状态,而为了与其他对象协作,就需要提供相应的 get/set 函数。如果成员变量的数值发生了变化,通常也需要提供通知(NOTIFY)信息告知相关对象,Qt 里的通知一般都是使用信号触发。set 函数可以作为槽函数,方便接收相关对象的信号以实现自动调整,比如上一节标签控件的 setText 槽函数。set 函数会导致成员变量数值变化,为了通知相关对象,set 函数里通常会 emit 该成员变量发生变化的信号。
属性系统也是通过元对象系统实现的,它也是需要直接或间接从 QObject 类继承,并在类的声明里需要 Q_OBJECT 宏。下面介绍简化版的属性声明,第一类是不指明与属性相关的私有成员变量,这时必须至少提供该属性的读函数:
Q_PROPERTY(type name
READ getFunction
[WRITE setFunction]
[RESET resetFunction]
[NOTIFY notifySignal] )
Q_PROPERTY()宏就是属性的声明:
- type 是指属性的类型,可以是 C++ 标准类型、类名、结构体、枚举等,name 就是属性的名字。
- READ 标出该属性的读函数 getFunction,Qt 属性的读函数通常省略 get 三个字母。
- WRITE 标出该属性的写函数 setFunction,中括号表示可选,写函数不是必须的。
- RESET 标出该属性的重置函数 resetFunction,重置函数将属性设为某个默认值,中括号表示可选,重置函数不是必须的。
- NOTIFY 标出该属性变化时发出的通知信号 notifySignal,中括号表示可选,这个信号不是必须的。
这仅仅列举了属性声明里简单的几行,复杂需要查阅 Qt 帮助文档。对于属性,注意 name 仅仅是一个用于标识属性的名字,它不是实际存在的成员变量,属性系统不会自动生成成员变量,它就是虚无的名字代号(不同属性的名字不能相同)。对于属性用到的数值会存在一 个真正的私有成员变量里面, 私有成员变量、读函数、写函数、信号等需要另外编写这些声明,对于函数还需要编写实体代码。
上面是不明确指出私有成员变量的情形,也可以明确指出使用了哪个成员变量,这时候属性声明为:
Q_PROPERTY(type name
MEMBER memberName
[READ getFunction]
[WRITE setFunction]
[RESET resetFunction]
[NOTIFY notifySignal] )
这里的 MEMBER 标出属性使用的成员变量 memberName,其他的行与上面的声明类似。
在明确标出属性使用的成员变量的情况下,属性的读写函数可以省略不写,Qt 的 moc 工具会自动为成员变量生成读写代码;而重置函数、信号等需要自己声明,并编写必须的代码;如果声明了属性值变化的通知信号,那么 moc 工具生成的写属性代码会自动触发该通知信号。
如果希望自己的编写的类库支持 QML,那么 NOTIFY 通知信号是必须的,一般建议把成员变量、读函数、写函数、通知信号都明确标出来,这样方便程序员阅读和使用。
4.4.2 普通属性示例
使用 Q_PROPERTY()宏声明的其实都是声明好的普通属性,在程序编译时就已经规定好了,本小节示范普通属性的例子,下一小节示范在运行时动态添加属性。本 小节示范为窗口类添加自定义的属性,然后从 main 函数里面设置和打印属性的值。
打开 QtCreator ,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:
①项目名称 normalpros,创建路径 D:\QtProjects\ch04,点击下一步;
②套件选择里面选择全部套件,点击下一步;
③基类选择 QWidget,点击下一步;
④项目管理不修改,点击完成。
建好项目之后,打开头文件 widget.h,添加属性的声明代码、读写函数声明、信号声明和成员变量:
#ifndef WIDGET_H#define WIDGET_H#include <QWidget>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();//声明属性//不直接标出成员变量的形式Q_PROPERTY(QString nickName READ nickName WRITE setNickName NOTIFY nickNameChanged)//直接标出成员变量的形式Q_PROPERTY(int count MEMBER m_count READ count WRITE setCount NOTIFY countChanged)//标出成员变量,可以省略读写函数Q_PROPERTY(double value MEMBER m_value NOTIFY valueChanged)//nickName读函数声明const QString& nickName();//count读函数int count();//value属性声明时,指出了成员变量并省略了读写函数,它的读写代码由moc工具自动生成//value数值被修改时,valueChanged信号会自动触发//自动生成的读写代码只有简单的读写功能,不能做数值有效性检查signals://三个属性数值变化时发信号void nickNameChanged(const QString& strNewName);void countChanged(int nNewCount);void valueChanged(double dblNewValue);public slots://写函数通常可以作为槽函数,方便与其他信号关联,自动调整数值//nickName写函数声明void setNickName(const QString& strNewName);//count写函数声明void setCount(int nNewCount);//value写代码由 moc 自动生成,没有写函数private:Ui::Widget *ui;//三个私有变量,对应是三个属性QString m_nickName;int m_count;double m_value;
};#endif // WIDGET_H
上面代码声明了三个属性,首先是 nickName:
- 没有直接指出其成员变量,但其实是有的;
- 读函数为 nickName(),返回值为属性里声明的 QString 类型(引用也算的);
- 写函数为 setNickName(),写函数接收 QString 类型参数,并返回 void;
- 当属性值变化时,发送 nickNameChanged 信号,信号参数里会带有新的数值。
属性声明里的读写函数都需要程序员自己编写,信号自己声明,并在写函数里触发。写函数 setNickName() 的声明放到槽函数声明部分了,这样方便关联其他信号,实现自动调整属性值。虽然属性声明里没说有成员变量,但是需要自己定义一个私有变量 m_nickName 保存属性的数值。
第二个属性是 count:
- 直接指出它对应的成员变量是 m_count;
- 读函数是 count(),返回 int 类型;
- 写函数是 setCount(),接收一个 int 参数,并返回 void;
- 当属性值变化时,发送 countChanged 信号,信号参数里会带有新的数值;
count 属性的工作原理 nickName 属性差不多,仅仅是在声明里明确说了对应的成员变量为 m_count。
第三个属性是 value ,这是属性声明的极简形式,读写函数都省略了,属性读写的代码由 moc 工具自动生成。因此这种极简形式的属性,它只需要在头文件里放三行声明代码,其他所有代码都不需要:
Q_PROPERTY(double value MEMBER m_value NOTIFY valueChanged)signals:void valueChanged(double dblNewValue);private:double m_value;
除了属性声明本身的一句,就只需要声明属性值变化是的信号和私有成员变量。
moc 工具自动生成的读写代码仅仅是代码片段(4.5 节专门讲这些代码),没有函数的,自然没有相关的槽函数,所以如果希望把写函数作为槽函数来用,就得用类 似 count 属性的声明,并自己编读写函数的实体代码。
头文件代码就如上编辑,下面来添加 widget.cpp 中必须的函数实体代码:
#include "widget.h"#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}//读函数定义
//读nickName数值
const QString& Widget::nickName()
{return m_nickName;
}
//读count数值
int Widget::count()
{return m_count;
}//写函数,在数值发生变化是才发信号
void Widget::setNickName(const QString &strNewName)
{if(strNewName == m_nickName){//数值没变化,直接返回return;}//修改数值并触发信号m_nickName = strNewName;emit nickNameChanged(strNewName);
}void Widget::setCount(int nNewCount)
{if(nNewCount == m_count){//数值没变化,直接返回return;}//修改数值并触发信号m_count = nNewCount;emit countChanged(nNewCount);
}
在 widget.cpp 只需要为 nickName 和 count 两个属性添加读写函数的代码。读函数 nickName() 和 count() 非常简 单,就是返回相应的私有成员变量数值。
对于写函数 setNickName()里面代码,首先判断传入的参数值是否与旧的值相同,如果是一样的就不用修改,直接返回。如果新旧数值不同,那就设置成员变量为新的值,并发送数 值发生变化的信号 nickNameChanged,并把新值作为参数放到信号里面。set 函数里其实可以顺便做一下数值的有效性检查,上面代码里省略了有效性 检查。
对于写函数 setCount 是类似的过程,新旧数值一样就不变,不一样就修改成员变量并发信号。
窗口类 Widget 的代码就是上面那么多。如何使用这些属性呢?我们可以编辑 main.cpp ,尝试修改和读取这些属性:
#include "widget.h"#include <QApplication>
#include <QDebug>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;//属性读写//通过写函数、读函数w.setNickName( "Wid" );qDebug()<<w.nickName();w.setCount(100);qDebug()<<w.count();//通过 setProperty() 函数 和 property() 函数w.setProperty("value", 2.3456);qDebug()<<fixed<<w.property("value").toDouble();//显示窗体w.show();return a.exec();
}
属性的读写既可以使用各个属性自己的读写函数,如 setNickName()、nickName()、setCount()、count(),也可以使用属性通用的函数:setProperty() 写属性,property() 读属性,都是通过属性的名称来寻找特定属性实现读写。
注意由于上面的 value 属性没有声明读写函数,所以对它的读写只能靠 setProperty() 和 property() 函数,这对通用的属性读写函数的声明如下:
bool QObject::setProperty(const char * name, const QVariant & value)QVariant QObject::property(const char * name) const
setProperty() 第一个参数是普通字符串,就是属性的名称,第二个参数是属性的数值,QVariant 是 Qt 定义的通用变量类型,标准 C++ 的类型和 Qt 自己的数值类型都可以自动转为 QVariant 类的对象。
property() 函数以变量名的字符串作为输入参数,返回一个通用的 QVariant 对象,将 QVariant 对象转为标准类型或者 Qt 数值类型,可以用对应的 to**** 函数,toDouble 是转为双精度浮点数,toInt 是转为整型数, toString 是转为 QString 字符串,还有其他转换函数,详细的请查阅 QVariant 帮助文档。
代码先添加这些,我们运行这个示例,它执行的结果会打印到输出面板,如下图所示:
这个例子展示属性的声明和访问,但是还有一个问题,我们在 set*** 函数里修改了属性值,应该发了值改变的信号,我们没有接收这些信号,还不知道它们真发信号还是假发信号,得验验货。
我们为这个例子添加一个 C++ 类,名字为 ShowChanges,基类为 QObject:
编辑 showchanges.h,为 valueChanged 信号添加对应的槽函数 RecvValue 声明:
#ifndef SHOWCHANGES_H#define SHOWCHANGES_H#include <QObject>class ShowChanges : public QObject
{Q_OBJECT
public:explicit ShowChanges(QObject *parent = 0);~ShowChanges();signals:public slots://槽函数,接收 value 变化信号void RecvValue(double v);
};#endif // SHOWCHANGES_H
然后编辑 showchanges.cpp,为槽函数添加实体代码:
#include "showchanges.h"#include <QDebug>ShowChanges::ShowChanges(QObject *parent) : QObject(parent)
{}ShowChanges::~ShowChanges()
{}
//接收并打印 value 变化后的新值
void ShowChanges::RecvValue(double v)
{qDebug()<<"RecvValue: "<<fixed<<v;
}
这个仅仅是类的代码,接收信号必须要定义一个对象,然后才能关联源头信号和接收端槽函数,下面修改 main.cpp 代码,添加接收端对象和关联函数代码:
#include "widget.h"#include <QApplication>
#include <QDebug>
#include "showchanges.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w; //源头对象//接收端对象ShowChanges s;//关联QObject::connect(&w, SIGNAL(valueChanged(double)), &s, SLOT(RecvValue(double)));//属性读写//通过写函数、读函数w.setNickName( "Wid" );qDebug()<<w.nickName();w.setCount(100);qDebug()<<w.count();//通过 setProperty() 函数 和 property() 函数w.setProperty("value", 2.3456);qDebug()<<fixed<<w.property("value").toDouble();//显示窗体w.show();return a.exec();
}
value 属性值被修改成了 2.3456,这个值一被修改,就会触发 valueChanged 信号,槽函数 RecvValue 被自动调用,并且会接收到新数值并打印出来。运行这个修改后的示例,得到的结果如下:
可以看到,槽函数打印的信息会在 qDebug()<<fixed<<w.property("value").toDouble(); 这行调用之前打印出来。无论使用通用的
setProperty() 函数,还是属性自己独有的 set 函数,变化信号都会正确地发出来。
上面只接收了 valueChanged 信号,还有 nickNameChanged 信号和 countChanged 信号没接收,这个两个信号对应的槽函数和关联函数代码作为练习,请读者自己动手完成。下面小节示范动态属性的使用。
4.4.3 动态属性示例
不仅在源代码里面添加属性声明,程序运行时也可以通过 setProperty() 函数为当前类对象添加动态属性,获取动态属性的值也是用 property() 函数。获取全部的动态属性名称列表可以通过 dynamicPropertyNames 函数:
QList<QByteArray> QObject::dynamicPropertyNames() const
dynamicPropertyNames 返回当前所有动态属性名称字符串的列表。
如果尝试用 property() 函数获取不存在的属性名的数值,那么会得到一个特殊的 QVariant 对象,这个对象处于不可用状态,这时 QVariant 对象的 isValid() 函数返回 false。下面示范动态属性的例子。
重新打开 QtCreator ,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:
①项目名称 dynamicpros,创建路径 D:\QtProjects\ch04,点击下一步;
②套件选择里面选择全部套件,点击下一步;
③基类选择 QWidget,点击下一步;
④项目管理不修改,点击完成。
建好项目之后,Widget 类的代码不需要修改,动态属性不需要额外声明,直接在 main 函数里面使用就行了,我们编辑 main.cpp:
#include "widget.h"#include <QApplication>
#include <QDebug>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;//如果动态属性不存在,QVariant 对象的 isValid() 函数返回 false,变量不可用qDebug()<<w.property("myValue").isValid();//添加属性 myValuew.setProperty("myValue", 2.3456);//获取并打印qDebug()<<w.property("myValue").toDouble();//添加属性 myNamew.setProperty("myName", "Wid");//获取并打印qDebug()<<w.property("myName").toString();//获取所有动态属性名称,打印名称列表qDebug()<<w.dynamicPropertyNames();w.show();return a.exec();
}
在 main 函数里,我们先尝试获取一个不存在的属性 myValue,property() 函数返回 QVariant 对象,由于属性不存在,所以 QVariant 对象 isValid() 函数会返回 false,表明不可用状态,也就意味着该属性是不存在的。
然后我们向窗体对象 w 添加了动态属性 myValue,并用 property() 函数获取了该属性的 QVariant 对象,然后转成双精度浮点数打印。接着我们向窗体对象 w 添加了动态属性 myName,类似地用 property() 函数获取属性的 QVariant 对象,然后转为字符串打印。
最后我们用窗体对象 w 的 dynamicPropertyNames() 函数获取了所有的动态属性名称列表,并打印了动态属性名称列表。
例子修改的代码只有上面那些,例子运行的效果如下图所示:
通过在头文件声明普通的属性,可以为属性标明相关的成员变量、读函数、写函数、变化通知信号等等,而动态属性就没有这些特性,它其实就是以成对的属性名、属性值的 形式存在,可以通过 setProperty() 和 property() 读写,仅此而已,所以动态属性应用得比较少。
属性系统不仅可以声明普通属性和在运行时添加动态属性,还有一个衍生功能,就是为类添加一些附加信息,运行时可以查询这些附加信息,方便程序员或用户查询相关信 息,下面学习为类添加附加信息的例子。
4.4.4 类的附加信息
Qt 程序运行时可以查询当前对象的名称,我们之前 4.2.3 节用 QObject::objectName() 函数获取过对象名称。运行时还可以查询类的信息,主要包括两种类信息,一种是基本的类信息,Qt 类会自动生成这些信息,包括当前类名、继承树上的基类名称等;另一类是程序员手动添加的类附加信息,通过 Q_CLASSINFO() 宏,在类的声明里添加类附加信息,比如:
Q_CLASSINFO("Version", "1.0.0")
附加信息也是成对的“名称-值”,不过注意 名称和值 都是普通字符串。如果希望在运行时查询类的附加信息,可以先用 QObject::metaObject() 获取当前对象包含的元对象数据,然后使用 QMetaObject::classInfo 函数查询某个序号的附加信息:
const QMetaObject * QObject::metaObject() const [virtual]
QMetaClassInfo QMetaObject::classInfo(int index) const
对于基本信息,可以用 QMetaObject::className() 函数获取类名。如果要判断继承树上是否有某个基类,使用 QObject::inherits() 函数判断。这两个函数声明如下:
const char * QMetaObject::className() const
bool QObject::inherits(const char * className) const
下面示范类的附加信息例子,并顺路打印类的基本信息,判断基类名。
重新打开 QtCreator ,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:
①项目名称 classinfo,创建路径 D:\QtProjects\ch04,点击下一步;
②套件选择里面选择全部套件,点击下一步;
③基类选择 QWidget,点击下一步;
④项目管理不修改,点击完成。
建好项目之后,编辑 widget.h 头文件,添加附加信息:
#ifndef WIDGET_H#define WIDGET_H#include <QWidget>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();//类的附加信息Q_CLASSINFO("Version", "1.0.0")Q_CLASSINFO("Author", "Winland")Q_CLASSINFO("Site", "https://lug.ustc.edu.cn/sites/qtguide/")private:Ui::Widget *ui;
};#endif // WIDGET_H
头文件里添加了三行,也就是三对“名称-值”,都是普通字符串。附加信息只需要添加到声明里,不需要其他代码,所以 widget.cpp 文件保持原样,不修改。
下面我们在 main.cpp 里面访问窗体的附加信息和基本信息:
#include "widget.h"#include <QApplication>
#include <QDebug>
#include <QMetaClassInfo>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;//获取类的附加信息const QMetaObject *pMO = w.metaObject();//附加信息个数int nInfoCount = pMO->classInfoCount();//打印所有的附加信息for(int i=0; i<nInfoCount; i++){QMetaClassInfo info = pMO->classInfo(i);qDebug()<<info.name()<<"\t"<<info.value();}//基本信息qDebug()<<"Class Name: "<<pMO->className();qDebug()<<"Object Name: "<<w.objectName();//判断是否为基类qDebug()<<w.inherits("QWidget");qDebug()<<w.inherits("nothing");//显示窗口w.show();return a.exec();
}
文件开头添加了 <QDebug> 和 <QMetaClassInfo> 两个头文件,QMetaClassInfo 就是存储附加信息项的类,它有两个公开函数:name() 获取附加信息项的名称,value() 获取附加信息项的值。
在 main 函数里,定义好窗体对象 w,之后,先通过 metaObject() 函数得到一个 QMetaObject 常量指针 pMO;
通过 pMO->classInfoCount() 获知附加信息项的个数;
然后在 for 循环里枚举 pMO->classInfo(i) 信息项,打印信息项的名称和值。
接下来是一些基本信息的获取,类的名字也是通过元对象指针获取 pMO->className();
窗体对象名可以直接用 w.objectName() 获取;
判断窗体是否从某个基类派生,使用 inherits 函数,这个函数接收一个字符串,如果是对象的直接或间接基类名称就返回 true,否则返回 false。
这个示例运行结果如下图所示(这里窗体的类名和对象名正好是一样的,其他情况可能不一样):
相关文章:

QT属性系统,简单属性功能快速实现 QT属性的简单理解 属性学习如此简单 一文就能读懂QT属性 QT属性最简单的学习
4.4 属性系统 Qt 元对象系统最主要的功能是实现信号和槽机制,当然也有其他功能,就是支持属性系统。有些高级语言通过编译器的 __property 或者 [property] 等关键字实现属性系统,用于提供对成员变量的访问权限,Qt 则通过自己的元对…...

【IEEE出版丨EI检索】2024新型电力系统与电力电子国际会议(NPSPE 2024)
2024新型电力系统与电力电子国际会议(NPSPE 2024)将于8月16日至18日在中国大连举行,本届大会致力于为相关领域的专家和学者提供一个探讨行业热点问题,促进科技进步,增加科研合作的平台。本届大会涵盖新型电力系统和电力…...

【Netty】nio阻塞非阻塞Selector
阻塞VS非阻塞 阻塞 阻塞模式下,相关方法都会导致线程暂停。 ServerSocketChannel.accept() 会在没有建立连接的时候让线程暂停 SocketChannel.read()会在没有数据的时候让线程暂停。 阻塞的表现就是线程暂停了,暂停期间不会占用CPU,但线程…...
ES 操作
1、删除索引的所有记录 curl -X POST "localhost:9200/<index-name>/_delete_by_query" -H Content-Type: application/json -d {"query": {"match_all": {}} }POST /content_erp_nlp_help/_delete_by_query { "query": { &quo…...
uniapp如何实现跳转
在 UniApp 中,页面跳转主要可以通过两种方式实现:使用 <navigator> 组件和调用 UniApp 提供的导航 API。以下是这两种方式的详细说明: 1. 使用 <navigator> 组件 <navigator> 组件允许你在页面上创建一个可点击的元素&am…...

Stable-Diffusion-WebUI 常用提示词插件
SixGod提示词插件 SixGod提示词插件可以帮助用户快速生成逼真、有创意的图像。其中包含,清空正向提示词”和“清空负向提示词、提示词起手式包含人物、服饰、人物发型等各个维度的提示词、一键清除正面提示词与负面提示词、随机灵感关键词、提示词分类组合随机、动…...

单片机 PWM输入捕获【学习记录】
前言 学习是永无止境的,就算之前学过的东西再次学习一遍也能狗学习到很多东西,输入捕获很早之前就用过了,但是仅仅是照搬例程没有去进行理解。温故而知新! 定时器 定时器简介 定时器的分类 高级定时器 通用定时器 基本定时器…...

3.1、前端异步编程(超详细手写实现Promise;实现all、race、allSettled、any;async/await的使用)
前端异步编程规范 Promise介绍手写Promise(resolve,reject)手写Promise(then)Promise相关 API实现allraceallSettledany async/await和Promise的关系async/await的使用 Promise介绍 Promise是一个类,可以翻…...
3.1. 马氏链-马氏链的定义和示例
马氏链的定义和示例 马氏链的定义和示例1. 马氏链的定义2. 马氏链的示例2.1. 随机游走2.2. 分支过程2.3. Ehrenfest chain2.4. 遗传模型2.5. M/G/1 队列 马氏链的定义和示例 1. 马氏链的定义 对于可数状态空间的马氏链, 马氏性指的是给定当前状态, 其他过去的状态与未来的预测…...

红利之外的A股底仓选择:A50
内容提要 华泰证券指出,当前指数层面下行风险不大,市场再入震荡期下,可关注三条配置线索:1)A50为代表的产业巨头;2)以家电/食饮/物流/出版为代表的稳健消费龙头,3)消费电…...

wondershaper 一款限制 linux 服务器网卡级别的带宽工具
文章目录 一、关于wondershaper二、文档链接三、源码下载四、限流测试五、常见报错1. /usr/local/sbin/wondershaper: line 145: tc: command not found2. Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist: No URLs.. 一、关于wonder…...
独孤思维:盲目进群,根本赚不到钱
01 我看有些伙伴,对标同行找写作素材和灵感的时候。 喜欢把对标文章发给ai提炼总结。 这个方法好是好,但是,有一个问题。 即,无法感受全文的细节。 更无法感受作者的情感和温度。 就好像电影《记忆大师》一样。 我提取了记…...

针对indexedDB的简易封装
连接数据库 我们首先创建一个DBManager类,通过这个类new出来的对象管理一个数据库 具体关于indexedDB的相关内容可以看我的这篇博客 indexedDB class DBManager{}我们首先需要打开数据库,打开数据库需要数据库名和该数据库的版本 constructor(dbName,…...

网络编程--网络理论基础(二)
这里写目录标题 网络通信流程mac地址、ip地址arp协议交换机路由器简介子网划分网关 路由总结 为什么ip相同的主机在与同一个互联网服务通信时不冲突公网ip对于同一个路由器下的不同设备,虽然ip不冲突,但是因为都是由路由器的公网ip转发通信,接…...

Python MongoDB 基本操作
本文内容主要为使用Python 对Mongodb数据库的一些基本操作整理。 目录 安装类库 操作实例 引用类库 连接服务器 连接数据库 添加文档 添加单条 批量添加 查询文档 查询所有文档 查询部分文档 使用id查询 统计查询 排序 分页查询 更新文档 update_one方法 upd…...
Node.js 入门:
Node.js 是一个开源、跨平台的 JavaScript 运行时环境,它允许开发者在浏览器之外编写命令行工具和服务器端脚本。以下是一些关于 Node.js 的基础教程: 1. **Node.js 入门**: - 了解 Node.js 的基本概念,包括它是一个基于 Chro…...
java8 List的Stream流操作 (实用篇 三)
目录 java8 List的Stream流操作 (实用篇 三) 初始数据 1、Stream过滤: 过滤-常用方法 1.1 筛选单元素--年龄等于18 1.2 筛选单元素--年龄大于18 1.3 筛选范围--年龄大于18 and 年龄小于40 1.4 多条件筛选--年龄大于18 or 年龄小于40 and sex男 1.5 多条件筛…...

机器学习python实践——数据“相关性“的一些补充性个人思考
在上一篇“数据白化”的文章中,说到了数据“相关性”的概念,但是在统计学中,不仅存在“相关性”还存在“独立性”等等,所以,本文主要对数据“相关性”进行一些补充。当然,如果这篇文章还能入得了各位“看官…...

MySQL——触发器(trigger)基本结构
1、修改分隔符符号 delimiter $$ $$可以修改 2、创建触发器函数名称 create trigger 函数名 3、什么样在操作触发,操作哪个表 after :……之后触发 before :……之后触发 insert :……之后触发 update :……之后触…...

数字孪生定义及应用介绍
数字孪生定义及应用介绍 1 数字孪生(Digital Twin, DT)概述1.1 定义1.2 功能1.3 使用场景1.4 数字孪生三步走1.4.1 数字模型1.4.2 数字影子1.4.3 数字孪生 数字孪生地球平台Earth-2 参考 1 数字孪生(Digital Twin, DT)概述 数字孪…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...