QML中常见模型使用

news2024/10/7 20:29:58

目录

  • 引言
  • 基础知识
  • 简单模型
  • 重复模型
    • 常见视图
      • ListView
      • Repeater
    • ListModel
    • QbjectModel
    • 容器模型
      • 数组
      • QStringList
      • QList<XX *>
    • QAbstractItemModel
    • QSortFilterProxyModel
  • 总结

引言

Qt Quick的基础组件中大量使用到模型,如ListView、Repeater等。视图所支持模型类型的也非常多,除了Qt提供的抽象模型QAbstractItemModel 、标准模型QStandardItemModel 之外,还支持队列QList、数组QJsonArray,甚至是数字。相对宽松的自由度也给后期代码维护带来困难,本文旨在对比QML中各种模型优劣(实现复杂度、可读性、可拓展性等),总结相对较好的实现方式。

基础知识

在这里插入图片描述
上图是Qt Widget中模型/视图的框架图,虽然Qt Quick中有些许差异,但基本继承原有框架,可进行对比参考。与MVC架构类似,但是又不相同,MVC包括模型、视图和控制。模型表示数据,视图表示用户界面,控制定义了用户的操作。它能够有效将数据和显示分离,提高了代码的灵活性。Qt的模型视图同样有这样的效果。但是Qt的模型视图将视图和控制放在一起,以便简化框架。另外,为了能够更好地处理用户输入,Qt的模型视图加入了代理(也称作委托)。通过代理能够自定义item的显示和编辑。

简单模型

在这里插入图片描述

模型是数据的集合,当然也可以用来表示单个数据,作为视图关键数据的抽象,但模型更新时视图被动更新,最简单的模型通过QObject的属性即可完成。例如,为实现上图中单张卡片信息展示,需要如下代码:
模型类:

class SimpleModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged FINAL)
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
    Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged FINAL)

public:
    explicit SimpleModel(QObject *parent = nullptr);

public:
    QString url() const;
    void setUrl(const QString &url);

    QString name() const;
    void setName(const QString &name);

    int duration() const;
    void setDuration(int duration);

signals:
    void urlChanged();
    void nameChanged();
    void durationChanged();

private:
    QString url_ = "qrc:/image/default_style.png";
    QString name_ = "VID_003.insv";
    int duration_ = 61;
};

卡片视图:

import QtQuick 2.15
import QtQuick.Layouts 1.15

Rectangle {
    property var itemModel
 
    function secondConvertToTime(second) {
        let mininue = Math.floor(second / 60.0)
        let hour = Math.floor(mininue / 60.0)
        mininue %= 60
        second %= 60
        let dateTime = new Date(0, 0, 0, hour, mininue, second)
        let timeFormat = hour > 0 ? "hh:mm:ss" : "mm:ss"
        return Qt.formatTime(dateTime, timeFormat)
    }

    width: 290
    height: 180
    radius: 10
    color: '#FFD200'

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10
        spacing: 6

        Image {
            Layout.fillWidth: true
            Layout.fillHeight: true
            source: itemModel.url

            Text {
                anchors.right: parent.right
                anchors.bottom: parent.bottom
                anchors.margins: 6
                color: "white"
                font.family: "Microsoft YaHei UI"
                text: secondConvertToTime(itemModel.duration)
            }
        }

        Text {
            color: "white"
            font.family: "Microsoft YaHei UI"
            text: itemModel.name
        }
    }
}

注册上下文:

// 设置上下文属性
QQmlContext *context = engine.rootContext();
context->setContextProperty("SimpleModel", new SimpleModel);
视图组件使用:
import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    FootageCard {
        anchors.centerIn: parent
        itemModel: SimpleModel
    }
}

重复模型

在实际开发中,视图除了上述单张卡片之外,为了提高信息密度通常以列表的形式展示,可以是列表,可以是表格展示,也可以是流布局形式的栅格视图。而这种我们需要讨论的重复模型,则依赖于上述视图,因此需要想简单介绍Qt Quick中所提供的视图。

常见视图

[图片]

ListView与GridView、TableView类似,使用较为简单,Repeater则有一定差别,需要其他组件的配合或者是特殊处理。后面Model的例子中将主要配合ListView进行展示。

ListView

A ListView displays data from models created from built-in QML types like ListModel and XmlListModel, or custom model classes defined in C++ that inherit from QAbstractItemModel or QAbstractListModel.
A ListView has a model, which defines the data to be displayed, and a delegate, which defines how the data should be displayed. Items in a ListView are laid out horizontally or vertically. List views are inherently flickable because ListView inherits from Flickable.

相对可用视图组件,需要设置model和delegate才能正常使用,创建出来的组件能够自动布局,支持行布局、列布局,可通过orientation修改方向。由于父类是Flickable,继承有诸多鼠标操作,如滚动、拖拽等,但在子项中有MouseArea时,易出现冲突,需要通过MouseArea的pressedButtons属性去方式鼠标事件被抢夺。

ListView {
    width: 180
    height: 200

    model: ListModel {
        ListElement {
            name: "Bill Smith"
            number: "555 3264"
        }
        ListElement {
            name: "John Brown"
            number: "555 8426"
        }
        ListElement {
            name: "Sam Wise"
            number: "555 0473"
        }
    }

    delegate: Text {
        text: name + ": " + number
    }
}

Repeater

The Repeater type is used to create a large number of similar items. Like other view types, a Repeater has a model and a delegate: for each entry in the model, the delegate is instantiated in a context seeded with data from the model. A Repeater item is usually enclosed in a positioner type such as Row or Column to visually position the multiple delegate items created by the Repeater.

Repeater和ListView类似,是重复类的创建器,同样是需要设置model和delegate,但是并没有布局,也就是创建的组件x、y值并不会被修改,通常需要搭配布局组件使用,亦或者可以通过模型内的数据控制位置。

Row {
    anchors.fill: parent
    Repeater {
        model: 3
        Rectangle {
            width: 100; height: 40
            border.width: 1
            color: "yellow"
        }
    }
}

ListModel

直接使用Qt Quick中的已经封装好的元素ListModel,代码如下:

ListView {
    anchors.fill: parent
    spacing: 5

    model: ListModel {
        ListElement {
            name: "Bill Smith"
            url: "qrc:/image/default_style.png"
            duration: "61"
        }
        ListElement {
            name: "John Brown"
            url: "qrc:/image/default2_style.png"
            duration: "231"
        }
        ListElement {
            name: "Sam Wise"
            url: "qrc:/image/default3_style.png"
            duration: "15"
        }
    }

    delegate: FootageCard {
        itemModel: model
    }
}

上述代码中需要注意的是delegate内的model节点,和ListView中的model节点并不相同,delegate中表示的是单个item,而ListView中表示的整个数据集合。为了更加直观的表示,可增加点击修改模型信息的功能,如下所示:

    delegate: FootageCard {
        itemModel: model

        MouseArea {
            id: mouseArea
            anchors.fill: parent
        }

        Connections {
            target: mouseArea
            function onClicked() {
                model.name = "Jojo"
            }
        }
    }

[图片]

QbjectModel

比较特殊,可以省去delegate,代码如下:

ObjectModel {
    id: itemModel
    Rectangle { height: 30; width: 80; color: "red" }
    Rectangle { height: 40; width: 80; color: "green" }
    Rectangle { height: 15; width: 80; color: "blue" }
}

ListView {
    anchors.fill: parent
    model: itemModel
}

容器模型

数组

通过简单的数组或者是队列实现,一般使用在简单场景下。需要注意的是使用的时候简单数组,delegate能用到的item属性就不再是model而是modelData,如下所示:

ListView {
    anchors.fill: parent
    spacing: 5

    model: ["Bill Smith", "John Brown", "Sam Wise"]
    delegate: Text {
        text: modelData
    }
}

QStringList

也可以使用QStringList作为模型,如下所示(同样需要注册到上下文):

class CommonModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QStringList stringList READ stringList WRITE setStringList NOTIFY stringListChanged FINAL)

public:
    explicit CommonModel(QObject *parent = nullptr) : QObject{parent} {
        string_list_ << "Bill Smith" << "John Brown" << "Sam Wise";
    };

public:
    QStringList stringList() const;
    void setStringList(const QStringList &string_list);

signals:
    void stringListChanged();

private:
    QStringList string_list_;
};
ListView {
    anchors.fill: parent
    spacing: 5

    model: CommonModel.stringList
    delegate: Text {
        text: modelData
    }
}

QList<XX *>

使用上述代码在实际开发中存在问题,就是每个item只有一个属性,没法达到之前ListModel中的单个item有多个属性,可以采用QList<XX *>的方式(QVector相同),如下所示:

class CommonModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<SimpleModel*> modelList READ modelList WRITE setModelList NOTIFY modelListChanged FINAL)

public:
    explicit CommonModel(QObject *parent = nullptr) : QObject{parent} {
        qRegisterMetaType<SimpleModel *>("SimpleModel *");
        model_list_ << new SimpleModel(this) << new SimpleModel(this) << new SimpleModel(this);
        model_list_[0]->setName("Bill Smith");
        model_list_[1]->setName("John Brown");
        model_list_[2]->setName("Sam Wise");
    };

public:
    QList<SimpleModel *> modelList() const;
    void setModelList(const QList<SimpleModel *> &model_list);

signals:
    void modelListChanged();

private:
    QList<SimpleModel *> model_list_;
};

上述代码使用的SimpleModel与前文相同,在使用时需要注册元对象类型,否则QML中将无法识别。注册完成后的使用方式与前文类似,只是需要将model改为modelData,如下所示:

ListView {
    anchors.fill: parent
    spacing: 5

    model: CommonModel.modelList

    delegate: FootageCard {
        itemModel: modelData
    }
}

QAbstractItemModel

[图片]
[图片]

由于是抽象类重写纯虚函数以及部分虚函数,还需要准备容器用于记录数据,最后为了在QML中使用需要重写roleNames进行映射,代码如下:

class AbstractItemModel : public QAbstractItemModel
{
    Q_OBJECT

public:
    explicit AbstractItemModel(QObject *parent = nullptr);
    enum ItemRole {
        ItemRoleUrl = Qt::UserRole,
        ItemRoleName,
        ItemRoleDuration,
    };

public:
    // 重写纯虚函数:
    QModelIndex index(int row, int column,
                      const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

public:
    // 修改属性
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;

    // QML属性映射
    QHash<int, QByteArray> roleNames() const override;

private:
    QList<SimpleModel*> item_list_;
    QHash<int, QByteArray> role_names_;
};
AbstractItemModel::AbstractItemModel(QObject *parent)
    : QAbstractItemModel(parent)
{
    role_names_.insert(ItemRoleUrl, "url");
    role_names_.insert(ItemRoleName, "name");
    role_names_.insert(ItemRoleDuration, "duration");

    item_list_ << new SimpleModel(this) << new SimpleModel(this) << new SimpleModel(this);
    item_list_[0]->setName("Bill Smith");
    item_list_[1]->setName("John Brown");
    item_list_[2]->setName("Sam Wise");
}

QModelIndex AbstractItemModel::index(int row, int column, const QModelIndex &parent) const
{
    return hasIndex(row, column, parent) ? createIndex(row, column) : QModelIndex();
}

QModelIndex AbstractItemModel::parent(const QModelIndex &index) const
{
    Q_UNUSED(index);
    return QModelIndex();
}

int AbstractItemModel::rowCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : item_list_.count();
}

int AbstractItemModel::columnCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : 1;
}

QVariant AbstractItemModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if(index.row() >= rowCount()){
        return QVariant();
    }

    auto info = item_list_[index.row()];
    switch (role) {
    case ItemRoleUrl:
        return info->url();
    case ItemRoleName:
        return info->name();
    case ItemRoleDuration:
        return info->duration();
    }
    return QVariant();
}

bool AbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid())
        return false;

    if(index.row() >= rowCount()){
        return false;
    }

    auto info = item_list_[index.row()];
    switch (role) {
    case ItemRoleUrl:
        info->setUrl(value.toString());
        break;
    case ItemRoleName:
        info->setName(value.toString());
        break;
    case ItemRoleDuration:
        info->setDuration(value.toInt());
        break;
    }

    emit dataChanged(index, index, QVector<int>{role});
    return true;
}

QHash<int, QByteArray> AbstractItemModel::roleNames() const
{
    return role_names_;
}

QAbstractListModel与QAbstractItemModel类似,只是少了几个纯虚函数的实现,繁琐程度依旧。
看到这里大家应该会有疑惑,既然AbstractItemModel模型类内有一个容器item_list_,那为什么不直接用容器模型就可以了,还耗费时间增加一个模型类,凭空增加后续维护成本。
主要是为了增量更新,原本的容器模型是作为类的一个属性存在的,尽管item的属性变化是通过信号推送的,但item的增加和删除只能通过整个变更信号通知视图更新,也就是将原有的元素整体删除后再重建视图的全量更新。
当然因为是抽象类,还是需要实现部分代码才能有增加、删除功能,代码如下:

bool AbstractItemModel::insertRows(int row, int count, const QModelIndex &parent)
{
    if(!count){
        return false;
    }

    if(row >= rowCount()){
        row = rowCount();
    }
    else {
        if(!hasIndex(row, 0, parent)) {
            return false;
        }
    }

    beginInsertRows(QModelIndex(), row, row + count - 1);
    for(int i = 0; i < count; i++) {
        item_list_.insert(row, new SimpleModel(this));
    }
    endInsertRows();
    return true;
}

bool AbstractItemModel::removeRows(int row, int count, const QModelIndex &parent)
{
    if(!count){
        return false;
    }

    if(!hasIndex(row, 0, parent)){
        return false;
    }

    beginRemoveRows(QModelIndex(), row, row + count - 1);
    for(int i = 0; i < count; i++) {
        item_list_.at(i)->deleteLater();
        item_list_.removeAt(i);
    }
    endRemoveRows();

    return true;
}

里面比较关键的就是beginInsertRows、endInsertRows和beginRemoveRows、endRemoveRows,也可以不重写insertRows、removeRows,只要保证前面的函数成组出现即可,里面会发送信号给视图进行处理。
QStandardItemModel
上述使用抽象模型的方式非常繁琐,过高的使用难度必然导致该方法的使用频率不高,甚至是在后期的维护中逐渐消亡。那有没有一种既使用简单,又能保证增量更新的方法。
可以直接使用QStandardItemModel,只需要将数据转换为标准元素QStandardItem。因为需要在QML中使用,再重写roleNames()即可。代码如下:

class StandardItemModel : public QStandardItemModel
{
    Q_OBJECT

public:
    explicit StandardItemModel(QObject *parent = nullptr);
    enum ItemRole {
        ItemRoleUrl = Qt::UserRole,
        ItemRoleName,
        ItemRoleDuration,
    };

public:
    // QML属性映射
    QHash<int, QByteArray> roleNames() const override;

public:
    Q_INVOKABLE void append();
    Q_INVOKABLE void remove();

private:
    QHash<int, QByteArray> role_names_;
};
StandardItemModel::StandardItemModel(QObject *parent)
    : QStandardItemModel(parent)
{
    role_names_ = QStandardItemModel::roleNames();
    role_names_.insert(ItemRoleUrl, "url");
    role_names_.insert(ItemRoleName, "name");
    role_names_.insert(ItemRoleDuration, "duration");

    // 初始化
    auto item1 = new QStandardItem;
    item1->setData("qrc:/image/default_style.png", ItemRoleUrl);
    item1->setData("Bill Smith", ItemRoleName);
    item1->setData(61, ItemRoleDuration);

    auto item2 = new QStandardItem;
    item2->setData("qrc:/image/default2_style.png", ItemRoleUrl);
    item2->setData("John Brown", ItemRoleName);
    item2->setData(233, ItemRoleDuration);

    auto item3 = new QStandardItem;
    item3->setData("qrc:/image/default3_style.png", ItemRoleUrl);
    item3->setData("Sam Wise", ItemRoleName);
    item3->setData(15, ItemRoleDuration);

    appendRow(item1);
    appendRow(item2);
    appendRow(item3);
}

QHash<int, QByteArray> StandardItemModel::roleNames() const
{
    return role_names_;
}

void StandardItemModel::append()
{
    auto item = new QStandardItem;
    item->setData("qrc:/image/default_style.png", ItemRoleUrl);
    item->setData("Bill Smith", ItemRoleName);
    item->setData(61, ItemRoleDuration);
    appendRow(item);
}

void StandardItemModel::remove()
{
    removeRow(rowCount() - 1);
}

QSortFilterProxyModel

代理模型在使用时需要源模型,也就是原始数据模型,针对原始模型进行代理,而QSortFilterProxyModel顾名思义是针对源模型的排序和过滤的代理。
在实际开发中,排序和过滤是对模型的高频操作,不同界面的模型内容可能只是因为排序方式、过滤内容的不同而被迫分为两个模型,例如界面A需要显示升序,而界面B需要显示降序,这里如果不对视图进行特殊操作就只能存在两种模型。
使用两种只有细微差别的模型显然不是明智的决定,这里则可以使用QSortFilterProxyModel完成过滤及排序,代理只是调整了两个数据集索引的映射关系,不是原Model的拷贝,尽管存在一部分维护映射关系的开销,但相比于维护两份相同的数据开销要小得多。同时将过滤和排序拆分出来,则属于非常合适的解耦,在数据格式类似的源模型之间还可以复用代理模型。
下面进行代理模型的实际应用展示,Demo整体分为三个ListView,第一列使用的是源模型,第二列是只有收藏的数据(卡片右侧的实心黄色圆形代表收藏,空心黄色圆圈代表未收藏),第三列使用的名称降序排序数据。
在这里插入图片描述

完整代码见QML常见模型使用源码。

总结

综上所述,对于简单模型可以采用QObject的属性系统,通过属性完成视图的被动刷新。对于重复模型建议使用QStandardItemModel,能够实现增量更新,后续也可以进行代理模型的拓展。元素数量极少且后续没有拓展需求才考虑使用容器模型。

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

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

相关文章

YOLOv5算法改进(15)— 如何去更换Neck网络(包括代码+添加步骤+网络结构图)

前言:Hello大家好,我是小哥谈。在学习完了如何去更换主干网络之后,接着就让我们通过案例的方式去学习下如何去更换Neck网络。本篇文章的特色就是比较浅显易懂,附加了很多的网络结构图,通过结构图的形式向大家娓娓道来,希望大家学习之后能够有所收获!🌈 前期回顾: YO…

linux-(from_timer)-定时器的升级

查看linux版本&#xff1a;cat proc/version 使用旧主板型号&#xff08;SSD202D&#xff09;4.9.84 使用新主板型号&#xff08;RV1126&#xff09;4.19.111 移植yaffs驱动时发现内核对定时器进行了升级&#xff0c;很扯淡啊&#xff01; 多亲切多易懂啊&#xff01; 你看这…

2023年中国纸箱机械优点、市场规模及发展前景分析[图]

纸箱机械行业是指涉及纸箱生产和加工的机械设备制造、销售和相关服务的产业。这个行业的主要任务是设计、制造和提供用于生产各种类型和规格纸箱的机械设备&#xff0c;以满足包装行业对纸箱的不同需求。 纸箱机械行业优点 资料来源&#xff1a;共研产业咨询&#xff08;共研网…

Java-io(输入/输出)

文章目录 绪论输入流输出流File类文件输入 / 输出流字节输入输出流字符输入输出流 End 绪论 首先如果我们在Java编写的一个程序中获得了想要的某种结果&#xff0c;那么在程序结束后&#xff0c;这个结果还存在嘛&#xff1f;不存在的&#xff0c;因为在Java程序运行时&#x…

SSM - Springboot - MyBatis-Plus 全栈体系(三十四)

第八章 项目实战 四、后台功能开发 1. 用户模块开发 1.1 jwt 和 token 介绍 1.1.1 token 介绍 令牌&#xff08;Token&#xff09;&#xff1a;在计算机领域&#xff0c;令牌是一种代表某种访问权限或身份认证信息的令牌。它可以是一串随机生成的字符或数字&#xff0c;用…

git创建与合并分支

文章目录 创建与合并分支分支管理的概念实际操作 解决冲突分支管理策略Bug分支Feature分支多人协作 创建与合并分支 分支管理的概念 分支在实际中有什么用呢&#xff1f;假设你准备开发一个新功能&#xff0c;但是需要两周才能完成&#xff0c;第一周你写了50%的代码&#xf…

jdk21的外部函数和内存API(官方翻译)

1、jdk21&#xff1a; 引入一个 API&#xff0c;通过该 API&#xff0c;Java 程序可以与 Java 运行时之外的代码和数据进行互操作。通过有效地调用外部函数&#xff08;即JVM外部的代码&#xff09;和安全地访问外部内存&#xff08;即不由JVM管理的内存&#xff09;&#xf…

一种基于HTTPS实现的Web账号登录Linux桌面系统的实现方案

问题由来 客户需求计划列入支持第三方帐号系统&#xff0c;包括Web账号。需求来源是用户想要用它们的帐号直接登录Linux Deepin操作系统。一个失败的实现方案是用户以较小的成本改造帐号管理系统发布HTTP服务&#xff0c;我们开发一个PAM模块与Web服务器交互&#xff0c;数据格…

基于天鹰优化的BP神经网络(分类应用) - 附代码

基于天鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于天鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.天鹰优化BP神经网络3.1 BP神经网络参数设置3.2 天鹰算法应用 4.测试结果&#xff1a;5.M…

2023年【广东省安全员A证第四批(主要负责人)】考试试卷及广东省安全员A证第四批(主要负责人)模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 广东省安全员A证第四批&#xff08;主要负责人&#xff09;考试试卷根据新广东省安全员A证第四批&#xff08;主要负责人&#xff09;考试大纲要求&#xff0c;安全生产模拟考试一点通将广东省安全员A证第四批&#x…

小程序之后台数据动态交互及WXS的使用 (5)

⭐⭐ 小程序专栏&#xff1a;小程序开发专栏 ⭐⭐ 个人主页&#xff1a;个人主页 目录 一.前言 二.后台数据交互 2.1 准备工作 2.1 前台首页数据连接&#xff1a; 三.WXS的使用 今天就分享到这啦&#xff01;&#xff01;&#xff01; 一.前言 本文章续前面的文章的前端界面…

【JUC系列-15】深入理解CompletableFuture的基本使用

JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…

2023 10月8日 至 10 月16日学习总结

1.做的题目 [RootersCTF2019]I_&#xff1c;3_Flask_双层小牛堡的博客-CSDN博客 [NCTF2019]SQLi regexp 盲注-CSDN博客 [网鼎杯 2018]Comment git泄露 / 恢复 二次注入 .DS_Store bash_history文件查看-CSDN博客 PHP LFI 利用临时文件Getshell_双层小牛堡的博客-CSDN博客 …

《动手学深度学习 Pytorch版》 9.7 序列到序列学习(seq2seq)

循环神经网络编码器使用长度可变的序列作为输入&#xff0c;将其编码到循环神经网络编码器固定形状的隐状态中。 为了连续生成输出序列的词元&#xff0c;独立的循环神经网络解码器是基于输入序列的编码信息和输出序列已经看见的或者生成的词元来预测下一个词元。 要点&#x…

关于使用 vxe-table 时设置了 show-overflow tooltip 不展示的问题(Dialog 组件和 table 同时使用)

众所周知&#xff0c;vxe-table 是可以支撑万级数据渲染的表格组件&#xff0c;本质上还是用了虚拟滚动的实现。之前一直知道vxe-table, 但是基本没有机会用的上这个组件&#xff0c;最近在开发埋点数据的统计&#xff0c;后端一次性返回了上千条数据&#xff0c;elementui 的 …

【Shell】环境变量 自定义变量 特殊变量

Shell变量&#xff1a;环境变量 目标 1、理解什么是系统环境变量&#xff1f; 2、掌握常用的系统环境变量都有哪些&#xff1f; Shell变量的介绍 变量用于存储管理临时的数据, 这些数据都是在运行内存中的. 变量类型 系统环境变量 自定义变量 特殊符号变量 系统环境变…

golang查看CPU使用率与内存及源码中的//go:指令

golang查看CPU使用率与内存 1 psutil 1.1 概念与应用场景 psutil是业内一个用于监控OS资源的软件包&#xff0c;目前已经多种语言&#xff0c;包括但不限于Python、Go。 gopsutil是 Python 工具库psutil 的 Golang 移植版&#xff0c;可以帮助我们方便地获取各种系统和硬件信…

基于Java的实验室设备管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

如何在电脑上设置新的蓝牙耳机

本文介绍如何将蓝牙耳机连接到Windows或Mac电脑。 如何在Windows上设置新的蓝牙耳机 蓝牙耳机的设置过程因平台而异&#xff0c;但以下是Windows 11的步骤&#xff1a; 1、选择“开始”&#xff0c;然后在搜索框中输入蓝牙&#xff0c;以显示蓝牙和其他设备。 2、选择添加设…

【LeetCode:2316. 统计无向图中无法互相到达点对数 | BFS + 乘法原理】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…