Components

A component is a reusable element. QML provides different ways to create components. Currently, we will look only at the simplest form - a file-based component. A file-based component is created by placing a QML element in a file and giving the file an element name (e.g. Button.qml). You can use the component like every other element from the Qt Quick module. In our case, you would use this in your code as Button { ... }.

For example, let’s create a rectangle containing a text component and a mouse area. This resembles a simple button and doesn’t need to be more complicated for our purposes.

  1. Rectangle { // our inlined button ui
  2. id: button
  3. x: 12; y: 12
  4. width: 116; height: 26
  5. color: "lightsteelblue"
  6. border.color: "slategrey"
  7. Text {
  8. anchors.centerIn: parent
  9. text: "Start"
  10. }
  11. MouseArea {
  12. anchors.fill: parent
  13. onClicked: {
  14. status.text = "Button clicked!"
  15. }
  16. }
  17. }
  18. Text { // text changes when button was clicked
  19. id: status
  20. x: 12; y: 76
  21. width: 116; height: 26
  22. text: "waiting ..."
  23. horizontalAlignment: Text.AlignHCenter
  24. }

The UI will look similar to this. In the first image, the UI is in its initial state, and in the second image the button has been clicked.

Components - 图1

Components - 图2

Now our task is to extract the button UI into a reusable component. For this, we should think about a possible API for our button. You can do this by imagining how someone else should use your button. Here’s what I came up with:

  1. // minimal API for a button
  2. Button {
  3. text: "Click Me"
  4. onClicked: { /* do something */ }
  5. }

I would like to set the text using a text property and to implement my own click handler. Also, I would expect the button to have a sensible initial size, which I can overwrite (e.g. with width: 240 for example).

To achieve this we create a Button.qml file and copy our button UI inside. Additionally, we need to export the properties a user might want to change at the root level.

  1. // Button.qml
  2. import QtQuick
  3. Rectangle {
  4. id: root
  5. // export button properties
  6. property alias text: label.text
  7. signal clicked
  8. width: 116; height: 26
  9. color: "lightsteelblue"
  10. border.color: "slategrey"
  11. Text {
  12. id: label
  13. anchors.centerIn: parent
  14. text: "Start"
  15. }
  16. MouseArea {
  17. anchors.fill: parent
  18. onClicked: {
  19. root.clicked()
  20. }
  21. }
  22. }

We have exported the text property and the clicked signal at the root level. Typically we name our root element root to make referencing it easier. We use the alias feature of QML, which is a way to export properties inside nested QML elements to the root level and make this available for the outside world. It is important to know that only the root level properties can be accessed from outside this file by other components.

To use our new Button element we can simply declare it in our file. So the earlier example will become a little bit simplified.

  1. Button { // our Button component
  2. id: button
  3. x: 12; y: 12
  4. text: "Start"
  5. onClicked: {
  6. status.text = "Button clicked!"
  7. }
  8. }
  9. Text { // text changes when button was clicked
  10. id: status
  11. x: 12; y: 76
  12. width: 116; height: 26
  13. text: "waiting ..."
  14. horizontalAlignment: Text.AlignHCenter
  15. }

Now you can use as many buttons as you like in your UI by just using Button { ... }. A real button could be more complex, e.g providing feedback when clicked or showing a nicer decoration.

TIP

If you want to, you could even go a step further and use an Item as a root element. This prevents users from changing the color of the button we designed, and provides us with more control over the exported API. The target should be to export a minimal API. Practically, this means we would need to replace the root Rectangle with an Item and make the rectangle a nested element in the root item.

  1. Item {
  2. id: root
  3. width: 116; height: 26
  4. property alias text: label.text
  5. signal clicked
  6. Rectangle {
  7. anchors.fill parent
  8. color: "lightsteelblue"
  9. border.color: "slategrey"
  10. }
  11. ...
  12. }

With this technique, it is easy to create a whole series of reusable components.