好吧,看标题有点绕口。本文要讲的也不是qtquick的插件的内容。不过也不复杂,就一层窗户纸。可能太简单了,反而很少这方面的资料。
现实中经常需要根据需要(譬如不同的配置文件),装载不同的dll,然后将dll中的qml显示在UI上。譬如说,一个QGuiApplication应用appA作为启动程序,还有依照配置文件需要调用的动态链接库dllB.当然,也可以通过更改配置文件装载不同的dllC,dllD等。
关于appA,vs2019+qt扩展,新建的工程大概就是这样的:
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/qt/qml/qwork/main.qml")));
....
main.qml里面定义个Rectangle:
Rectangle
{
objectName: "rect_main"
anchors.fill:parent
}
dllB里面建立了一个page_start.qml.当然,以后也许会加载其他的什么dll里面的qml。但是不希望每次都修改appA了。希望把page_start.qml添加到rect_main里面。怎么做呢?大概的思路基于以下几点:
1 QLibrary提供了动态调用dll的方法;
2 C++要操作两个UI控件,直接的想法无非就是先建立两个控件的实体,然后获得两者的指针。对于已有的实体,通过findChild找到,对于需要新建的,通过QQmlComponent的create方法,当然,别忘了把engine的指针也传过去供QQmlComponent使用。
3 qml本质上就是个脚本,通过QQmlComponent的create或者其他的一些方法建立一个与之对应的QQuickItem的实体,这个QQuickItem的实体本质可以理解为发芽自engine的树状结构。通过setParentItem告诉当前的树枝或者树叶,它的上一级是哪一号,这样就可以把它拼接到这棵树上。
4 只要接口规则不变,就可以随意替换。
建立一个类,最简单的如下:
class c_interface: public QObject
{
public:
Q_INVOKABLE virtual bool addQmltoParent(QObject* p_parent, QQmlApplicationEngine* p_engine) = 0;
}
在dllB中,定义一个class dllPage继承自c_interface。当然,后续我们也可以建立其他的dll,只要建立一个继承自c_interface的类。
于是
class Q_DECL_EXPORT dllPage : public c_interface
{
public:
explicit dllPage(QObject* parent = nullptr);
~dllPage();
Q_INVOKABLE bool addQmltoParent(QObject* p_parent, QQmlApplicationEngine* p_engine) override;
private:
QQmlComponent* m_qml_component;
QQuickItem* m_qml_item;
}
Q_INVOKABLE bool dllPage::addQmltoParent(QObject* p_parent, QQmlApplicationEngine* p_engine)
{
m_qml_component = new QQmlComponent(p_engine, QUrl::fromLocalFile("page_start.qml"));
m_qml_item = qobject_cast<QQuickItem*>(m_qml_component->create());
if(m_qml_item != nullptr)
{
m_qml_item->setProperty("anchors.fill", "parent");
m_qml_item->setParentItem((QQuickItem*)p_parent);
return true;
}
return false;
}
不过这还没完,你还得定义一个函数供QLibrary去resolve:
extern "C"
{
Q_DECL_EXPORT c_interface* createNewInstance();
}
于是,在appA里面,你就可以这样:
typedef c_interface* (*createNewInstanceFunc)(void);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/qt/qml/qwork/main.qml")));
QObjectList obj_lst = engine.rootObjects();
if (obj_lst.isEmpty())
return -1;
QLibrary lib("dllB.dll");
if(lib.load())
{
auto main = obj_lst[0]->findChild<QObject*>("rect_main");
if(main != nullptr)
{
createNewInstanceFunc func = (createNewInstanceFunc)lib.resolve("createNewInstance");
if (func != nullptr)
{
c_interface* ins = func();
if (ins != nullptr)
{
ins->addQmltoParent(main, &engine);
}
}
}
}