2012-09-18 4 views
22

Ich mag QML wirklich. Ich mag es, wie ich Komponenten (vergleichbar mit Klassen) und ihre Eigenschaften definieren und von irgendwoher instanziieren kann (vergleichbar mit Objekten).Wie definiert man eine "Vorlage" mit untergeordneten Platzhaltern in QML?

Ich kann definieren, sagen wir mal, eine Schaltfläche, einige Look-and-Feel, und einen Label-Text darauf. Dies könnte zum Beispiel dieser Komponente Definition (Button.qml):

Item { 
    id: button 
    property string label 

    anchors.fill: parent 

    Rectangle { 
     anchors.fill: parent 
     radius: 10 
     color: "gray" 

     Text { 
      anchors.centerIn: parent 
      font.pixelSize: 20 
      text: button.label 
      color: "white" 
     } 
    } 
} 

und instanziiert in dieser Hauptdatei (main.qml):

Rectangle { 
    width: 300 
    height: 200 

    Button { 
     anchors.centerIn: parent 
     anchors.margins: 50 
     label: "Hello button!" 
    } 
} 

Aber ich sehe die folgende Einschränkung: Ich kann nur Definieren Sie eine Schaltflächenvorlage mit einigen Eigenschaften, nicht mit einigen Platzhalter. Alle untergeordneten Objekte, die in der Instanz definiert sind, sind mindestens standardmäßig untergeordnete Elemente, und ich möchte dieses Verhalten ändern.

Nehmen wir an, ich möchte ein Element (sagen wir ein Bild, aber ich will nicht die Definition von Button, dass es ein Bild sein wird) in der Schaltfläche platzieren. Ich stelle mir so etwas vor:

Item { 
    id: button 
    property Item contents <-- the client can set the placeholder content here 

    anchors.fill: parent 

    Rectangle { 
     anchors.fill: parent 
     radius: 10 
     color: "gray" 

     Item { 
      id: placeholder  <-- where the placeholder should be inserted 
     } 
    } 

    Component.onCompleted: { 
     // move the contents into the placeholder... 
    } 
} 

Wie kann ich das erreichen? Ich weiß nicht, ob Component.onCompleted der richtige Weg ist. Beachten Sie jedoch, dass sich in meinem Fall die Inhalte nie ändern werden (zumindest in meinem aktuellen Design der Anwendung ...).

Auch möchte ich Verankerung innerhalb des Platzhalters arbeiten. Zum Beispiel, wenn ich den Inhalt als ein Text-Element definiere, das in seinem Elternteil zentriert ist (welches zuerst die Vorlage selbst sein wird). Dann verschiebt mein Code diese Textinstanz in den Platzhalter und die übergeordneten Anker sollten dann diejenigen des Platzhalterelements sein, nicht das Vorlagenelement.

Antwort

30

Ich fand eine viel schönere Antwort auf diese Frage, vorgeschlagen in einer Präsentation der Qt Developer Days 2011 "Qt Quick Best Practices and Design Patterns".

Sie verwenden default property alias ... zu alias die untergeordneten Elemente zu jeder Eigenschaft eines beliebigen Elements. Wenn Sie die untergeordneten Elemente nicht aliasieren möchten, aber der Alias-Eigenschaft einen Namen geben möchten, entfernen Sie einfach default. (Wörtliche Kinder sind pro QML Definition der Wert der Standardeigenschaft.)

Item { 
    id: button 
    default property alias contents: placeholder.children 

    anchors.fill: parent 

    Rectangle { 
     anchors.fill: parent 
     radius: 10 
     color: "gray" 

     Item { 
      id: placeholder  <-- where the placeholder should be inserted 
     } 
    } 
} 
+0

BTW, wenn Sie noch Fragen über das QML-Scope-Puzzle aus dem Gespräch sind, finden Sie unter: http://StackOverflow.com/Questions/11339719/qml-Component-Scope-Puzzle – mlvljr

+1

Fantastisch, danke für diesen Link!;) Übrigens kann der "d-pointer" (auch in diesem Vortrag) verwendet werden, um private Mitglieder (auch JS-Funktionen) besser zu verstecken. Ich finde es besser als die doppelte Unterstreichung. Ich nenne es sogar "priv", um sicherzustellen, dass niemand versehentlich auf Mitglieder dieses Kindobjekts zugreift. – leemes

+0

Interessant, erinnere mich nicht daran (d-ptr), das ist also eine Art QML/js pimpl? – mlvljr

3

Eigentlich ist die richtige Antwort von dem, was ich gehört habe, eine QML Loader zu verwenden, um zu erreichen, was Sie wollen.

[das wird gesagt; Ich habe es noch nicht wirklich versucht, aber es ist auf meiner Terminkalender-Liste und sieht ziemlich geradlinig]

Suchen Sie auch stackoverflow für andere "QML Loader" Fragen, da es eine Nummer gibt, die Ihnen den Einstieg erleichtern wird.

+0

Danke, wusste nicht, was der QML Loader ist und dass es in solchen Situationen hilft. Ich werde es mir ansehen. – leemes

+0

Wie die Antwort von Misch, funktioniert Ihre Lösung nicht mit der Verankerung. Beispiel: 'Loader {sourceComponent: Rectangle {color:" red "; anchors.fill: parent}} '. Das Rechteck sollte das Parent des Loaders füllen, tut es aber nicht. (Es ist unsichtbar, wenn ich nicht ausdrücklich eine Breite und Höhe festlege.) – leemes

+0

Hoppla, ich habe vergessen, dem Loader zu sagen, dass er seine Eltern füllen soll. Jetzt funktioniert es, glaube ich ... – leemes

1

Sie können den Artikel bewegen (s) mit diesem Stück Code (wenn Sie mehrere Elemente innerhalb der Platzhalter unterstützen möchten):

property list<Item> contents 

Component.onCompleted: { 
    var contentItems = []; 
    for(var i = 0; i < contents.length; ++i) 
     contentItems.push(contents[i]); 
    placeholder.children = contentItems; 
} 

Beachten Sie, dass Sie nicht über eine Liste von Elementen zu schaffen Die Eigenschaft contents wird als einzelne Werte von der QML-Engine auch für Listeneigenschaften akzeptiert.

+0

Ja, das scheint das richtige Code-Snippet zu sein, um die Items zu verschieben. Es funktioniert, aber es respektiert nicht die Verankerung. (Wenn ich zum Beispiel einen Text mit 'anchors.centerIn: parent 'zur Verfügung stelle, sieht das Ergebnis so aus, als hätte ich die anchors -Eigenschaft nicht gesetzt.) – leemes

+0

Ich weiß nicht, wie ich die Ankereigenschaft (vielleicht die Inhaltselemente)" aktualisieren "soll denken Sie immer noch, dass ihr Elternteil die Vorlage ist?), aber Sie können die Größe ändern, um den Inhaltsbereich zu füllen. Legen Sie ihre Breite und Höhe auf die Größe des Platzhalters fest. – Misch

1

Kurz (auf die Idee zeigen):

import QtQuick 1.1 

Item { 
    width: 200 
    height: 100 

    //<-- the client can set the placeholder content here 
    property Item contents: Rectangle { 
     anchors.fill: parent 
     anchors.margins: 25 
     color: "red" 
    } 

    Rectangle { 
     id: container 

     anchors.fill: parent 
     radius: 10 
     color: "gray" 

     //<-- where the placeholder should be inserted 
    } 

    Component.onCompleted: { 
     contents.parent = container 
    } 
} 

Etwas längere Version (mit Unterstützung Inhalt Neuzuordnung):

import QtQuick 1.1 

Item { 
    width: 200 
    height: 100 

    //<-- the client can set the placeholder content here 
    property Item contents: Rectangle { 
     //anchors can be "presupplied", or set within the insertion code 
     //anchors.fill: parent 
     //anchors.margins: 25 
     color: "red" 
    } 

    Rectangle { 
     id: container 

     anchors.fill: parent 
     radius: 10 
     color: "gray" 

     //<-- where the placeholder should be inserted 
     //Item { 
     // id: placeholder 
     //} 
    } 

    //"__" means private by QML convention 
    function __insertContents() { 
     // move the contents into the placeholder... 
     contents.parent = container 
     contents.anchors.fill = container 
     contents.anchors.margins = 25 
    } 

    onContentsChanged: { 
     if (contents !== null) 
      __insertContents() 
    } 

    Component.onCompleted: { 
     __insertContents() 
    } 
} 

this helps :)

+0

Wenn Sie Fragen oder zusätzliche Anforderungen haben, zögern Sie nicht zu fragen (oder Änderungen anfordern) :) – mlvljr

+2

Danke für diese Lösung, aber ich fügte eine andere Antwort von mir selbst zitiert etwas sehr schönes aus einer Qt Developer Days Präsentation, die ich vor ein paar Tagen gefunden. – leemes

+0

Ja, das ist ein weiterer Trick, aber hüte dich davor, dass du 1) die Elemente statisch hinzufügst (außer du manipulierst die children-Eigenschaft direkt, was den ursprünglichen Zweck des Aliasing vereitelt) und 2) (eine Nebensache) Das Erben von QtObject (wie Timer) kann nicht auf diese Weise hinzugefügt werden (da Kinder eine Liste von Items sind, nicht einfache QtObjects). Mithilfe des Reparent-Ansatzes können Sie außerdem hinzugefügte Elemente beliebig positionieren (indem Sie sie jeweils in einen eigenen Container setzen usw.). – mlvljr

4

Necro Anrufbeantworter, falls jemand anderes hier am Ende wie ich.

In Qt5 wurde irgendwo in der Zeile die Standardeigenschaft "data" und nicht "Kinder". Dies ermöglicht das Hinzufügen anderer Objekttypen als "Objekt". z.B. Verbindungen können ebenfalls hinzugefügt werden (meine eigene Frage oben antworten)

So in QT5 sollten Sie tun:

Item { 
    id: button 
    default property alias contents: placeholder.data 

    anchors.fill: parent 

    Rectangle { 
     anchors.fill: parent 
     radius: 10 
     color: "gray" 

     Item { 
      id: placeholder  <-- where the placeholder should be inserted 
     } 
    } 
} 

Notiere die: placeholder.data statt placeholder.children Bitte beachten Sie auch, dass Sie nicht haben Verwenden Sie den Aliasnamen contents - dies kann alles sein, was Sie möchten. Ein Beispiel:

+0

https://stackoverflow.com/a/10031214/11722 hat ein Beispiel, das auch den Zusatz zum Platzhalter zeigt. – Zitrax

Verwandte Themen