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

时间:2022-11-27 12:54:47


目录

​​1  QML代码生成​​

​​2  注册机制的含义​​

​​3   QWidgetInQml  QML里面集成widget​​

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

​​5  参考链接​​


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++文档类资源-

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

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

​​(175条消息) QWidget嵌入QML窗口中_Eosin_Sky的博客-

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

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