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

相关文章
|
4月前
|
算法框架/工具 C++ Python
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
333 0
|
26天前
|
监控 安全 测试技术
在实施自动化和持续集成的过程中,如何确保代码的安全性和合规性
在自动化和持续集成中,确保代码安全与合规至关重要。措施包括集成自动化安全工具、执行自动化合规检查、进行代码质量与安全检测、评估开源代码安全、实施基础设施即代码的安全标准、采用多层防御策略、加强安全教育与文化建设、使用合规性检测工具及许可证合规分析等,共同提升代码安全性与合规水平。
|
1月前
|
监控 安全 测试技术
在实施自动化和持续集成的过程中,如何确保代码的安全性和合规性?
在实施自动化和持续集成的过程中,如何确保代码的安全性和合规性?
|
1月前
|
算法 安全 C++
提高C/C++代码的可读性
提高C/C++代码的可读性
54 4
|
2月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
357 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
2月前
|
安全 算法 Java
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
本文提供了在数据库中对密码等敏感信息进行加盐加密的详细教程,包括手写MD5加密算法和使用Spring Security的BCryptPasswordEncoder进行加密,并强调了使用BCryptPasswordEncoder时需要注意的Spring Security配置问题。
192 0
数据库信息/密码加盐加密 —— Java代码手写+集成两种方式,手把手教学!保证能用!
|
4月前
|
Java Devops 持续交付
探索Java中的Lambda表达式:简化代码,提升效率DevOps实践:持续集成与部署的自动化之路
【8月更文挑战第30天】本文深入探讨了Java 8中引入的Lambda表达式如何改变了我们编写和管理代码的方式。通过简化代码结构,提高开发效率,Lambda表达式已成为现代Java开发不可或缺的一部分。文章将通过实际例子展示Lambda表达式的强大功能和优雅用法。
|
3月前
|
C++
继续更新完善:C++ 结构体代码转MASM32代码
继续更新完善:C++ 结构体代码转MASM32代码
|
3月前
|
C++ Windows
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
|
3月前
|
C++
2合1,整合C++类(Class)代码转换为MASM32代码的平台
2合1,整合C++类(Class)代码转换为MASM32代码的平台
下一篇
DataWorks