C++ 中如何访问 QML

作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

前言

上一章节我们介绍了在 Qt中如何注册一个C++类到qml,也就是在qml中使用C++的类。那么这一章节我们要倒过来,看看在C++中是如何去访问qml的,包括修改qml中控件的属性,以及动态创建qml组件。

Qt Quick 加载QML文档过程解析

先来看一下 Qt Quick 中常用的一些类的继承关系

inherit
inherit
inherit
inherit
inherit
inherit
inherit
inherit
QQuickItem
QObject
QQuickPaintedItem
QWindow
QQuickWindow
QQuickView
QCoreApplication
QGuiApplication
QApplication
inherit
inherit
inherit
inherit
inherit
inherit
QJSEngine
QObject
QQmlEngine
QQmlApplicationEngine
QQmlContext
QQmlComponent
QQmlExpression

介绍几个重要的成员:

  1. QQmlEngine
    每个QML组件都在 QQmlContext 中实例化。QQmlContext 对于向QML组件传递数据至关重要。在QML中上下文按层次结构排列,此层次结构由 QQmlEngine 管理。在创建任何QML组件之前,应用程序必须已创建 QQmlEngine 才能访问QML上下文

  2. QQmlContext
    上下文允许数据暴露给由QML引擎实例化的QML组件。每个 QQmlContext 都包含一组不同于其QObject属性的属性,这些属性允许按名称将数据显式绑定到上下文。通过调用 QQmlContext::setContextProperty() 来定义和更新上下文属性。

  3. QQmlComponent
    组件是可重用的、封装的QML类型,具有定义良好的接口。可以从QML文件创建QQmlComponent实例。

  4. QQmlExpression
    动态执行表达式 QQmlExpression, 允许客户端在C++中利用一个特定的QML上下文执行JavaScript表达式,表达式执行的结果以QVariant的形式返回,并且遵守QML引擎确定的转换规则。

  5. QQuickWindow
    QQuickWindow 提供与 QQuickItems 的场景交互和显示所需的图形场景管理。QQuickWindow总是有一个不可见的根项。若要将项添加到此窗口,请将项重新分配到根项或场景中的现有项。

  6. QQuickView
    这是QQuickWindow的一个子类,当给定主源文件的URL时,它将自动加载和显示QML场景。或者,您可以使用QQmlComponent实例化自己的对象,并将它们放置在手动设置的QQuickWindow中。

Qt Quick 加载一个 QML 对象的条件:

  • 唯一的 一个 QQmlEngine 引擎用于加载QML文件
  • 唯一的 一个 QQmlContext 用于QML对象实例化和表达式执行的上下文环境
  • 一个 QQmlComponent 组件用于实例化第一个QML对象

第一种加载方式:QQmlEngine + QQmlComponent

#include <QApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickWindow>
#include <QDebug>

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

    QQmlEngine engine;
    QQmlComponent *component = new QQmlComponent(&engine);

    QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));

    component->loadUrl(QUrl("qrc:/main.qml"));

    if (!component->isReady() ) {
        qWarning("%s", qPrintable(component->errorString()));
        return -1;
    }

    QObject *topLevel = component->create();
    QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);

    QSurfaceFormat surfaceFormat = window->requestedFormat();
    window->setFormat(surfaceFormat);
    window->show();

    return app.exec();
}

使用 QQmlComponent 来实例化 QML 对象的注意事项:

  1. 要在 QQmlEngine 实例不可用的代码中创建组件实例,可以使用qmlContext() 或qmlEngine()
  2. 必须通过 component.create() 来创建实例
  3. 创建完成之后需要把对象放入窗口下才能显示

第二种加载方式:QQmlApplicationEngine + Window

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}


import QtQuick 2.12
import QtQuick.Window 2.12

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

QQmlApplicationEngine 是Qt中创建 Qt Quick 工程默认使用的引擎。
QQmlApplicationEngine 联合了 QQmlEngine 和 QmlComponent 去加载单独的QML文件,QQmlApplicationEngine 加载以 Window 为根对象的QML文档,QML文档拥有窗口的完整控制权

第三种加载方式:QQuickView + Item

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQuickView *view = new QQuickView;
    view->setSource(QUrl("qrc:/main.qml"));
    view->show();
    return app.exec();
}


import QtQuick 2.12

Item {
    visible: true
    width: 640
    height: 480
}

不同于 QQmlApplicationEngine 的是,使用 QQuickView 显示QML文档,对窗口的控制权全部在C++代码

C++中各种加载方式通过 object name 修改qml中的属性

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQuickWindow>
#include <QQuickView>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    // 1. 使用 QQmlApplicationEngine
//    QQmlApplicationEngine engine;
//    const QUrl url(QStringLiteral("qrc:/main.qml"));
//    engine.load(url);

//    QObject *rect = engine.rootObjects().at(0)->findChild<QObject*>("rect");
//    if (rect)
//        rect->setProperty("color", "red");



    // 2. 使用 QQmlEngine + QQmlComponent
//    QQmlEngine engine;
//    QQmlComponent *component = new QQmlComponent(&engine);
//    component->loadUrl(QUrl("qrc:/main.qml"));
//    QObject *topLevel = component->create();
//    QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
//    window->show();

//    QObject *rect = topLevel->findChild<QObject*>("rect");
//      if (rect)
//          rect->setProperty("color", "red");



    // 3. 使用 QQuickView
    QQuickView *view = new QQuickView;
    view->setSource(QUrl("qrc:/main.qml"));
    view->show();

    QObject *rect = view->findChild<QObject*>("rect");
    if (rect)
        rect->setProperty("color", "red");

    return app.exec();
}


import QtQuick 2.12
import QtQuick.Window 2.12

Item {
    visible: true
    width: 640
    height: 480

    Rectangle {
        objectName: "rect"
        anchors.fill: parent
        color: "yellow"
    }
}

Qt中的对象是一种树形的结构,对Qt对象树模型不了解的同学可以参考 Qt 对象树。所以我们只要知道了一个节点,就可以通过 findChild 来找到他的子节点。以上三种方式都可以通过 findChild 来找到object然后修改属性。前面两种方式,qml文件的根对象是Window,而最后一种是Item

C++ 调用qml中的 function

import QtQuick 2.12

Item {
    function myQmlFunction(msg) {
        console.log("Got message:", msg)
        return "some return value"
    }
}


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

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlEngine engine;
    QQmlComponent component(&engine, "qrc:/main.qml");
    QObject *object = component.create();

    QVariant returnedValue;
    QVariant msg = "Hello from C++";
    QMetaObject::invokeMethod(object, "myQmlFunction",
                              Q_RETURN_ARG(QVariant, returnedValue),
                              Q_ARG(QVariant, msg));

    qDebug() << "QML function returned:" << returnedValue.toString();
    delete object;

    return app.exec();
}

运行结果:
qml: Got message: Hello from C++
QML function returned: "some return value"

C++ 连接qml中的信号

myclass.h

#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>
#include <QDebug>

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

signals:

public slots:
    void cppSlot(const QString &msg) {
        qDebug() << "Called the C++ slot with message:" << msg;
    }
};

#endif // MYCLASS_H
main.qml

import QtQuick 2.0

Item {
    width: 100; height: 100

    signal qmlSignal(string msg)

    MouseArea {
        anchors.fill: parent
        onClicked: qmlSignal("Hello from QML")
    }
}
main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QQuickItem>
#include "myclass.h"

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

    QQuickView view(QUrl("qrc:/main.qml"));
    QObject *item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QString)),
                     &myClass, SLOT(cppSlot(QString)));

    view.show();
    return app.exec();
}

运行结果:
Called the C++ slot with message: "Hello from QML"
  • 17
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++程序使用QML QML API是分为三个主类——QDeclarativeEngine, QdeclarativeComponent 与 QDeclarativeContext。QDeclarativeEngine 提供QML运行的环境,QdeclarativeComponent 封装了QML Documents 与QDeclarativeContext允许程序导出数据到QML组件实例。 QML还包含了API的一个方便,通过QDeclarativeView 应用程序只需要简单嵌入QML组件到一个新的QGraphicsView就可以了。这有许多细节将在下面讨论。QDeclarativeView 主要是用于快速成型的应用程序里。 如果你是重新改进使用QML的Qt应用程序,请参阅 整合QML到现有的Qt UI代码。 基本用法 每个应用程序至少需求一个QDeclarativeEngine。QDeclarativeEngine允许配置全局设置应用到所有的QML组件实例,例如QNetworkAccessManager是用于网络通信以及永久储存的路径。如果应用程序需求在QML组件实例间需求不同的设置只需要多个QDeclarativeEngine。 使用QDeclarativeComponent类载入QML Documents。每个QDeclarativeComponent实例呈现单一QML文档。QDeclarativeComponent可以传递一个文档的地址或文档的原始文本内容。该文档的URL可以是本地文件系统的地址或通过QNetworkAccessManager支持的网络地址。 QML组件实例通过调用QDeclarativeComponent::create()模式来创建。在这里载入一个QML文档的示例并且从它这里创建一个对象。 QDeclarativeEngine *engine = new QDeclarativeEngine(parent); QDeclarativeComponent component(engine, QUrl::fromLocalFile(“main.qml”)); QObject *myObject = component.create(); 导出数据 QML组件是以QDeclarativeContext实例化的。context允许应用程序导出数据到该QML组件实例。单个QDeclarativeContext 可用于一应用程序的所有实例对象或针对每个实例使用QDeclarativeContext 可以创建更为精确的控制导出数据。如果不传递一个context给QDeclarativeComponent::create()模式;那么将使用QDeclarativeEngine的root context。数据导出通过该root context对所有对象实例是有效的。 简单数据 为了导出数据到一个QML组件实例,应用程序设置Context属性;然后由QML属性绑定的名称与JavaScrip访问。下面的例子显示通过QGraphicsView如何导出一个背景颜色到QML文件: //main.cpp #include <QApplication> #include <QDeclarativeView> #include <QDeclarativeContext> int main(int argc, char *argv[]) { QApplication app(argc, argv); QDeclarativeView view; QDeclarativeContext *context = view.rootContext(); context->setContextProperty(“backgroundColor”, QColor(Qt::yellow)); view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show(); return app.exec(); } //main.qml import Qt 4.7 Rectangle { width: 300 height: 300 color: backgroundColor Text { anchors.centerIn: parent text: “Hello Yellow World!” } } 或者,如果你需要main.cpp不需要在QDeclarativeView显示创建的组件,你就需要使用QDeclarativeEngine::rootContext()替代创建QDeclarativeContext实例。 QDeclarativeEngine engine; QDeclarativeContext *windowContext = new QDeclarativeContext(engine.rootContext()); windowContext->setContextProperty(“backgroundColor”, QColor(Qt::yellow)); QDeclarativeComponent component(&engine, “main.qml”); QObject *window = component.create(windowContext); Context属性的操作像QML绑定的标准属性那样——在这个例子的backgroundColor Context属性改变为红色;那么该组件对象实例将自动更新。注意:删除任意QDeclarativeContext的构造是创建者的事情。当window组件实例撤消时不再需要windowContext时,windowContext必须被消毁。最简单的方法是确保它设置window作为windowContext的父级。 QDeclarativeContexts 是树形结构——除了root context每个QDeclarativeContexts都有一个父级。子级QDeclarativeContexts有效的继承它们父级的context属性。这使应用程序分隔不同数据导出到不同的QML对象实例有更多自由性。如果QDeclarativeContext设置一context属性,同样它父级也被影响,新的context属性是父级的影子。如下例子,background context属性是Context 1,也是root context里background context属性的影子。 结构化数据 context属性同样可用于输出结构化与写数据到QML对象。除了QVariant支持所有已经存在的类型外,QObject 派生类型可以分配给context属性。 QObject context属性允许数据结构化输出并允许QML来设置值。 下例创建CustomPalette对象并设置它作为palette context属性。 class CustomPalette : public QObject { Q_OBJECT Q_PROPERTY(QColor background READ background WRITE setBackground NOTIFY backgroundChanged) Q_PROPERTY(QColor text READ text WRITE setText NOTIFY textChanged) public: CustomPalette() : m_background(Qt::white), m_text(Qt::black) {} QColor background() const { return m_background; } void setBackground(const QColor &c) { if (c != m_background) { m_background = c; emit backgroundChanged(); } } QColor text() const { return m_text; } void setText(const QColor &c) { if (c != m_text) { m_text = c; emit textChanged(); } } signals: void textChanged(); void backgroundChanged(); private: QColor m_background; QColor m_text; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QDeclarativeView view; view.rootContext()->setContextProperty(“palette”, new CustomPalette); view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show(); return app.exec(); } QML引用palette对象以及它的属性,为了设置背景与文本的颜色,这里是当单击窗口时,面板的文本颜色将改变成蓝色。 import Qt 4.7 Rectangle { width: 240 height: 320 color: palette.background Text { anchors.centerIn: parent color: palette.text text: “Click me to change color!” } MouseArea { anchors.fill: parent onClicked: { palette.text = “blue”; } } } 可以检测一个C++属性值——这种情况下的CustomPalette的文本属性改变,该属性必须有相应的NOTIFY信息。NOTIFY信号是属性值改变时将指定一个信号发射。 实现者应该注意的是,只有值改变时才发射信号,以防止发生死循环。访问一个绑定的属性,没有NOTIFY信号的话,将导致QML在运行时发出警告信息。 动态结构化数据 如果应用程序对结构化过于动态编译QObject类型;那么对动态结构化数据可在运行时使用QDeclarativePropertyMap 类构造。 从QML调用 C++ 通过public slots输出模式或Q_INVOKABLE标记模式使它可以调用QObject派生出的类型。 C++模式同样可以有参数并且可以返回值。QML支持如下类型: •bool •unsigned int, int •float, double, qreal •QString •QUrl •QColor •QDate,QTime,QDateTime •QPoint,QPointF •QSize,QSizeF •QRect,QRectF •QVariant 下面例子演示了,当MouseArea单击时控制“Stopwatch”对象的开关。 //main.cpp class Stopwatch : public QObject { Q_OBJECT public: Stopwatch(); Q_INVOKABLE bool isRunning() const; public slots: void start(); void stop(); private: bool m_running; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QDeclarativeView view; view.rootContext()->setContextProperty(“stopwatch”, new Stopwatch); view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show(); return app.exec(); } //main.qml import Qt 4.7 Rectangle { width: 300 height: 300 MouseArea { anchors.fill: parent onClicked: { if (stopwatch.isRunning()) stopwatch.stop() else stopwatch.start(); } } } 值得注意的是,在这个特殊的例子里有更好的方法来达到同样的效果,在main.qml有”running”属性,这将会是一个非常优秀的QML代码: // main.qml import Qt 4.7 Rectangle { MouseArea { anchors.fill: parent onClicked: stopwatch.running = !stopwatch.running } } 当然,它同样可以调用 functions declared in QML from C++。 网络组件 如果URL传递给QDeclarativeComponent是一网络资源或者QML文档引用一网络资源,QDeclarativeComponent要先获取网络数据;然后才可以创建对象。在这种情况下QDeclarativeComponent将有Loading status。直到组件调用QDeclarativeComponent::create()之前,应用程序将一直等待。 下面的例子显示如何从一个网络资源载入QML文件。在创建QDeclarativeComponent之后,它测试组件是否加载。如果是,它连接QDeclarativeComponent::statusChanged()信号,否则直接调用continueLoading()。这个测试是必要的,甚至URL都可以是远程的,只是在这种情况下要防组件是被缓存的。 MyApplication::MyApplication() { // … component = new QDeclarativeComponent(engine, QUrl(“http://www.example.com/main.qml”)); if (component->isLoading()) QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueLoading())); else continueLoading(); } void MyApplication::continueLoading() { if (component->isError()) { qWarning() << component->errors(); } else { QObject *myObject = component->create(); } } Qt资源 QML的内容可以使用qrc:URL方案从Qt 资源系统载入。例如: [project/example.qrc] <!DOCTYPE RCC> <RCC version=”1.0″> <qresource prefix=”/”> <file>main.qml</file> <file>images/background.png</file> </qresource> </RCC> [project/project.pro] QT += declarative SOURCES += main.cpp RESOURCES += example.qrc [project/main.cpp] int main(int argc, char *argv[]) { QApplication app(argc, argv); QDeclarativeView view; view.setSource(QUrl(“qrc:/main.qml”)); view.show(); return app.exec(); } [project/main.qml] import Qt 4.7 Image { source: “images/background.png” } 请注意,资源系统是不能从QML直接访问的。如果主QML文件被加载作为资源,所有的文件指定在QML做为相对路径从资源系统载入。在QML层使用资源系统是完全透明的。这也意味着,如果主QML文件没有被加载作为资源,那么从QML不能访问资源系统。 1.这里主要是介绍,如何在c++调用QML的函数和设置QML的属性的问题 2.具体代码 // UICtest.qml import Qt 4.7 Rectangle { id: mainWidget; width: 640 height: 480 function callbyc(v) { mainWidget.color = v; return "finish"; } Rectangle{ id: secondRect; x: 100; y: 20; width: 400; height: 300; Rectangle{ x: 10; y: 20; width: 30; height: 40; color: "#FF035721" Text { objectName: "NeedFindObj"; anchors.fill: parent; text: ""; } } } } // main.cpp #include <QtGui/QApplication> #include <QtDeclarative/QDeclarativeView> #include <QtDeclarative/QDeclarativeEngine> #include <QtDeclarative/QDeclarativeComponent> #include <QtDeclarative/QDeclarativeContext> #include <QtDeclarative/QDeclarativeItem> #include <QMetaObject> int main(int argc, char *argv[]) { QApplication a(argc, argv); QDeclarativeView qmlView; qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml")); qmlView.show(); // 获取根节点,就是 QML id是mainWidget的节点 QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(qmlView.rootObject()); item->setProperty("color", QVariant("blue")); // 查找到我们需要的节点根均objectname NeedFindObj 来获得,并设置他的文本属性 QDeclarativeItem *item1 = item->findChild<QDeclarativeItem *>("NeedFindObj"); if (item1) { item1->setProperty("text", QVariant("OK")); } // 调用QML的函数, 分别是 函数所在的对象, 函数名,返回值, 参数 QVariant returnVar; QVariant arg1 = "blue"; QMetaObject::invokeMethod(item, "callbyc", Q_RETURN_ARG(QVariant, returnVar),Q_ARG(QVariant, arg1)); qDebug(" %s",returnVar.toString().toLocal8Bit().data()); return a.exec(); } 说明: 这里的根节点是id为mainWidget的矩形元素,那么在C++获取根节点后就可以,直接的设置他的属性了。其他属性也可以同样,调用指定节点内的函数是通过QMetaObject的invokeMethod 来进行调用的。 最后所有关于QMLc++交互部分就基本写完,如果想要更多的东西,或者一些其他方法,强烈看看 http://doc.qt.nokia.com/4.7-snapshot/qtbinding.html,或者帮助文档,(究竟是不是我的文档里面没有还是怎么的)
### 回答1: 在Qt C++使用文路径到QML,可以通过以下步骤实现: 1. 首先,确保项目的.pro文件添加了对文路径的支持。在.pro文件添加如下代码: ``` # 用于支持文路径 CONFIG += utf8_sources ``` 2. 在Qt C++,使用QDir类来处理文路径。使用QDir的toNativeSeparators()方法将文路径转换为本地路径。例如: ``` QString chinesePath = "文路径"; QString nativePath = QDir::toNativeSeparators(chinesePath); ``` 3. 在要将文路径传递给QML的地方,将转换后的本地路径传递给QML的属性或绑定。例如,将转换后的本地路径传递给一个名为"sourcePath"的QML属性: ``` QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("sourcePath", nativePath); ``` 4. 在QML,通过使用转换后的本地路径加载和处理文路径。例如,使用Image元素加载图像并使用转换后的本地路径: ``` Image { source: sourcePath } ``` 通过以上步骤,您可以在Qt C++使用文路径到QML。请务必处理好转换后的本地路径,以确保正确加载和处理文路径。 ### 回答2: 在Qt C++,要将文路径传递给QML文件,可以通过以下步骤实现。 首先,确保你的Qt版本支持文路径。然后,创建一个QML文件,并使用QQuickView或QQuickWidget加载它。 接下来,在你的QML文件,可以通过使用QString类型的属性来接收文路径。例如,在QML文件添加一个属性: property string chinesePath: "" 然后,你可以在C++代码,通过setProperty()函数将文路径传递给QML文件的属性。例如,如果你有一个QString类型的变量chinesePath,你可以这样传递它的值: viewer->rootObject()->setProperty("chinesePath", chinesePath); 此外,在QML文件,你可以使用Qt提供的FileDialog来选择文路径。例如,你可以在QML添加一个按钮,当点击时,弹出文件选择对话框,并将选择的路径传递给上述的chinesePath属性。 FileDialog { id: fileDialog // 设置文件选择对话框属性 title: "选择文件" folder: Qt.resolvedUrl(".") // 设置默认打开目录为当前目录 selectFolder: true // 打开目录选择模式 onAccepted: { chinesePath = fileDialog.fileUrl.toString().substring(8); // 去除file:/// } } 最后,在QML,你可以使用这个属性来操作文路径,例如,加载一个图片: Image { source: chinesePath } 总的来说,要将文路径传递给QML文件,你需要在C++QML之间传递文路径的字符串,并在QML使用这个字符串来操作文路径。通过这种方式,你就可以在Qt C++实现文路径到QML的传递了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值