qt笔记之qml和C++的交互系列(二):rootObject

news2024/10/5 19:10:24

qt笔记之qml和C++的交互系列(二):rootObject

code review!

—— 2024-06-17 杭州 夜

文章目录

  • qt笔记之qml和C++的交互系列(二):rootObject
    • 一.使用rootObject例程
      • 1.运行
      • 2.main.cpp
      • 3.main.qml
      • 3.用于Debug的加长版main.cpp
    • 二.QML文件的根对象详解
      • 基本概念
      • 常见根对象类型
        • Rectangle
        • ApplicationWindow
      • 属性和方法
      • 作用域和继承
    • 三.使用`rootObject`通过`QObject`的`setProperty`方法,可以设置`QML对象的属性`。`rootObjects`和 `QMetaObject::invokeMethod`的结合使用调用`QML中的方法`
      • 3.1.运行
      • 3.2.main.cpp
      • 3.3.main.qml
    • 四.理解`rootObjects`和`QMetaObject::invokeMethod`的结合使用
      • 理解 `rootObjects` 和 `QMetaObject::invokeMethod` 的结合使用
      • 详细示例
        • 1. 加载 QML 文件并获取根对象
        • 2. 动态调用 QML 方法
      • 为什么 `QMetaObject::invokeMethod` 不能单独使用?
      • 总结
    • 五.`rootObjects`是一个`QObject列表`,包含了通过该引擎加载的QML文档的根对象
      • 作用
      • 示例代码
    • 六.为什么findChild<QObject*>("myRectangle") 不起作用:`id` vs `objectName`
      • `id` vs. `objectName`
      • 为什么 `findChild<QObject*>("myRectangle")` 不起作用
      • 为什么 `objectName` 有效
      • 如何仅使用 `findChild<QObject*>("myRectangle")`
        • 示例
          • QML 文件:`main.qml`
          • C++ 文件:`main.cpp`
      • 总结

一.使用rootObject例程

1.运行

在这里插入图片描述

2.main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QDebug>

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty()) {
        qDebug() << "No root objects found.";
        return -1;
    }

    QObject *rootObject = engine.rootObjects().first();
    QObject *rectangle = rootObject->findChild<QObject*>("myRectangle", Qt::FindChildrenRecursively);

    if (rectangle) {
        // 修改属性
        rectangle->setProperty("width", 200);
        // 调用方法
        QVariant newColor = "blue";
        QMetaObject::invokeMethod(rectangle, "changeColor", Q_ARG(QVariant, newColor));
    } else {
        qDebug() << "Rectangle object not found.";
        return -1;
    }

    return app.exec();
}

3.main.qml

import QtQuick 2.0
import QtQuick.Controls 2.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: "Hello World"

    Rectangle {
        id: myRectangle
        objectName: "myRectangle"  // 添加 objectName
        width: 100
        height: 100
        color: "red"

        function changeColor(newColor) {
            console.log("Changing color to: " + newColor)
            color = newColor;
        }
    }
}

3.用于Debug的加长版main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlComponent>
#include <QDebug>

void printObjectTree(QObject *object, int level = 0) {
    if (!object) return;
    QString indent(level * 2, ' ');
    qDebug() << indent << object->objectName() << "(" << object->metaObject()->className() << ")";
    const QObjectList children = object->children();
    for (QObject *child : children) {
        printObjectTree(child, level + 1);
    }
}

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty()) {
        qDebug() << "No root objects found.";
        return -1;
    }

    QObject *rootObject = engine.rootObjects().first();
    if (!rootObject) {
        qDebug() << "Root object not found.";
        return -1;
    }

    qDebug() << "Root object found. Printing object tree:";
    printObjectTree(rootObject);

    QObject *rectangle = rootObject->findChild<QObject*>("myRectangle", Qt::FindChildrenRecursively);
    if (!rectangle) {
        qDebug() << "Rectangle object not found.";
        return -1;
    } else {
        qDebug() << "Rectangle object found.";
    }

    // 修改属性
    if (rectangle->setProperty("width", 200)) {
        qDebug() << "Width changed successfully.";
    } else {
        qDebug() << "Failed to change width.";
    }

    // 调用方法
    QVariant newColor = "blue";
    if (QMetaObject::invokeMethod(rectangle, "changeColor", Q_ARG(QVariant, newColor))) {
        qDebug() << "changeColor method called successfully.";
    } else {
        qDebug() << "Failed to call changeColor method.";
    }

    return app.exec();
}

运行打印

Root object found. Printing object tree:
"" "" ( ApplicationWindow_QMLTYPE_0 )
"  " "" ( QQuickRootItem )
"    " "" ( QQuickOverlay )
"      " "" ( QQmlComponent )
"      " "" ( QQmlComponent )
"    " "ApplicationWindow" ( QQuickContentItem )
"  " "myRectangle" ( QQuickRectangle_QML_3 )
Rectangle object found.
Width changed successfully.
qml: Changing color to: blue
changeColor method called successfully.

二.QML文件的根对象详解

在 QML(Qt Meta Language)中,QML 文件的根对象是整个用户界面组件的基础。根对象定义了该文件中所有子对象的上下文和作用域。通常,根对象是一个 UI 组件,比如 RectangleItemApplicationWindow 等,以下是对 QML 文件根对象的详解:

基本概念

  1. 唯一性

    • 每个 QML 文件必须且只能有一个根对象。这是 QML 文件的入口点,所有其他元素都必须嵌套在这个根对象之内。
  2. 类型选择

    • 根对象可以是任何 QML 类型,但通常会选择合适的 UI 组件,根据需要创建不同类型的根对象。例如,可以使用 Rectangle 创建一个矩形区域,或者使用 ApplicationWindow 创建一个完整的应用窗口。
  3. 作用域

    • 根对象定义了一个新的作用域,文件内的所有子对象都在这个作用域内。这意味着根对象的属性和方法可以被子对象访问和修改。

常见根对象类型

Rectangle
Rectangle {
    width: 200
    height: 200
    color: "lightblue"

    Text {
        text: "Hello, QML!"
        anchors.centerIn: parent
    }
}
  • Rectangle 是一个常见的根对象类型,用于定义一个矩形区域。
ApplicationWindow
import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: "QML Application"

    Button {
        text: "Click Me"
        anchors.centerIn: parent
    }
}
  • ApplicationWindow 用于创建一个完整的应用窗口,通常用于更复杂的应用程序。

属性和方法

根对象可以包含属性和方法,这些属性和方法可以在子对象中访问。例如:

Rectangle {
    id: root
    width: 300
    height: 300
    color: "lightgreen"

    function changeColor(newColor) {
        root.color = newColor
    }

    MouseArea {
        anchors.fill: parent
        onClicked: root.changeColor("lightcoral")
    }
}

作用域和继承

根对象中的属性和方法可以被子对象继承和使用。例如:

Rectangle {
    id: root
    width: 400
    height: 400
    color: "lightyellow"

    property string message: "Hello from root"

    Text {
        text: root.message
        anchors.centerIn: parent
    }
}

三.使用rootObject通过QObjectsetProperty方法,可以设置QML对象的属性rootObjectsQMetaObject::invokeMethod的结合使用调用QML中的方法

3.1.运行

在这里插入图片描述

3.2.main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickItem>
#include <QMetaObject>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    QObject *rootObject = engine.rootObjects().first();
    QObject *rectangle = rootObject->findChild<QObject*>("myRectangle");

    if (rectangle) {
        qDebug() << "Rectangle found!";
        rectangle->setProperty("width", 300);
        rectangle->setProperty("color", "red");
        QMetaObject::invokeMethod(rectangle, "changeColor", Q_ARG(QVariant, "green"));
    } else {
        qDebug() << "Rectangle not found!";
    }

    return app.exec();
}

3.3.main.qml

import QtQuick 2.0
import QtQuick.Controls 2.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    Rectangle {
        id: myRectangle
        objectName: "myRectangle"  // 添加 objectName
        width: 200
        height: 200
        color: "blue"

        function changeColor(newColor) {
            color = newColor;
        }
    }
}

四.理解rootObjectsQMetaObject::invokeMethod的结合使用

rootObjectsQMetaObject::invokeMethod 可以结合使用,特别是在需要从 C++ 调用 QML 中的方法时。让我们详细解释一下这个过程,并澄清为什么 QMetaObject::invokeMethod 不能单独用于调用 QML 中的方法。

理解 rootObjectsQMetaObject::invokeMethod 的结合使用

  1. 获取 QML 根对象:

    • 使用 rootObjects 获取 QML 加载后的根对象。这一步是必要的,因为需要一个具体的 QML 对象实例来调用其方法。
  2. 使用 QMetaObject::invokeMethod 调用 QML 方法:

    • 一旦有了 QML 的根对象(或任何需要调用方法的对象),可以使用 QMetaObject::invokeMethod 动态调用其方法。

详细示例

假设有一个简单的 QML 文件 main.qml,其中定义了一个方法 myQmlMethod

import QtQuick 2.0

Rectangle {
    width: 200
    height: 200

    function myQmlMethod() {
        console.log("myQmlMethod called")
    }
}

在 C++ 中,可以按以下步骤调用这个方法:

1. 加载 QML 文件并获取根对象
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QMetaObject>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    QObject *rootObject = engine.rootObjects().first();

    // Now use QMetaObject::invokeMethod to call the QML method
    QMetaObject::invokeMethod(rootObject, "myQmlMethod");

    return app.exec();
}
2. 动态调用 QML 方法
// Assuming rootObject is already obtained as shown above
QMetaObject::invokeMethod(rootObject, "myQmlMethod");

为什么 QMetaObject::invokeMethod 不能单独使用?

QMetaObject::invokeMethod 需要一个具体的对象实例以及方法名称来调用方法。它不直接与 QML 交互,而是用于调用任何 QObject 派生类的实例方法。因此,必须首先通过 rootObjects 获取 QML 加载后的对象实例,然后才能使用 QMetaObject::invokeMethod 调用该对象的方法。

总结

  • rootObjects: 用于获取 QML 加载后的根对象。
  • QMetaObject::invokeMethod: 用于动态调用 QObject 实例的方法。

这两者结合使用时,首先通过 rootObjects 获取 QML 对象实例,然后使用 QMetaObject::invokeMethod 调用该对象的方法。这样可以在 C++ 代码中灵活地调用 QML 中定义的方法。

五.rootObjects是一个QObject列表,包含了通过该引擎加载的QML文档的根对象

属性/方法描述
rootObjects返回一个包含所有通过该引擎加载的 QML 根对象的列表。
append()添加一个对象到 rootObjects 列表。
at(int index)返回 rootObjects 列表中指定索引处的对象。
clear()清空 rootObjects 列表。
contains(QObject* obj)检查 rootObjects 列表是否包含指定的对象。
indexOf(QObject* obj)返回指定对象在 rootObjects 列表中的索引,如果不包含返回 -1。
removeAt(int index)移除 rootObjects 列表中指定索引处的对象。
size()返回 rootObjects 列表的大小。
first()返回 rootObjects 列表中的第一个对象。
last()返回 rootObjects 列表中的最后一个对象。

作用

  1. 访问根对象: 可以方便地访问通过 QML 加载的主界面对象,通常用于设置属性或调用方法。
  2. 对象管理: 允许在运行时动态地管理 QML 根对象,例如添加、移除或替换根对象。
  3. 调试和测试: 在调试和测试过程中,可以通过 rootObjects 访问和操作 QML 对象树进行检查和验证。

示例代码

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    // 获取第一个根对象
    QObject *rootObject = engine.rootObjects().first();
    qDebug() << "Root object:" << rootObject;

    return app.exec();
}

在这个示例中,engine.rootObjects().first() 获取并打印了加载的 QML 文档的根对象。

六.为什么findChild<QObject*>(“myRectangle”) 不起作用:id vs objectName

在 Qt 中,findChild 方法用于查找对象树中的子对象。默认情况下,它只会查找直接的子对象,而不会递归地查找整个对象树。此外,findChild 方法通常依赖于对象的 objectName 属性,而不是 id 属性。

id vs. objectName

  • id:在 QML 中,id 是一个局部标识符,用于在 QML 文件中引用对象。它在 C++ 层不可见。
  • objectName:这是一个标准的 Qt 属性,可用于在 C++ 和 QML 层之间进行对象查找和引用。

为什么 findChild<QObject*>("myRectangle") 不起作用

提到的代码:

QObject *rectangle = rootObject->findChild<QObject*>("myRectangle");

如果没有设置 objectName,这个方法不会成功,因为它只查找对象的 objectName 属性,而不是 id

为什么 objectName 有效

当将 objectName 设置为 "myRectangle" 时,findChild 方法可以成功找到对象:

Rectangle {
    id: myRectangle
    objectName: "myRectangle"  // 添加 objectName
    ...
}

在 C++ 中:

QObject *rectangle = rootObject->findChild<QObject*>("myRectangle", Qt::FindChildrenRecursively);

使用 Qt::FindChildrenRecursively 标志可以确保在对象树中递归查找。

如何仅使用 findChild<QObject*>("myRectangle")

如果希望不使用递归查找,且对象位于 rootObject 的直接子对象中,需要确保对象的 objectName 已正确设置,并且对象确实是 rootObject 的直接子对象。

示例
QML 文件:main.qml
import QtQuick 2.0
import QtQuick.Controls 2.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    Rectangle {
        id: myRectangle
        objectName: "myRectangle"  // 添加 objectName
        width: 200
        height: 200
        color: "blue"

        function changeColor(newColor) {
            color = newColor;
        }
    }
}
C++ 文件:main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickItem>
#include <QMetaObject>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    QObject *rootObject = engine.rootObjects().first();
    QObject *rectangle = rootObject->findChild<QObject*>("myRectangle");

    if (rectangle) {
        qDebug() << "Rectangle found!";
        rectangle->setProperty("width", 300);
        rectangle->setProperty("color", "red");
        QMetaObject::invokeMethod(rectangle, "changeColor", Q_ARG(QVariant, "green"));
    } else {
        qDebug() << "Rectangle not found!";
    }

    return app.exec();
}

总结

为了在 C++ 代码中成功找到 QML 对象,需要确保以下几点:

  1. 在 QML 中设置对象的 objectName 属性。
  2. 使用 findChild 方法进行对象查找时,查找的是 objectName,而不是 id
  3. 如果对象不是直接子对象,使用 Qt::FindChildrenRecursively 标志进行递归查找。

这样做可以确保能够在 C++ 代码中准确查找到所需的 QML 对象。

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

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

相关文章

蜂鸣器:基础(1)

蜂鸣器&#xff1a;基础&#xff08;1&#xff09; 原文&#xff1a; In this tutorial, we are going to learn how to use the buzzer with Arduino, In detail, we will learn: 在本教程中&#xff0c;我们将学习如何将蜂鸣器与Arduino一起使用&#xff0c;将学习&#xff…

C的I/O操作

目录 引言 一、文件与目录操作 1. 打开与关闭文件 2. 文件读写操作 3. 文件定位与错误处理 二、字符流与字节流 1. 字符流处理 2. 字节流处理 三、序列化与反序列化 1. 序列化 2. 反序列化 四、新的I/O&#xff08;NIO&#xff09; 表格总结 文件与目录操作 字符…

证明 均匀分布 的期望和方差

均匀分布 均匀分布&#xff08;Uniform Distribution&#xff09;是一种常见的连续型概率分布&#xff0c;其中随机变量在给定区间内的每个值都有相同的概率。假设随机变量 ( X ) 在区间 ([a, b]) 上服从均匀分布&#xff0c;记作 均匀分布的概率密度函数&#xff08;PDF&am…

好用的库函数,qsort函数大详解(干货满满!)(进阶)

前言&#xff1a; 小编在上一篇文章说了这一篇将要写qsort函数的模拟实现&#xff0c;那么废话不多说&#xff0c;现在开始进入今天的代码之旅喽&#xff01; 目录&#xff1a; 1.qsort函数的模拟实现的逻辑和思路 2.qsort函数模拟实现的代码实现 3.代码展示 1.qsort函数的模…

YOLOv9独家提点|加入MobileViT 、SK 、Double Attention Networks、CoTAttention等几十种注意力机制(五)

本文介绍了YOLOv9模型的改进,包括加入多种注意力机制,如SE、CBAM、ECA和SimAM。此外,还探讨了MobileViT轻量级视觉Transformer在移动设备上的应用,以及SelectiveKernelNetworks和A2-Nets的双注意力结构。最后,CoTAttention网络在视觉问答任务中的改进展示了跨模态注意力交…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 6月17日,星期一

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年6月17日 星期一 农历五月十二 1、 今年首个红色山洪灾害预警&#xff1a;17日&#xff0c;浙江西南部、福建北部局地发生山洪灾害可能性很大。 2、 国家医疗保障局重构产科服务价格项目&#xff0c;“分娩镇痛”亲情陪产…

AI大模型在运动项目的深度融合和在穿戴设备的实践及未来运动健康技术发展

文章目录 1. 技术架构2. 模型选择2.1 LSTM&#xff08;长短期记忆网络&#xff09;2.2 CNN&#xff08;卷积神经网络&#xff09;2.3 Transformer 3. 数据处理数据预处理 4. 实时性要求4.1 边缘计算4.2 模型优化 5. 数据隐私与安全6. 深入分析AI大模型在穿戴设备的应用和未来发…

Yum安装LAMP

查看当前80端口是否被占用 ss -tulanp | grep 80查询httpd是否在yum源中 yum info httpd安装httpd yum -y install httpd启动httpd服务&#xff0c;设置开机自启 systemctl enable httpd --now systemctl start httpd查看当前进程 ps aux | grep httpd查看当前IP&#xff…

Google谈出海:品牌「性价比」转向「心价比」

Google Marketing Live中国站活动现场 越来越多的中国全球化品牌基于对全球消费和海外地区的深刻洞察&#xff0c;不断提升产品研发和迭代能力&#xff0c;在海外消费者心中塑造「中国质造」和「中国智造」的新形象。2023年6月15日&#xff0c;凯度与Google合作发布《2023 凯…

AI数据分析:根据Excel表格数据进行时间序列分析

ChatGPT中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个Python脚本编写的任务&#xff0c;具体步骤如下&#xff1a; 读取Excel表格&#xff1a;"F:\AI自媒体内容\AI行业数据分析\toolify月榜\toolify2023年-2024年月排行榜汇总数据.xlsx"…

vite-plugin-pwa 离线安装Vite应用

渐进式Web应用&#xff08;PWA&#xff09;通过结合 Web 和移动应用的特点&#xff0c;为用户带来更加流畅和快速的体验。且PWA支持离线访问能力&#xff08;访问静态资源本地缓存&#xff09;&#xff0c;极大提高了用户交互的流畅性&#xff0c;降低非必要的网络依赖。尤其适…

基于 U-Net 的图像分割

点击下方卡片&#xff0c;关注“小白玩转Python”公众号 图像分割是一种将图像划分为不同区域或对象的过程。它通常在像素级别进行&#xff0c;通过将图像中具有相似特征的区域分组或定义对象的边界来完成。这是一种识别和解析图像中不同对象或特征的方法。 假设一位医学专业人…

气象数据NC、grb2解析成矢量json、CMIS、MICPS及图片应用到webgis

一、基础概念 气象数据通常以多种格式存储和交换&#xff0c;以适应不同的应用需求和处理工具。以下是一些常见的气象数据格式及其转换方法的概述&#xff1a; 常见气象数据格式 1. NetCDF&#xff08;Network Common Data Form&#xff09;&#xff1a;一种自描述、自包含的…

SD卡、MicroSD卡与SD NAND的全面对比分析

在当今多样化的存储设备市场中&#xff0c;SD卡、MicroSD卡和SD NAND因其不同的特性和应用场景而广受欢迎。这三种存储解决方案各有千秋&#xff0c;为消费者提供了丰富的选择。以下是MK米客方德SD卡、MicroSD卡和SD NAND全面的对比分析&#xff1a;

springboot“漫画之家”系统 LW+PPT+源码

3 系统分析 链接&#xff1a;https://pan.baidu.com/s/1ihILTui-XEFdC15mcOB0vA?pwdewry 提取码&#xff1a;ewry 3.1系统可行性分析 3.1.1经济可行性 由于本系统是作为毕业设计系统&#xff0c;且系统本身存在一些技术层面的缺陷&#xff0c;并不能直接用于商业用途&#xf…

英伟达发布开源模型Nemotron-4 340B

&#x1f680; 英伟达发布开源模型Nemotron-4 340B 摘要&#xff1a;英伟达最新发布的开源模型Nemotron-4 340B&#xff0c;可能彻底改变大语言模型&#xff08;LLM&#xff09;训练方式。该模型支持多种自然语言和编程语言&#xff0c;使用9万亿个token训练&#xff0c;高达9…

python数据分析-心脏瓣膜手术风险分析与预测

一、研究背景和意义 人的心脏有四个瓣膜&#xff0c;主动脉银、二尖、肺动脉和三尖源 不管是那一个膜发生了病变&#xff0c;都会导致心脏内的血流受到影响&#xff0c;这就是通常所说的心脏期膜病&#xff0c;很多是需要通过手术的方式进行改善的。随着人口老龄化的加剧,&…

Aeron:Multi-Destination-Cast

Multi-Destination-Cast&#xff08;MDC&#xff09;是一种功能&#xff0c;允许 Aeron 从单个 Publication 同时向多个目的地传送数据。Multiple-Destination-Cast是 Aeron 的一项高级功能&#xff0c;本指南将介绍如何开发一个简单示例的基本知识。 一、MDC Publications 注&…

机器学习中的监督学习介绍

In this post well go into the concept of supervised learning, the requirements for machines to learn, and the process of learning and enhancing prediction accuracy. 在这篇文章中&#xff0c;我们将深入探讨监督学习的概念、机器学习的要求以及学习和提高预测准确…

Proxmox VE (PVE) 教学 (3) | 在 Proxmox VE 中安装与配置 OpenWrt

大家好,很长时间没有更新这个系列了。最近正在开发新项目,刚刚想起来我是不是还有一个什么专栏没更新。 本期的网络配置背景同于前两期的描述( 详见https://www.hestudio.net/category/proxmox-ve/ ),这一期只是对网络配置的扩展,也就是安装软路由,实现网络配置的更多功…