C++生成QML代码与QML里面集成QWidget

简介: 这篇文章介绍了如何在C++中生成QML代码,以及如何在QML中集成QWidget,包括使用Qt Widgets嵌入到QML界面中的技术示例。

1 QML代码生成

/******************************************************************************
 * QSkinny - Copyright (C) 2016 Uwe Rathmann
 * This file may be used under the terms of the 3-clause BSD License
 *****************************************************************************/

#pragma once

#include "GridAccessor.h"
#include <QQuickWidget>

class QGraphicsGridLayout;

class GridQuick : public QQuickWidget, public GridAccessor
{
   
  public:
    GridQuick( QWidget* parent = nullptr );
    ~GridQuick() override;

    void insert( const QByteArray& colorName,
        int row, int column, int rowSpan, int columnSpan ) override;

    void setSpacing( Qt::Orientations, int spacing ) override;

    void setStretchFactor( int pos, Qt::Orientation, int stretch ) override;
    void setSizeHint( int pos, Qt::Orientation, Qt::SizeHint, int hint ) override;

    void setSizeHintAt( int index, Qt::Orientation, Qt::SizeHint, int hint ) override;
    void setSizePolicyAt( int index, Qt::Orientation, int policy ) override;
    void setAlignmentAt( int index, Qt::Alignment ) override;
    void setRetainSizeWhenHiddenAt( int index, bool on ) override;
    void setVisibleAt( int index, bool on ) override;

    QSize preferredSize() const override;

  protected:
    void resizeEvent( QResizeEvent* ) override;

  private:
    QQuickItem* m_grid;
};
/******************************************************************************
 * QSkinny - Copyright (C) 2016 Uwe Rathmann
 * This file may be used under the terms of the 3-clause BSD License
 *****************************************************************************/

#include "GridQuick.h"
#include <QQuickItem>
#include <QtQml>
#include <QDebug>

static QQuickItem* createQml( const char* qmlCode )
{
   
    QQmlEngine engine( nullptr );

    QQmlComponent component( &engine );
    component.setData( qmlCode, QUrl() );

    if ( component.status() != QQmlComponent::Ready )
        qWarning() << component.errorString();

    return qobject_cast< QQuickItem* >( component.create() );
}

static QQuickItem* itemAt( const QQuickItem* grid, int index )
{
   
    const auto children = grid->childItems();
    if ( ( index >= 0 ) && ( index < children.count() ) )
        return children.at( index );

    return nullptr;
}

static QObject* attachedProperties( const QQuickItem* item )
{
   
    for ( auto child : item->children() )
    {
   
        if ( child->inherits( "QQuickLayoutAttached" ) )
            return child;
    }

    return nullptr;
}

static QObject* attachedPropertiesAt( const QQuickItem* grid, int index )
{
   
    if ( auto item = itemAt( grid, index ) )
        return attachedProperties( item );

    return nullptr;
}

GridQuick::GridQuick( QWidget* parent )
    : QQuickWidget( parent )
{
   
    setContentsMargins( QMargins() );
    setResizeMode( QQuickWidget::SizeRootObjectToView );

    auto contentItem =
        createQml( "import QtQuick 2.0\nimport QtQuick.Layouts 1.1\nItem { GridLayout {} }" );
    setContent( QUrl(), nullptr, contentItem );

    m_grid = contentItem->childItems().constFirst();
    m_grid->setProperty( "rowSpacing", 5 );
    m_grid->setProperty( "columnSpacing", 5 );
}

GridQuick::~GridQuick()
{
   
}

void GridQuick::insert( const QByteArray& colorName,
    int row, int column, int rowSpan, int columnSpan )
{
   
    /*
        We need to create a temporary layout in QML, so that the
        object for the attachedProperties is created early
     */
    auto layout = createQml( "import QtQuick 2.0\nimport QtQuick.Layouts 1.15\nGridLayout { Rectangle {} }" );

    auto rectangle = layout->childItems().constFirst();
    rectangle->setParent( nullptr );

    delete layout;

    rectangle->setParent( m_grid );
    rectangle->setParentItem( m_grid );

    rectangle->setImplicitWidth( 50 );
    rectangle->setImplicitHeight( 50 );
    rectangle->setProperty( "color", QColor( colorName.constData() ) );

    if ( auto props = attachedProperties( rectangle ) )
    {
   
        props->setProperty( "row", row );
        props->setProperty( "column", column );
        props->setProperty( "rowSpan", rowSpan );
        props->setProperty( "columnSpan", columnSpan );
        props->setProperty( "fillWidth", true );
        props->setProperty( "fillHeight", true );
    }
}

void GridQuick::setSpacing( Qt::Orientations orientations, int spacing )
{
   
    if ( orientations & Qt::Vertical )
        m_grid->setProperty( "rowSpacing", spacing );

    if ( orientations & Qt::Horizontal )
        m_grid->setProperty( "columnSpacing", spacing );
}

void GridQuick::setSizeHint( int, Qt::Orientation, Qt::SizeHint, int )
{
   
    qWarning() << "setSizeHint is not supported by Quick Layouts.";
}

void GridQuick::setStretchFactor( int, Qt::Orientation, int )
{
   
    qWarning() << "setStretchFactor is not supported by Quick Layouts.";
}

void GridQuick::setSizeHintAt( int index, Qt::Orientation orientation,
    Qt::SizeHint which, int hint )
{
   
    if ( auto props = attachedPropertiesAt( m_grid, index ) )
    {
   
        const qreal size = hint;

        switch( static_cast< int >( which ) )
        {
   
            case Qt::MinimumSize:
            {
   
                if ( orientation == Qt::Horizontal )
                    props->setProperty( "minimumWidth", size );
                else
                    props->setProperty( "minimumHeight", size );

                break;
            }
            case Qt::PreferredSize:
            {
   
                if ( orientation == Qt::Horizontal )
                    props->setProperty( "preferredWidth", size );
                else
                    props->setProperty( "preferredHeight", size );

                break;
            }
            case Qt::MaximumSize:
            {
   
                if ( orientation == Qt::Horizontal )
                    props->setProperty( "maximumWidth", size );
                else
                    props->setProperty( "maximumHeight", size );

                break;
            }
        }
    }
}

void GridQuick::setSizePolicyAt(
    int index, Qt::Orientation orientation, int policy )
{
   
    const auto qPolicy = static_cast< QSizePolicy::Policy >( policy & 0xF );

#if 0
    const bool isConstrained = policy & ( 1 << 4 );
#endif

    const bool doFill = ( qPolicy & QSizePolicy::GrowFlag )
        || ( qPolicy & QSizePolicy::ExpandFlag );

    if ( auto props = attachedPropertiesAt( m_grid, index ) )
    {
   
        if ( orientation == Qt::Horizontal )
            props->setProperty( "fillWidth", doFill );
        else
            props->setProperty( "fillHeight", doFill );
    }
}

void GridQuick::setAlignmentAt( int index, Qt::Alignment alignment )
{
   
    if ( auto props = attachedPropertiesAt( m_grid, index ) )
        props->setProperty( "alignment", QVariant::fromValue( alignment ) );
}

void GridQuick::setRetainSizeWhenHiddenAt( int, bool )
{
   
    qWarning() << "setRetainSizeWhenHidden is not supported by Quick Layouts.";
}

void GridQuick::setVisibleAt( int index, bool on )
{
   
    if ( auto item = itemAt( m_grid, index ) )
        item->setVisible( on );
}

static const qreal margin = 10.0;

QSize GridQuick::preferredSize() const
{
   
    return QSize(
        m_grid->implicitWidth() + 2 * margin,
        m_grid->implicitHeight() + 2 * margin );
}

void GridQuick::resizeEvent( QResizeEvent* event )
{
   
    QQuickWidget::resizeEvent( event );

    m_grid->setX( margin );
    m_grid->setY( margin );
    m_grid->setWidth( width() - 2 * margin );
    m_grid->setHeight( height() - 2 * margin );
}

2 注册机制的含义


#include "QskQuickItem.h"
#include "QskQuickItemPrivate.h"
#include "QskQuick.h"
#include "QskEvent.h"
#include "QskSetup.h"
#include "QskSkin.h"
#include "QskDirtyItemFilter.h"

#include <qglobalstatic.h>
#include <qquickwindow.h>

#if defined( QT_DEBUG )

QSK_QT_PRIVATE_BEGIN

#if QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 )
    #ifndef emit
        #define emit
        #include <private/qabstractanimation_p.h>
        #undef emit
    #endif
#endif

#include <private/qquickpositioners_p.h>

QSK_QT_PRIVATE_END

#endif

#include <unordered_set>

static inline void qskSendEventTo( QObject* object, QEvent::Type type )
{
   
    QEvent event( type );
    QCoreApplication::sendEvent( object, &event );
}

static inline void qskApplyUpdateFlags(
    QskQuickItem::UpdateFlags flags, QskQuickItem* item )
{
   
    auto d = static_cast< QskQuickItemPrivate* >( QskQuickItemPrivate::get( item ) );
    d->applyUpdateFlags( flags );
}

static inline void qskFilterWindow( QQuickWindow* window )
{
   
    if ( window == nullptr )
        return;

    static QskDirtyItemFilter itemFilter;
    itemFilter.addWindow( window );
}

namespace
{
   
    class QskQuickItemRegistry
    {
   
      public:
        QskQuickItemRegistry()
        {
   
            /*
                Its faster and saves some memory to have this registry instead
                of setting up direct connections between qskSetup and each control
             */
            QObject::connect( qskSetup, &QskSetup::itemUpdateFlagsChanged,
                qskSetup, [ this ] {
    updateControlFlags(); } );

            /*
                We would also need to send QEvent::StyleChange, when
                a window has a new skin. TODO ...
             */
            QObject::connect( qskSetup, &QskSetup::skinChanged,
                qskSetup, [ this ] {
    updateSkin(); } );
        }

        inline void insert( QskQuickItem* item )
        {
   
            m_items.insert( item );
        }

        inline void remove( QskQuickItem* item )
        {
   
            m_items.erase( item );
        }

        void updateControlFlags()
        {
   
            const auto flags = qskSetup->itemUpdateFlags();

            for ( auto item : m_items )
                qskApplyUpdateFlags( flags, item );
        }

        void updateSkin()
        {
   
            QEvent event( QEvent::StyleChange );

            for ( auto item : m_items )
            {
   
                event.setAccepted( true );
                QCoreApplication::sendEvent( item, &event );
            }
        }

      private:
        std::unordered_set< QskQuickItem* > m_items;
    };
}

namespace
{
   
    /*
        A helper class to store the released window to be able to
        put it later into the WindowChange event.
     */
    class QskWindowStore
    {
   
      public:
        QskWindowStore()
            : m_refCount( 0 )
            , m_window( nullptr )
        {
   
        }

        void setWindow( QQuickWindow* window )
        {
   
            if ( m_window != window )
            {
   
                m_window = window;
                m_refCount = 0;
            }

            if ( m_window )
                m_refCount++;
        }

        QQuickWindow* window()
        {
   
            QQuickWindow* w = m_window;

            if ( m_window )
            {
   
                if ( --m_refCount == 0 )
                    m_window = nullptr;
            }

            return w;
        }

      private:
        int m_refCount;
        QQuickWindow* m_window;
    };
}

Q_GLOBAL_STATIC( QskQuickItemRegistry, qskRegistry )
Q_GLOBAL_STATIC( QskWindowStore, qskReleasedWindowCounter )

QskQuickItem::QskQuickItem( QskQuickItemPrivate& dd, QQuickItem* parent )
    : QQuickItem( dd, parent )
{
   
    setFlag( QQuickItem::ItemHasContents, true );

    if ( dd.updateFlags & QskQuickItem::DeferredUpdate )
        qskFilterWindow( window() );

    qskRegistry->insert( this );
}

QskQuickItem::~QskQuickItem()
{
   
    /*
        We set componentComplete to false, so that operations
        that are triggered by detaching the item from its parent
        can be aware of the about-to-delete state.
     */
    d_func()->componentComplete = false;

    if ( qskRegistry )
        qskRegistry->remove( this );
}

const char* QskQuickItem::className() const
{
   
    return metaObject()->className();
}

void QskQuickItem::classBegin()
{
   
    Inherited::classBegin();
}

3 QWidgetInQml QML里面集成widget


bool QMLInteract::initUI()
{
   
    m_engin.rootContext()->setContextProperty("QMLInteractObj", this);
    m_engin.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
    if (m_engin.rootObjects().isEmpty())
    {
   
        return false;
    }

    QObject* obj = m_engin.rootObjects().at(0);
    basewindowobj.setWndBaseInfo(obj);
    basewindowobj.setWndSplitType();

    m_engin.load(QUrl(QStringLiteral("qrc:/qml/CalibTwoPage.qml")));

    QObject* obj1 = m_engin.rootObjects().at(1);

    return true;
}





void BaseWindowContainer::setWndBaseInfo(QObject* obj)
{
   
    QWindow * mainWindow = qobject_cast<QWindow*>(obj);

    if (obj)
    {
   
        WId proc2Window_HWND = mainWindow->winId();

        m_widget->setProperty("_q_embedded_native_parent_handle", QVariant(proc2Window_HWND));
        m_widget->setWindowFlags(Qt::Widget|Qt::FramelessWindowHint);
        m_widget->winId();
        m_widget->setStyleSheet("background-color: rgb(46,138,201)");
        m_widget->windowHandle()->setParent(mainWindow);

        m_delegateObj = obj;
    }
}

4 QML_OSR_EXP 将Qt Widgets嵌入到QML界面中的一种示范


bool WidgetOSRItem::sendEventToOSRWidget(QEvent *e)
{
   
    QWindow* wHandle = mOSRWidget->windowHandle();
    bool res = false;
    if(wHandle)
    {
   
        res = qApp->sendEvent(wHandle, e);
    }

    return res;
}

#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))

bool WidgetOSRItem::eventFilter(QObject *obj, QEvent *e)
{
   
    QWindow* pw = (QWindow*)(window());
    bool res = QQuickPaintedItem::eventFilter(obj, e);
    if(obj == mOSRWidget)
    {
   
        switch(e->type())
        {
   
        case QEvent::Paint: //当OsrWidget paint的时候也触发自己paint
        {
   
            QPaintEvent* pe = (QPaintEvent*)e;
            this->update(pe->rect());
        }
            break;
        }
    }
    else if(obj == pw)
    {
   

        //* 如果是鼠标等(有鼠标坐标信息的事件。)的话我们得计算一下偏移量并修正一下,这里就只处理QMouseEvent和QWheelEvent作为示例
        //* 如果有其他类似的也需要修正,不然可能坐标偏移
        switch(e->type())
        {
        case QEvent::MouseButtonDblClick  :
        case QEvent::MouseButtonPress      :
        case QEvent::MouseButtonRelease      :
        case QEvent::MouseMove              :
        case QEvent::MouseTrackingChange  :
        case QEvent::Move                  :
        {
            QMouseEvent *me = (QMouseEvent*)e;
            QEvent::Type type = me->type();
            QPointF localPosF(QPointF(0,0));
//          QPointF localPosF = me->position();
            Qt::MouseButton mouseButton = me->button();
            Qt::MouseButtons mouseButtons = me->buttons();
            Qt::KeyboardModifiers modifiers = me->modifiers();

            //修正一下localpos
            QPointF offsetF = mapToScene(QPoint(0,0));
            QPointF diffPosF = localPosF - offsetF;

            QMouseEvent tme(type, diffPosF, mouseButton, mouseButtons, modifiers);
            sendEventToOSRWidget(&tme);
        }
            break;
        case QEvent::Wheel:
        {
            QWheelEvent *we = (QWheelEvent*)e;
            QPointF localPosF = we->position();
            QPointF gloabalPosF = we->globalPosition();
            QPoint  pixelDelta = we->pixelDelta();
            QPoint  angleDelta = we->angleDelta();

            Qt::MouseButtons mouseButtons = we->buttons();
            Qt::KeyboardModifiers modifiers = we->modifiers();

            //修正一下localpos
            QPointF offsetF = mapToScene(QPoint(0,0));
            QPointF diffPosF = localPosF - offsetF;

            QWheelEvent twe(diffPosF, gloabalPosF, pixelDelta, angleDelta,
                               mouseButtons, modifiers, Qt::ScrollBegin, false);

            sendEventToOSRWidget(&twe);
        }

            break;
        default:
        {
            sendEventToOSRWidget(e);
        }
            break;
        }
    }

    return res;
}


void WidgetOSRItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
    QQuickPaintedItem::geometryChange(newGeometry, oldGeometry);

    if(mOSRWidget)
    {
        mOSRWidget->setGeometry(newGeometry.toRect());
    }
}

#else


bool WidgetOSRItem::eventFilter(QObject *obj, QEvent *e)
{
    QWindow* pw = (QWindow*)(window());
    bool res = QQuickPaintedItem::eventFilter(obj, e);
    if(obj == mOSRWidget)
    {
        switch(e->type())
        {
        case QEvent::Paint: //当OsrWidget paint的时候也触发自己paint
        {
            QPaintEvent* pe = (QPaintEvent*)e;
            this->update(pe->rect());
        }
            break;
        }
    }
    else if(obj == pw)
    {

        //* 如果是鼠标等(有鼠标坐标信息的事件。)的话我们得计算一下偏移量并修正一下,这里就只处理QMouseEvent和QWheelEvent作为示例
        //* 如果有其他类似的也需要修正,不然可能坐标偏移
        switch(e->type())
        {
        case QEvent::MouseButtonDblClick  :
        case QEvent::MouseButtonPress      :
        case QEvent::MouseButtonRelease      :
        case QEvent::MouseMove              :
        case QEvent::MouseTrackingChange  :
        case QEvent::Move                  :
        {
            QMouseEvent *me = (QMouseEvent*)e;
            QEvent::Type type = me->type();
            QPointF localPosF = me->localPos();
            Qt::MouseButton mouseButton = me->button();
            Qt::MouseButtons mouseButtons = me->buttons();
            Qt::KeyboardModifiers modifiers = me->modifiers();

            //修正一下localpos
            QPointF offsetF = mapToScene(QPoint(0,0));
            QPointF diffPosF = localPosF - offsetF;

            QMouseEvent tme(type, diffPosF, mouseButton, mouseButtons, modifiers);
            sendEventToOSRWidget(&tme);
        }
            break;
        case QEvent::Wheel:
        {
            QWheelEvent *we = (QWheelEvent*)e;
            QPointF localPosF = we->posF();
            QPointF gloabalPosF = we->globalPosF();
            QPoint  pixelDelta = we->pixelDelta();
            QPoint  angleDelta = we->angleDelta();
            int qt4Delta = we->delta();
            Qt::Orientation orientation = we->orientation();
            Qt::MouseButtons mouseButtons = we->buttons();
            Qt::KeyboardModifiers modifiers = we->modifiers();

            //修正一下localpos
            QPointF offsetF = mapToScene(QPoint(0,0));
            QPointF diffPosF = localPosF - offsetF;

            QWheelEvent twe(diffPosF, gloabalPosF, pixelDelta, angleDelta, qt4Delta, orientation, mouseButtons, modifiers);
            sendEventToOSRWidget(&twe);
        }
            break;
        default:
        {
            sendEventToOSRWidget(e);
        }
            break;
        }
    }

    return res;
}


void WidgetOSRItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
    QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry);

    if(mOSRWidget)
    {
        mOSRWidget->setGeometry(newGeometry.toRect());
    }
}

#endif

5 参考链接

uwerat/qskinny: A lightweight framework on top of the Qt scene graph and only few classes from Qt/Quick. It is usable from C++ and/or QML. (github.com)

Skycoder42/QtMvvm: A mvvm oriented library for Qt, to create Projects for Widgets and Quick in parallel (github.com)

(177条消息) qwidget嵌入qml最完整代码_qml嵌入widget,qml嵌入qwidget-C++文档类资源-CSDN文库

(177条消息) 如何获取指定objectName的QObject_qyvlik的博客-CSDN博客

pengguanjun/UseQtWidgetInQML: 在qt widget中使用qml很容易,那怎么在qml中使用qt widget控件呢 (github.com)

(175条消息) QWidget嵌入QML窗口中_Eosin_Sky的博客-CSDN博客_qwidget放到qml

(175条消息) 将Qt Widgets嵌入到QML界面中的一种示范_Eosin_Sky的博客-CSDN博客_widgetosritem

(176条消息) 在Qt中将QWindow或者QWidget嵌入到别的进程中的窗口中(windows)_Eosin_Sky的博客-CSDN博客_qwindow

相关文章
|
11天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
8天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2522 18
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
8天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1525 15
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
4天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
10天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
590 14
|
1月前
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19283 30
|
10天前
|
人工智能 自动驾驶 机器人
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
过去22个月,AI发展速度超过任何历史时期,但我们依然还处于AGI变革的早期。生成式AI最大的想象力,绝不是在手机屏幕上做一两个新的超级app,而是接管数字世界,改变物理世界。
491 49
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
|
1月前
|
人工智能 自然语言处理 搜索推荐
阿里云Elasticsearch AI搜索实践
本文介绍了阿里云 Elasticsearch 在AI 搜索方面的技术实践与探索。
18842 20
|
1月前
|
Rust Apache 对象存储
Apache Paimon V0.9最新进展
Apache Paimon V0.9 版本即将发布,此版本带来了多项新特性并解决了关键挑战。Paimon自2022年从Flink社区诞生以来迅速成长,已成为Apache顶级项目,并广泛应用于阿里集团内外的多家企业。
17530 13
Apache Paimon V0.9最新进展
|
2天前
|
云安全 存储 运维
叮咚!您有一份六大必做安全操作清单,请查收
云安全态势管理(CSPM)开启免费试用
367 4
叮咚!您有一份六大必做安全操作清单,请查收