簡體   English   中英

C ++ / QML:如何為動態創建的組件定義和處理多個上下文?

[英]C++/QML: How to define and handle multiple contexts for dynamically created components?

基本上我的情況是這樣的:

我有一個擴展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 ++創建一個組件並定義它應該使用的上下文......就像這樣(從這里獲取的代碼片段):

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 ++創建一個新視圖(由鼠標區域上的點擊引起)時,我創建一個以“someStuff”為屬性的新上下文,這樣每個視圖都有自己的“東西”......但是我需要訪問來自QML的新創建的視圖,因為我在main.qml中的addView()函數中創建它之后我訪問視圖以便做某些細分(不重要的是什么),如果我從C ++創建組件的istance我不知道如何從QML訪問它...有沒有辦法將組件從C ++傳遞到QML才能訪問它?

我沒有關於如何解決這個問題的想法或找到另一種方法來動態創建具有自定義內容的視圖同時可見...任何建議都表示贊賞。

我實際上發現可以(並且很容易)將用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
}

所以我設法不要過多地改變代碼。

我認為您可以通過將對象設置為上下文屬性來傳遞組件實例(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()來獲取組件實例。 希望這可以幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM