C++与QML混合编程

news2024/12/22 20:55:50

一、前言

简单来说,混合编程就是通过Qml高效便捷的构建UI界面,而使用C ++来实现业务逻辑和复杂算法。Qt集成了QML引擎Qt元对象系统,使得QML很容易从C ++中得到扩展,在一定的条件下,QML就可以访问QObject派生类的成员,例如信号、槽函数、枚举类型、属性、成员函数等。

C++与QML交互时,一些基本类型C++与QML之间能自动转化,两者之间可以互通。如下:

除以上的数据类型外,C++传递给QML的值是无法被QML识别的,也就是自定义的数据类型是无法通过编译系统自动转化的,比如 Enum、QList、QMap、struct 结构体等复杂数据。因此只能采用发送信号携带参数或者使用 Q_INVOKABLE 宏在qml中获得返回值,将该自定义数据类型以QObject子类对象的形式传递给QML

二、基础数据类型实现方式

Qt中提供了两种在 QML 环境中使用C ++对象的方式

方式一: 在C ++中实现一个类,注册到Qml环境中,Qml环境中使用该类型创建对象

方式二: 在C ++中构造一个对象,将这个对象设置为Qml的上下文属性,在Qml环境中直接使用该属性

两种方式之间的区别是第一种可以使C ++类在QML中作为一个数据类型,例如函数参数类型或属性类型,也可以使用其枚举类型、单例等,功能更强大。下面主要说明下方式一的实现步骤

第一步: 创建继承自QObject的C++类,对象必须继承自QObject才能在QML被使用和访问

第二步:在类定义中使用Q_PROPERTY导出成员的READ、WRITE、NOTIFY接口,这样类中的成员变量就可以在QML调用和修改了,同时变量被修改后也会发送信号通知QML端。用 Q_INVOKABLE 修饰成员函数,这样类中的成员函数就可以直接被QML调用。前提是该模块已经被注册过!!!

class MyObject : public QObject
{
    Q_OBJECT //必须有

public:
    explicit MyObject(QObject *parent = nullptr);
    ~MyObject();
    static MyObject * getInstance();

    //读取函数,对应READ
    int getIValue() {return iValue};
    QString getSStr() {return sStr};

    //被 Q_INVOKABLE 修饰C++函数能直接被QML调用
    Q_INVOKABLE void setCapture(bool state);

    //写函数,对应 WRITE,可以没有
    Q_INVOKABLE void setIValue(int value) {iValue = value};
    Q_INVOKABLE void setSStr(const QString &str) {sStr = value};

    //定义公有的槽函数
public slots:
    Q_INVOKABLE void cppSlot(int i, QString s);

private slots:
    void timer_timeout();

signals:
    //修改通知,对应 NOTIFY,可以没有。可以分开写,也可以用同一个信号
    void iValueChanged(int value);
    void sStrChanged(const QString &str);
    void myObjDataChanged();
    void cppSig(QVariant i, QVariant s);

private:
    QTimer *timer;
    int iValue;
    QString sStr;

    // property declarations required for QML
    Q_PROPERTY(int iValue READ getIValue WRITE setIValue NOTIFY myObjDataChanged)
    Q_PROPERTY(QString sStr READ getSStr WRITE setSStr NOTIFY myObjDataChanged)
};

第三步:在main.cpp中注册模块

//使用qmlRegisterType注册模块,在qml中通过 import 模块名称 进行引用
//模块名称、主版本号、次版本号、类名称
qmlRegisterType<MyObject>("MyObj11", 1, 0, "MyObject");

方式二实现方式

QQmlApplicationEngine engine;
//使用setContextProperty设置全局对象/上下文对象。作用域为全局
//常用于一些不变的常量
QQmlContext *context = engine.rootContext();
context->setContextProperty("SCREEN_WIDTH", 800);

第四步:QML端调用

import QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
import MyObj11 1.0 //根据模块名称导入模块

//创建MyObject对象
MyObject{
    objectName: "myobj"
    //可直接操作MyObject类中的数据了
    id: myobj
    iValue: 10
    sStr: "www"
    Component.onCompleted:{
        console.log(iValue, sStr)
    }
}
//监控myobj.iValue的改变
onValueChanged: {
    console.log("onValueChanged: ", value)
}

//1、直接访问C++的成员变量和成员函数....................................
Button{
    id: btn1
    objectName: "button1"
    x:20; y:20
    anchors.margins: 10
    text: "访问C++成员和方法"
    onClicked: {
        myobj.iValue += 2 //修改myobj.iValue的值
        onoff = onoff ? 0 : 1
        myobj.setCapture(onoff) //调用C++端函数
    }
}

三、自定义数据类型实现方式

1、在QML中使用C++端的枚举类型

C++的枚举类型如果要在QML中使用,需要在使用 Q_ENUMS 去修饰这个枚举类型

.h头文件

#ifndef TIMINGSAMPPARA_H
#define TIMINGSAMPPARA_H

#include <QObject>

class TimingSampPara : public QObject
{
    Q_OBJECT
public:
    explicit TimingSampPara(QObject *parent = nullptr);
    ~TimingSampPara();

    //枚举
    enum E_TIMINGSAMP_PARA
    {
        E_TIMINGSAMP_PARA_NO = 0,
        E_TIMINGSAMP_PARA_ENABLE,
        E_TIMINGSAMP_PARA_TIME,
        E_TIMINGSAMP_PARA_ALL
    };
    Q_ENUMS(E_TIMINGSAMP_PARA)

    int getNo() const;
    int getEnable() const;
    QString getTime() const;

    void setNo(int value);
    void setEnable(int value);
    void setTime(QString value);

    Q_PROPERTY(int no READ getNo WRITE setNo)
    Q_PROPERTY(int enable READ getEnable WRITE setEnable)
    Q_PROPERTY(QString time READ getTime WRITE setTime)

private:
    int no;
    int enable;    //使能
    QString time;  //时间(月-日 时:分)
};

#endif // TIMINGSAMPPARA_H

main.cpp中注册自定义类型

qmlRegisterType<TimingSampPara>("MyTimingSampPara",1,0, "TimingSampPara");

.qml 端使用

import MyTimingSampPara 1.0

arrTimingSampPara[TimingSampPara.E_TIMINGSAMP_PARA_NO] = 6
arrTimingSampPara[TimingSampPara.E_TIMINGSAMP_PARA_ENABLE] = 201
arrTimingSampPara[TimingSampPara.E_TIMINGSAMP_PARA_TIME] =  "00-00 05:02"

2、在QML中读写C++端的自定义结构体数据

结构体在QML中是无法被识别的,必须将结构体类型转换为QVariantMap或QVariant,或者将结构体封装到QObject子类中,在QObject子类中用Q_PROPERTY定义其成员。转成QVariantMap或QVariant的方式 也是需要先定义成QObject子类,而且QML端无法修改,所以最为简单的还是将结构体封装到QObject子类中

比如想要在QML中读写如下的自定义结构体数据

//定时采样
typedef struct
{
    int no;
    int enable;     //使能
    char time[16];  //时间(月-日 时:分)
}TIMING_SAMP_PARA_T;

//采样模式参数配置
typedef struct
{
    E_SAMP_MODE_T sampMode;       //采样模式 0:定时采样  1:时间等比 2:外控采样 3:流量等比 4:流量跟踪 5:液位触发
    int pipeSave;       //采样管存(S)
    int cycleTime;      //采样循环时间(S)

    TIMING_SAMP_PARA_T *timingSamp; //24个定时采样时刻,动态申请空间


    int timingSampCnt;

}SAMP_MODE_CFG_T;

2.1、QML获取C++类的自定义结构体数据

由于在该自定义结构体中还有嵌套的 TIMING_SAMP_PARA_T 结构体数据,且叫二级成员,没有嵌套的叫一级成员;对于一级成员,直接在QObject子类中使用Q_PROPERTY宏定义一个QML能访问的属性,并绑定其READ、WRITE、NOTIFY接口,就可以在QML端进行读取和修改了;对于二级成员,需要使用 Q_INVOKABLE 宏定义一个函数,函数的返回值为二级成员对应的QObject子类

1、定义二级成员---定时采样子类(timingsamppara.h/.cpp)

#ifndef TIMINGSAMPPARA_H
#define TIMINGSAMPPARA_H

#include <QObject>

class TimingSampPara : public QObject
{
    Q_OBJECT
public:
    explicit TimingSampPara(QObject *parent = nullptr);
    ~TimingSampPara();

    //枚举
    enum E_TIMINGSAMP_PARA
    {
        E_TIMINGSAMP_PARA_NO = 0,
        E_TIMINGSAMP_PARA_ENABLE,
        E_TIMINGSAMP_PARA_TIME,
        E_TIMINGSAMP_PARA_ALL
    };
    Q_ENUMS(E_TIMINGSAMP_PARA)

    int getNo() const;
    int getEnable() const;
    QString getTime() const;

    void setNo(int value);
    void setEnable(int value);
    void setTime(QString value);

    Q_PROPERTY(int no READ getNo WRITE setNo)
    Q_PROPERTY(int enable READ getEnable WRITE setEnable)
    Q_PROPERTY(QString time READ getTime WRITE setTime)

private:
    int no;
    int enable;    //使能
    QString time;  //时间(月-日 时:分)
};

#endif // TIMINGSAMPPARA_H

timingsamppara.cpp

#include "timingsamppara.h"
#include "global.h"

TimingSampPara::TimingSampPara(QObject *parent) : QObject(parent)
{
    logPrintf(LOG_APP_TRACE, "%s success\n", Q_FUNC_INFO);
}

TimingSampPara::~TimingSampPara()
{
    logPrintf(LOG_APP_TRACE, "%s success\n", Q_FUNC_INFO);
}

int TimingSampPara::getNo() const
{
    return no;
}

int TimingSampPara::getEnable() const
{
    return enable;
}

QString TimingSampPara::getTime() const
{
    return time;
}

void TimingSampPara::setNo(int value)
{
    no = value;
}

void TimingSampPara::setEnable(int value)
{
    enable = value;
}

void TimingSampPara::setTime(QString value)
{
    time = value;
}

2、定义一级成员---采样模式参数子类(modesetpara.h/.cpp)

#ifndef MODESETPARA_H
#define MODESETPARA_H

#include <QQuickItem>
#include "timingsamppara.h"
#include "global.h"

class ModeSetPara : public QQuickItem
{
    Q_OBJECT

public:
    explicit ModeSetPara(QQuickItem *parent = 0);
    ~ModeSetPara() override;

public:
    int getSampMode();
    int getPipeSave();
    int getCycleTime();

    void setSampMode(int value);
    void setPipeSave(int value);
    void setCycleTime(int value);

    Q_PROPERTY(int sampMode READ getSampMode WRITE setSampMode)
    Q_PROPERTY(int pipeSave READ getPipeSave WRITE setPipeSave)
    Q_PROPERTY(int cycleTime READ getCycleTime WRITE setCycleTime)

    Q_INVOKABLE void savePara(void);
    Q_INVOKABLE TimingSampPara *getTimingSampPara(int no);
    Q_INVOKABLE bool saveTimingSampPara(int no, const QVariantList &listpara);

private:
    SAMP_MODE_CFG_T sampModeCfg;
};

#endif // MODESETPARA_H

modesetpara.cpp

#include "modesetpara.h"
#include "global.h"
#include "xmlOp.h"

ModeSetPara::ModeSetPara(QQuickItem *parent):
    QQuickItem(parent)
{
    //从xml配置文件解析
    parseSampModeCfg(MODE_CONFIG_PATH, &sampModeCfg);
    logPrintf(LOG_APP_TRACE, "%s success\n", Q_FUNC_INFO);
}

ModeSetPara::~ModeSetPara()
{
    free(sampModeCfg.timingSamp);
    free(sampModeCfg.liquidLvlTrig);
    logPrintf(LOG_APP_TRACE, "%s success\n", Q_FUNC_INFO);
}

 // Write data into XML
void ModeSetPara::savePara(void)
{
    
    modifySampModePara(MODE_CONFIG_PATH, &sampModeCfg);
    logPrintf(LOG_APP_TRACE, "%s success\n", Q_FUNC_INFO);
}

int ModeSetPara::getSampMode()
{
    return sampModeCfg.sampMode;
}

int ModeSetPara::getPipeSave()
{
    return sampModeCfg.pipeSave;
}

int ModeSetPara::getCycleTime()
{
    return sampModeCfg.cycleTime;
}

void ModeSetPara::setSampMode(int value)
{
    sampModeCfg.sampMode = (E_SAMP_MODE_T)value;
}

void ModeSetPara::setPipeSave(int value)
{
    sampModeCfg.pipeSave = value;
}

void ModeSetPara::setCycleTime(int value)
{
    sampModeCfg.cycleTime = value;
}

 //获取二级结构体对应的QObject子类
TimingSampPara *ModeSetPara::getTimingSampPara(int no)
{
    // Assign value from structure to object
    TimingSampPara *pTimingSampPara = new TimingSampPara();
    pTimingSampPara->setNo(sampModeCfg.timingSamp[no].no);
    pTimingSampPara->setEnable(sampModeCfg.timingSamp[no].enable);
    pTimingSampPara->setTime(sampModeCfg.timingSamp[no].time);

    logPrintf(LOG_APP_TRACE, "%s success\n", Q_FUNC_INFO);
    return pTimingSampPara;
}

bool ModeSetPara::saveTimingSampPara(int no, const QVariantList &listpara)
{
    if(TimingSampPara::E_TIMINGSAMP_PARA_ALL != listpara.size())
    {
        logPrintf(LOG_APP_TRACE, "%s failed! listpara.size=%d(less than %d)\n", Q_FUNC_INFO,
                  listpara.size(), TimingSampPara::E_TIMINGSAMP_PARA_ALL);
        return false;
    }

    // Assign value from variant to structure
    sampModeCfg.timingSamp[no].no = listpara[TimingSampPara::E_TIMINGSAMP_PARA_NO].toInt();
    sampModeCfg.timingSamp[no].enable = listpara[TimingSampPara::E_TIMINGSAMP_PARA_ENABLE].toInt();
    strncpy(sampModeCfg.timingSamp[no].time, listpara[TimingSampPara::E_TIMINGSAMP_PARA_TIME].toString().toStdString().c_str(),
            sizeof(sampModeCfg.timingSamp[no].time));

    // Write data into XML
    modifySampModePara(MODE_CONFIG_PATH, &sampModeCfg);

    logPrintf(LOG_APP_TRACE, "%s success\n", Q_FUNC_INFO);

    return true;
}

3、在main.cpp中注册自定义对象

使用 qmlRegisterType,将自定义的QObject派生类注册到QML

qmlRegisterType<TimingSampPara>("MyTimingSampPara",1,0, "TimingSampPara");
qmlRegisterType<ModeSetPara>("MyModeSetPara",1,0, "ModeSetPara");

4、在QML中读取

import MyTimingSampPara 1.0
import MyModeSetPara 1.0

Item {

    width: 800
    height: 480
    visible: true

    ModeSetPara {
        id: modeCfgPara
    }

    Component.onCompleted: {
        //获取一级成员
        console.log("sampMode=" + modeCfgPara.sampMode)
        console.log("pipeSave=" + modeCfgPara.pipeSave)
        console.log("cycleTime=" + modeCfgPara.cycleTime)
        //设置一级成员
        modeCfgPara.sampMode = 0
        modeCfgPara.pipeSave = 21
        modeCfgPara.cycleTime = 31

        //获取二级成员
        var pTimingSampPara = modeCfgPara.getTimingSampPara(5)
        console.log("timingSamp[5]:")
        console.log("no=" + pTimingSampPara.no)
        console.log("enable=" + pTimingSampPara.enable)
        console.log("time=" + pTimingSampPara.time)
        //设置二级成员,收集界面数据后设置
        var arrTimingSampPara = []
        arrTimingSampPara[TimingSampPara.E_TIMINGSAMP_PARA_NO] = 6
        arrTimingSampPara[TimingSampPara.E_TIMINGSAMP_PARA_ENABLE] = 201
        arrTimingSampPara[TimingSampPara.E_TIMINGSAMP_PARA_TIME] =  "00-00 05:02"
        modeCfgPara.saveTimingSampPara(5, arrTimingSampPara)
    }
}

2.2、C++类接收QML传递的自定义数据

1、在QML中修改

对于一级成员直接修改属性值就可以了;对于二级成员,界面需要先收集界面的输入数据,使用 QVariantList 数据的形式暂时存储,再将其传递给C++类。使用QVariantList的原因是QVariantList以及QStringList在qml中是可以被识别的,可直接通过索引,比如 mList[i] 就可以访问,但是map就不能直接访问,需要做一些处理,没测试通!!

使用 QVariantList 设置的一个问题就是如何将索引与结构体变量对应起来,目前的方法是将结构体成员在 list 中的索引定义成枚举

2、C++接收QML传递的QVariantList变量后,将其转换成二级成员结构体

bool ModeSetPara::saveTimingSampPara(int no, const QVariantList &listpara)
{
    if(TimingSampPara::E_TIMINGSAMP_PARA_ALL != listpara.size())
    {
        logPrintf(LOG_APP_TRACE, "%s failed! listpara.size=%d(less than %d)\n", Q_FUNC_INFO,
                  listpara.size(), TimingSampPara::E_TIMINGSAMP_PARA_ALL);
        return false;
    }

    // Assign value from variant to structure
    sampModeCfg.timingSamp[no].no = listpara[TimingSampPara::E_TIMINGSAMP_PARA_NO].toInt();
    sampModeCfg.timingSamp[no].enable = listpara[TimingSampPara::E_TIMINGSAMP_PARA_ENABLE].toInt();
    strncpy(sampModeCfg.timingSamp[no].time, listpara[TimingSampPara::E_TIMINGSAMP_PARA_TIME].toString().toStdString().c_str(),
            sizeof(sampModeCfg.timingSamp[no].time));

    // Write data into XML
    modifySampModePara(MODE_CONFIG_PATH, &sampModeCfg);

    logPrintf(LOG_APP_TRACE, "%s success\n", Q_FUNC_INFO);

    return true;
}

3、在QML中获取C++端的Map类型

要在 C++和 QML 中使用 map 类型,不是直接使用 QMap,而是用 QVariantMap 来替换,QVariantMap 只是 QMap 的一个重定义的别名

typedef QVariantMap
Synonym for QMap<QString, QVariant>.

c++端定义如下:

#include <QObject>
#include <QVariant>
#include <QVariantMap>
#include <QVariantList>

class MyDemo : public QObject
{
    Q_OBJECT
    Q_ENUMS(Gender)
public:
    enum Gender
    {
        Boy,
        Girl
    }
    struct PeopleInfo
    {
        int year;      // 年龄
        int course ,   // 年纪
        Gender gender; // 性别
        QString name   // 名字
    };

public:
    MyDemo (QObject *parent = nullptr);
    ~MyDemo ();

    // 通过QVariantMap获取学生信息
    Q_INVOKABLE QVariantMap getCurrentStudentInfo(void)
    {
        QVariantMap map;
        map.clear();
        map.insert("year", m_studentInfo.year);
        map.insert("course ", m_studentInfo.course );
        map.insert("gender", m_studentInfo.gender);
        map.insert("name", m_studentInfo.name);
        return map;
    }
    // 通过QVariantList获取学生名字
     Q_INVOKABLE QVariantList getStudentNameList(void)
     {
            QVariantList varList;
            for (int i=0; i<m_studentNameList.count(); ++i)
            {
                varList.push_back(m_studentNameList.at(i));
            }
            return varList;
     }
     
private:
    // 学生信息
    PeopleInfo  m_studentInfo;
    QList<struct PeopleInfo> m_studentNameList;
};

qml端获取示例如下:

Item {
    function readValues(anArray, anObject) {
        for (var i=0; i<anArray.length; i++)
            console.log("Array item:", anArray[i])

        for (var prop in anObject) {
            console.log("Object item:", prop, "=", anObject[prop])
        }
    }
}

4、在QML中获取C++端的列表类型

要在 C++和 QML 中使用 list 类型,不是直接使用 QList,而是用 QVariantList 来替换,QVariantList 只是 QList 的一个重定义的别名


typedef QVariantList
Synonym for QList<QVariant>.

四、参考链接

C++与QML混合编程技术(传递自定义数据类型)_北星之茫的博客-CSDN博客_c++和qml混合编程

QML_Qml和C++混合编程_HX科技的博客-CSDN博客_qml c++混合编程

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

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

相关文章

112.(leaflet之家)leaflet椭圆修改

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

OpenHarmony之轻量系统编译构建流程

首先我们先来熟悉几个概念&#xff1a; - 子系统 子系统是一个逻辑概念&#xff0c;它由一个或多个具体的组件组成。OpenHarmony整体遵从分层设计&#xff0c;从下向上依次为&#xff1a;内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 组件”逐级…

别人家的公司年终奖52个月工资-互联网企业年终裁员脸在哪

52个月工资&#xff0c;开玩笑吧&#xff0c;还真不是。据台湾媒体报道&#xff0c;中国台湾长荣海运年终奖出炉&#xff0c;继去年40个月年终奖后&#xff0c;今年再创新高&#xff0c;传言最高发放52个月年终奖金。以基层员工月薪5万新台币计算&#xff0c;表现好的员工可领到…

6.vector、set和map

一、vector 1.简介 有些时候想开一个数组&#xff0c;但是却不知道应该开多大长度的数组合适&#xff0c;因为我们需要用到的数组可能会根据情况变动。这时候我们就需要用到动态数组。 所谓动态数组&#xff0c;也就是不定长数组&#xff0c;数组的长度是可以根据我们的需要…

第四章快速排序——分而治之

分而治之&#xff08;divide and conquer,D&C&#xff09; D&C算法是递归的&#xff0c;并且有2个步骤&#xff1a; 找出基线条件&#xff0c;并且条件尽可能简单不断将问题分解&#xff0c;直到符合基线条件 给定一个数组&#xff0c;求和&#xff1a; 利用循环很容…

秒懂双向链表

目录 一、双向链表概述 二、模拟实现双向链表 1、头插法插入元素 2、尾插法插入元素 3、在指定位置插入元素 4、查询双向链表中是否包含key 5、删除第一次出现关键字为key的结点 6、删除所有值为key的结点 7、求双向链表的长度 8、遍历双向链表 9、清空双向链表 三…

【 uniapp - 黑马优购 | 商品列表 】如何实现数据获取、结构渲染、自定义组件的封装

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大二在校生&#xff0c;讨厌编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;小新爱学习. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc…

在 Mac 上将 PDF 转换为 Word 的 5 种简单方法

当谈到将PDF格式转换为Word格式时&#xff0c;用户可能会从互联网上搜索并尝试在线将PDF转换为Word。如果是这样&#xff0c;您可能会得到不好的结果并冒着文件本身的风险。在线 PDF 到 Word 转换器工具可能会产生低质量的输出&#xff0c;对文件大小有限制&#xff0c;更糟糕的…

maven多模块依赖版本不一致问题

项目结构&#xff1a; ├─springcloud-alibaba ├─.idea├─shop-common├─shop-order├─shop-product└─shop-user项目环境&#xff1a; 父工程&#xff1a; <properties><spring-cloud.version>Greenwich.RELEASE</spring-cloud.version><sprin…

java虚拟机之垃圾回收机制

一.需要回收的内存区域 程序计数器、虚拟机栈、本地方法栈 3 个区域随线程生灭(因为是线程私有)&#xff0c;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。而 Java 堆和方法区则不一样&#xff0c;一个接口中的多个实现类需要的内存可能不一样&#xff0c…

<Focal Loss for Dense Object Detection>论文解读

目录1.简介2.模型2.1 二阶段要比单阶段模型效果好本质原因2.2 模型结构2.3.focal loss2.3.1 公式说明2.3.2 其他2.4 消融实验3.源码详解1.简介 目标识别有两大经典结构: 第一类是以Faster RCNN为代表的二阶段识别方法&#xff0c;这种结构的第一阶段专注于proposal的提取&am…

后端开发规约

目录 项目MVC层级设计规范 工程项目模块设计 设计规约 Java编码规范 参考《阿里巴巴Java开发手册》&#xff0c;见文末参考文档 OOP 面向对象设计 & 面向接口编程 Lombok工具包依赖 Guava、Hutool 等脚手架工具包&#xff08;三方包使用其一即可&#xff09; 日志打…

python初级教程十 Mongodb增、删、改、查

Mongodb 插入文档 MongoDB 中的一个文档类似 SQL 表中的一条记录。 插入集合 集合中插入文档使用 insert_one() 方法&#xff0c;该方法的第一参数是字典 name > value 对。 以下实例向 sites 集合中插入文档&#xff1a; #!/usr/bin/python3import pymongomyclient p…

03、Java并发 Java 线程池 ( Thread Pool ) (上)

本文我们将讲解 Java 中的线程池 ( Thread Pool )&#xff0c;从 Java 标准库中的线程池的不同实现开始&#xff0c;到 Google 开发的 Guava 库的前世今生。 本章节涉及到很多前几个章节中阐述的知识点。我们希望你是按照顺序阅读下来的&#xff0c;不然有些知识会一头雾水。 J…

Redis基础篇——Redis安装以及配置文件的修改

文章目录1. 认识Redis1.1 特征1.2 安装 Redis1. 安装 Redis 依赖2. 上传安装包1.3 默认启动1.4 指定配置启动1.5 开机自启&#xff08;推荐&#xff09;1. 认识Redis Redis 诞生于 2009 年&#xff0c;全称是 Remote Dictionary Server&#xff0c;远程词典服务器&#xff0c;…

Databend 开源周报 #73

Databend 是一款强大的云数仓。专为弹性和高效设计&#xff0c;自由且开源。 即刻体验云服务&#xff1a;https://app.databend.com。 What’s New 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 Features & Improvements Multiple Catalogs …

hudi实战-- 核心点解析

目录 Hudi 基础功能 Hudi 简介 Hudi 功能 Hudi 的特性 Hudi 的 架构 Hudi 数据管理 Hudi 表数据结构 hoodie 文件 数据文件 数据存储概述 Metadata 元数据 Index 索引 索引策略 Data 数据 Hudi 核心点解析 基本概念 时间轴Timeline 文件管理 索引 Index 表的存储…

NiN详解

入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 ✨完整代码在我的github上&#xff0c;有需要的朋友可以康康✨ https://github.com/tt-s-t/Deep-Learning.git 目录 一、NiN网络的…

【C语言开源库】 一个只有500行代码的开源http服务器:Tinyhttpd学习

项目搬运&#xff0c;带中文翻译&#xff1a;https://github.com/nengm/Tinyhttpd在嵌入式中&#xff0c;我们HTTP服务器用得最多的就是boa还有就是goahead,但是这2个代码量比较大&#xff0c;而Tinyhttpd只有几百行&#xff0c;比较有助于我们学习。一、编译及运行直接make之后…

用Python让奇怪的想法变成现实,2023年继续创作

2023年继续写作&#xff0c;用文章记录生活 时间过得真快&#xff0c;一下就到2023年了。 由于疫情肆虐&#xff0c;在网络的游弋的实现也长了&#xff0c;写作的自然也多了。 回想一下&#xff0c;2018-2021年这三年时间里一篇文章也没写过为0&#xff0c;哈哈&#xff0c;没…