QT属性系统,简单属性功能快速实现 QT属性的简单理解 属性学习如此简单 一文就能读懂QT属性 QT属性最简单的学习

news2024/11/24 11:48:54

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_OBJECT

public:
    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 帮助文档。

代码先添加这些,我们运行这个示例,它执行的结果会打印到输出面板,如下图所示:

run1


这个例子展示属性的声明和访问,但是还有一个问题,我们在 set*** 函数里修改了属性值,应该发了值改变的信号,我们没有接收这些信号,还不知道它们真发信号还是假发信号,得验验货。
我们为这个例子添加一个 C++ 类,名字为 ShowChanges,基类为 QObject:

class

编辑 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 被自动调用,并且会接收到新数值并打印出来。运行这个修改后的示例,得到的结果如下:

run2

可以看到,槽函数打印的信息会在 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();

    //添加属性 myValue
    w.setProperty("myValue", 2.3456);
    //获取并打印
    qDebug()<<w.property("myValue").toDouble();

    //添加属性 myName
    w.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() 函数获取了所有的动态属性名称列表,并打印了动态属性名称列表。

例子修改的代码只有上面那些,例子运行的效果如下图所示:

run

通过在头文件声明普通的属性,可以为属性标明相关的成员变量、读函数、写函数、变化通知信号等等,而动态属性就没有这些特性,它其实就是以成对的属性名、属性值的 形式存在,可以通过 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_OBJECT

public:
    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。

这个示例运行结果如下图所示(这里窗体的类名和对象名正好是一样的,其他情况可能不一样):

run

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1831469.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从零开始学GeoServer源码(一)(搭建开发环境Win10+IDEA23.3.5+jdk11+geoserver2.24.x)

搭建开发环境 参考资料 0、基础环境准备0.1、idea0.2、jdk0.3、源码 1、导入工程2、配置启动环境2.1、打开新增配置面板2.2、配置工作目录2.2.1、从常用配置中选择2.2.2、直接粘贴 2.3最终效果 3、调整源码3.1、添加maven引用3.2、注释无效代码3.3、删除测试代码 4、修改运行端…

论文《Dual-Contrastive for Federated Social Recommendation》阅读

论文《Dual-Contrastive for Federated Social Recommendation》阅读 论文概况MotivationMethodologyClient Local ComputingCenter Server Aggregation 总结 今天简单总结一下一篇关于联邦推荐方面的论文《Dual-Contrastive for Federated Social Recommendation》&#xff0c…

Kubernetes 1.18 部署 Traefik2.0

Kubernetes 1.18部署 Traefik2.0 参考资料&#xff1a; Traefik 2.0 官方文档&#xff1a;https://doc.traefik.io/traefik/v2.0/Kubernetes 1.18.3 部署 Traefik2.0&#xff1a;https://www.cnblogs.com/heian99/p/14608414.html 1. Traefik 介绍 traefik 是一款反向代理、…

“我们系统太丑了,怎么办?”一文告知你B端升级的正确姿势

我经常听到客户吐槽自己的B端系统不好看&#xff0c;客户老是吐槽&#xff0c;但是自己的工程师又是那个水平也不能强求&#xff0c;也找过一些外部设计师&#xff0c;设计的界面还不如原来的&#xff0c;这种情况下给怎么办呢&#xff1f;本位为大家解答下。 一、B端系统界面…

Git管理(Linux版本)

在Linux中我们如何把自己的代码上传到gitee中呢&#xff0c;本期将为大家讲解详细的步骤。 目录 查看Linux环境是否存在git工具 在gitee上创建代码仓库 复制仓库的HTTP路径到Linux中 代码上传 在仓库下创建文件或者将文件移动到仓库下 使用三板斧进行文件的上传 add …

IT入门知识大纲(0/10)

IT入门知识大纲 引言 什么是IT&#xff1f; 信息技术&#xff08;Information Technology&#xff09;&#xff0c;互联网技术是指在计算机技术的基础上开发建立的一种信息技术 。互联网技术通过计算机网络的广域网使不同的设备相互连接&#xff0c;加快信息的传输速度和拓宽信…

过滤器和拦截器的基本知识

过滤器和拦截器的基本知识 前提提要&#xff0c;由于过滤器和拦截器的相关函数是没有返回值的&#xff0c;所以我们像前端返回数据时需要使用到HttpServletResponse中的相关函数&#xff0c;且需要配合阿里巴巴的插件fastjson将返回信息转为json格式&#xff0c;或者使用gson转…

网络运维:企业数字化转型的基石

在数字化时代&#xff0c;网络已经成为企业运营不可或缺的一部分。网络运维作为确保网络稳定、安全和高效运行的关键环节&#xff0c;其重要性不言而喻。本文将探讨网络运维的重要性&#xff0c;并分析其在企业数字化转型中的作用。 网络运维的定义 网络运维&#xff0c;也称…

(自动深色模式)Windows 10 Auto Dark Mode

需求 想让Win10像手机那样&#xff0c;到点自动切换深色模式/浅色模式 解决方案 Windows 10 Auto Dark Mode – Win10 自动深色模式 GitHub&#xff1a;https://github.com/Armin2208/Windows-Auto-Night-Mode/

【Python】PySide6使用入门和注意事项

文章目录 前言关于PySide和PyQtQt Designerpyside6在vscode中ui文件转换兼容性问题主程序结构蓝牙协议初探&#xff08;应用层&#xff09; 前言 最近在开发一个带界面的软件&#xff0c;需要使用蓝牙&#xff0c;然后找到一个开源仓库使用的是Qt里面的Qbluetooth模块&#xff…

使用ARK工具ATool清除典型蠕虫MyDoom

1 概述 在长期的日常安全事件监测过程中&#xff0c;安天CERT经常捕获到大量的MyDoom蠕虫样本和传播该蠕虫的钓鱼邮件。受害主机感染MyDoom后会被放置后门&#xff0c;以便攻击者下发后续恶意软件&#xff0c;进行攻击或窃密等操作。MyDoom蠕虫最早发现于2004年&…

语音质量评价方法之MOS

引言 在语音增强、语音合成、语音转换、声音转换、语音克隆、语音修复等等领域&#xff0c;常常要对输出的语音进行评价。对语音的质量评价一般关注两个方面&#xff0c;即主观评价和客观评价。主观评价就是人凭借听觉感受对语音进行打分&#xff0c;客观评价比较广泛&#xf…

吉林省教育学院学报杂志社吉林省教育学院学报编辑部2024年第5期目录

“研培一体”理论与实践 教师培训管理共同体的职能定位与价值追求 张岩; 1-3 数字化转型背景下教师培训工作的发展路径 李春光; 4-6 挖掘数智潜能&#xff0c;推进教师培训融合创新 鲍赫; 7-9《吉林省教育学院学报》投稿&#xff1a;cn7kantougao163.com 精准培…

网络安全等级保护制度详解,一文掌握核心要点!

一、等级保护制度发展情况 等级保护制度的法律依据 《计算机信息系统安全保护条例》&#xff08;1994年General Office of the State Council第147号令&#xff09; 公安部主管全国计算机信息系统安全保护工作。 计算机信息系统实行安全等级保护&#xff0c;安全等级的划分…

夏季河湖防溺水新举措:EasyCVR+AI视频智能监控系统保障水域安全

近日一则新闻引起大众关注&#xff0c;有网友发布视频称&#xff0c;假期在逛西湖时&#xff0c;发现水面上“平躺”漂浮着一名游客在等待救援。在事发3分钟内&#xff0c;沿湖救生员成功将落水游客救到了岸边。 随着夏季的到来&#xff0c;雨水增多&#xff0c;各危险水域水位…

ensp模拟器USG6000V1配置DCHP功能

接着上一篇配置&#xff0c;继续本篇的内容。开启DHCP功能非常简单&#xff0c;只需几个命令即可。实验拓扑图也非常简单&#xff0c;如下&#xff1a; 开启防火墙DHCP功能&#xff1a; [USG6000V1]dhcp enable 选择DHCP接口并设置接口IP地址&#xff0c;这里给g1/0/0配置2网…

Chromium 开发指南2024 Mac篇-编译前的准备工作(一)

1.引言 Chromium 是一款开源的网页浏览器项目&#xff0c;作为 Google Chrome 浏览器的基础&#xff0c;其卓越的性能和广泛的应用使其成为众多开发者研究和学习的对象。对于希望深入了解浏览器内核&#xff0c;或是计划在 Chromium 基础上开发自定义浏览器的开发者来说&#…

【leetcode刷题】面试经典150题 88.合并两个有序数组

leetcode刷题 面试经典150 88. 合并两个有序数组 难度&#xff1a;简单 文章目录 一、题目内容二、自己实现代码2.1 实现思路2.2 实现代码2.3 结果分析 三、 官方解法3.1 直接合并后排序3.1.1 算法实现3.1.2 代码实现3.1.3 代码分析 3.2 双指针3.2.1 算法实现3.2.2 代码实现3.2…

Android ViewModel实现和原理

ViewModel实现和原理 前言1. 使用1.1 gradle准备1.2 模拟场景1.3. LiveData和ViewModel1.4 更新数据 2. 原理与源码解读2.1 添加观察者2.2 setValue2.3 post 参考资料 前言 ViewModel的主要基于观察者的设计模式&#xff0c;他主要分为两个部分&#xff1a; 提供者Provider&a…

Codesys 获取系统年、月、日、时、分、秒、星期几 +解决时区问题+ ST语言编程实现代码

一、 效果如图所示 二、功能说明 发现获取的时间比北京时间多一个时区&#xff08;8个小时&#xff09;&#xff0c;解决时区问题获取时间后&#xff0c;单独把年月日时分秒提取出来&#xff0c;单独保存在变量中获取星期几&#xff0c;保存在变量中 三、Codesys用ST语言实现…