首页 文章

C / QML:如何为动态创建的组件定义和处理多个上下文?

提问于
浏览
4

基本上我的情况是这样的:

我有一个扩展QQuickView的类,它通过设置上下文属性将某些对象从C暴露给QML . 显示的视图是从QML创建的,并且是同一个自定义组件的所有不同的结构;某些事件发生时会创建新视图,当发生这种情况时,现有视图应显示最初在C端分配给它们的对象,而新视图应显示分配给它们的内容 .

所以,在C方面,我有这样的事情:

WindowManager::WindowManager(QQuickView *parent) :
QQuickView(parent)
{
      // Setting the source file to use
      this->setSource(QUrl("qrc:/qml/main.qml"));

      // Exposing the istance of this class to QML for later use
      this->rootContext()->setContextProperty("qquickView", this);

      // Calling the method that will create dynamically a new view that will be child of main.qml; the parameter is not important, just a random number to start with
      this->prepareNewView(3)

      this->showFullScreen();
}

WindowManager::prepareNewView(int stuffId)
{
      MyDatabase db;

      // Getting something to show in QML from somewhere based on the parameter received
      SomeStuff stuff = db.getStuff(stuffId)

      // Exposing the object I need to show in QML
      this->rootContext()->setContextProperty("someStuff", stuff);



      QObject *object = this->rootObject();

      // Here I'm invoking a function from main.qml that will add a new view dynamically
      QMetaObject::invokeMethod(object, "addView");
}

现在,在QML方面,我有一个这样的主文件:

// main.qml
Rectangle {
    id: mainWindow
    width: 1000
    height: 1000

    // This function adds a component to mainWindow
    function addView()
    {
        // Creating the component from my custom made component
        var component = Qt.createComponent("MyComponent.qml");

        // Creating an istance of that component as a child of mainWindow
        var newView = component.createObject(mainWindow);


        // ... Now I would be doing something with this new view, like connecting signals to slots and such
    }
}

然后我有了自定义组件,这是将动态创建的视图:

// MyComponent.qml
Rectangle {
    id: customComponent

    // Here I would be using the object I exposed from the C++ side
    x: someStuff.x
    y: someStuff.y
    width: someStuff.width
    height: someStuff.height

    // Here I'm creating a MouseArea so that clicking this component will cause the creation of another view, that will have to show diffrent things since the parameter I'm passing should be different from the starting parameter passed in the constructor of WindowManager
    MouseArea {
        anchors.fill: parent
        onClicked: qquickView.prepareNewView(Math.random())
    }
}

现在,随着它的一切,它首先将显示id为3的“东西”,它被公开为主要上下文的上下文属性 .

但是,如果我单击MouseArea,假设将传递3以外的id,将公开具有相同名称的新上下文属性,从而导致覆盖旧属性 . 这意味着第一个视图现在将显示刚刚暴露的“东西”,而不是基于stuffId的“东西”等于3,而我需要的是第一个继续显示它应该显示的视图(“东西” “id = 3),以及任何其他视图后来会出现与其id相对应的内容 .

发生这种情况是因为我在上下文中定义了每个组件共有的属性,而我应该定义一个只能由动态创建的组件的新等特性可见的属性 . 但是我该怎么做?

在文档中,我读到可以直接从C创建一个组件并定义它应该使用的上下文...像这样(从here获取的代码片段):

QQmlEngine engine;
QStringListModel modelData;
QQmlContext *context = new QQmlContext(engine.rootContext());
context->setContextProperty("myModel", &modelData);

QQmlComponent component(&engine);
component.setData("import QtQuick 2.0\nListView { model: myModel }", QUrl());
QObject *window = component.create(context);

我认为这对我打算做的事情有用 . 每当我从C创建一个新视图(由单击MouseArea引起)时,我创建一个新的上下文,其中包含“someStuff”作为其属性,这样每个视图都有自己的“东西”......但是我需要访问来自QML的新创建的视图,因为在我在main.qml中的addView()函数中创建它后,我访问视图以便做某些细分(不重要的是什么),如果我从CI创建组件的istance不知道如何从QML访问它...有没有办法将组件从C传递到QML才能访问它?

我没有关于如何解决这个问题的想法或找到另一种方法来动态创建具有自定义内容的视图同时可见...任何建议都表示赞赏 .

2 回答

  • 5

    我实际上发现直接将用C创建的组件传递给QML是可能的(并且很容易) .

    所以现在,我修改代码非常像这样:

    WindowManager::prepareNewView(int stuffId)
    {
        MyDatabase db;
    
        // Getting something to show in QML from somewhere based on the parameter received
        SomeStuff stuff = db.getStuff(stuffId)
    
    
        // Creating the new context, based on the global one
        QQmlContext *context = new QQmlContext(this->rootContext());
    
    
        // Exposing the object I need to show in QML to the new context
        context ->setContextProperty("someStuff", stuff);
    
        // Creating the component
        QQmlComponent component(this->engine(), QUrl("qrc:/qml/MyComponent.qml"));
    
        // Creating the istance of the new component using the new context
        QQuickItem *newView = qobject_cast<QQuickItem*>(component.create(context));
    
    
        // Getting the root component (the Rectangle with it mainWindow)
        QObject *object = this->rootObject();
    
        // Manually setting the new component as a child of mainWIndow
        newView->setParentItem(qobject_cast<QQuickItem*>(object));
    
        // Invoking the QML that will connect the events of the new window, while passing the component created above as QVariant
        QMetaObject::invokeMethod(object, "addView", Q_ARG(QVariant, QVariant::fromValue(newView)));
     }
    

    在QML中,main.qml中的函数现在是这样的:

    // Function called from C++; the param "newView" is the last component added
    function addView(newView)
    {
        // ... Here I would use the new view to connect signals to slots and such as if I created "newView" directly in QML
    }
    

    所以我设法不要过多地改变代码 .

  • 2

    我认为您可以通过将对象设置为上下文属性来传递组件实例(QObject),就像在代码中一样 .

    class ViewInstance : public QObject
    {
    Q_OBJECT
        public:
        Q_INVOKABLE QObject* getCurrentViewInstance() {
            ...
            QObject *window = component.create(context);
            return window;
        }
    };
    
    int main(int argc, char *argv[]) {
        ...
        QQuickView view;
        ViewInstance data;
        view.rootContext()->setContextProperty("viewInstance", &data);
    }
    

    然后,在qml中,您可以通过调用viewInstance.getCurrentViewInstance()来获取组件实例 . 希望这可以帮助 .

相关问题