1
votes

I have TabView in main.qml

TabView {
    id: tabRoot
    objectName: "tabRootObj"
}

My application creates new tab on every new incoming TCP connection. Following code create new tabs on demand (It based on this answer https://stackoverflow.com/a/27093137/3960195).

void addTab(QQmlApplicationEngine& engine) {
    root_tab = engine.rootObjects().first()->findChild<QQuickItem*>(QStringLiteral("tabRootObj"));

    QVariant new_tab;
    QQmlComponent component(&engine, QUrl("qrc:/MyTab.qml");
    QMetaObject::invokeMethod(root_tab, "addTab",
        Q_RETURN_ARG(QVariant, new_tab),
        Q_ARG(QVariant, QStringLiteral("Tab name")),
        Q_ARG(QVariant, QVariant::fromValue(&component)));
}

With every TCP connection It also creates new instance of class ConnectionManager which contains some statistics (eg count of transferred bytes) accessible through properties.

//ConnectionManager.hpp
class ConnectionManager : public QObject
{
    Q_OBJECT
public:
    // ...
    Q_PROPERTY(QString address READ address NOTIFY ipChanged)
    Q_PROPERTY(int received READ received NOTIFY receivedChanged)
    //...
}

//MyTab.qml
Item {
    property string ip
    property int received
    ...
}

What I need is bind those properties with properties inside MyTab.qml. The problem is that the method TabView.addTab creates component of MyTab on it's own and I cannot inject concrete instance of ConnectionManager to its context. Also ConnectionManager doesn't exist until new connection is created so I cannot add it on the start of application through rootContext. How can I create bindings between this newly created objects?

It's my first project in QML so maybe there is a better "QML" way how to do it. In that case answer with showing the "right" way is also acceptable.

1
Can't you pass the ConnectionManager to the QML addTab function ? Also to limit the coupling between C++ and QML, I wouldn't add tabs from c++. I would expose a c++ model of the different connections and expose it to QML. Then I would use a Repeater with MyTab as a delegate. - GrecKo
QML addTab function has only two parameters which are title of the tab and constructed component (doc.qt.io/qt-5/qml-qtquick-controls-tabview.html#addTab-method). The repeater solution looks really great. I didn't know that you can use Repeater inside TabView. - Qeek
GrecKo was probably talking about your own C++- function: addTab(...), where you create the component. - derM
@GrecKo I can add this into my addTab but I still need somehow set to newly created tab associated ConnectionManager - Qeek

1 Answers

0
votes

As the GrecKo recommended in the comment I've used the Repeater inside TabView and defined for it a model.

The TabView with Repeater looks like this:

TabView {
    id: tabRoot
    Repeater {
        model: ConnectionModel
        delegate: Tab {
            title: connection.name
            Client {
                address connection.address
                received: connection.received
            }
        }
    }
}

How to create a model can be easily found in the Qt's documentation. Let's say I have a model class named ConnectionModel (which inherits from QAbstractListModel) and it's contains a role connection (which is used for referencing an instance of ConnectionManager in the Repeater's delegate). I've used the QQmlApplicationEngine.rootContext()->setContextProperty() method for connecting the Repeater with my model instance. The example below shows how to make the connection:

QApplication app(argc, argv);
QQmlApplicationEngine engine;
// Create instace of ConnectionModel
ConnectionModel model;
// Assasign the model instance to the QML context property "ConnectionModel"
engine.rootContext()->setContextProperty("ConnectionModel", &model);
// ...
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

return app.exec();

With every new TCP connection the program creates a new instance of ConnectionManager. After that it will call the method for inserting new records into the model and insert the reference of the new ConnectionManager.

I hope it helps to someone who has to create a dynamic tabs in QML.