Juliette Marquis
Juliette Marquis

Reputation: 199

QML : loading more than 50 components in a page

I am developing a Qt Quick application. My goal is to create a keyboard with pressed and released buttons on a qml page. This keyboard has 78 keys.

I wanted to represent a pressed key with the InnerShadow effect and a released key with the DropShadow effect : pressed keys released keys

Here is the qml code for the key button component :

Button {
    id: root
    text: "A"
    property color textColor: "#397f92"
    property color backgroundColor: "#f6f7fa"
    property color borderColor: "#397f92"
    property real fontSize: 14
    property int offset : 2;

    font.family: "Helvetica"
    font.pointSize: fontSize
    font.capitalization: Font.MixedCase

    contentItem: Text {
        anchors.centerIn: parent
        text: root.text
        color: textColor
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        font: root.font
    }

    background: Rectangle {
        implicitWidth: 55
        implicitHeight: 55
        color: backgroundColor
        border.color: borderColor
        radius: 10
        border.width: 2
    }

    DropShadow {
        visible: !root.down
        anchors.fill: parent
        horizontalOffset: 3
        verticalOffset: 3
        radius: 8.0
        samples: 17
        color: "#80000000"
        source: root.background
        cached: false
    }
    InnerShadow {
        visible: root.down
        anchors.fill: parent
        radius: 8.0
        samples: 16
        horizontalOffset: 6
        verticalOffset: 6
        color: "#b0000000"
        source: root.background
    }

The problem is that loading these 78 components takes time (more than 1sec). So I was looking for a better solution with a similar visual.

When using simple Rectangle components, the loading takes very little time (fast enough so that the user don't notice it).

When using two images (for pressed and released states), the loading also takes time (more than 1sec).

I've been looking for a solution on the documentation and on some forums but maybe I haven't looked for the correct thing. One of the hints I had was to find a way to load the components on the application once and for all when starting it instead of every time I load the page where this buttons are declared, but I haven't found how to do that.

Any help would be welcome to improve the performance of this design...

Here is the code for the use of images:

KeyButton.qml
    Button {
        id: root
        text: "A"
        property color textColor: "#397f92"
        property color backgroundColor: "#f6f7fa"
        property color borderColor: "#397f92"
        property real fontSize: 14
        property int offset : 2;

        font.family: "Helvetica"
        font.pointSize: fontSize
        font.capitalization: Font.MixedCase

       contentItem: Text {
            anchors.centerIn: parent
            text: root.text
            color: textColor
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
            font: root.font
        }

        background:Image {
            id: bkg_img
            source: root.down ? "qrc:/pressed.png" : "qrc:/release.png"
       }

Keypad.qml

Item {
    id: keypad_keys
    anchors.horizontalCenter: parent.horizontalCenter
    width: 57 * 14
    anchors.horizontalCenterOffset: - width / 20
    anchors.verticalCenter: parent.verticalCenter
    anchors.verticalCenterOffset: -150

    RowLayout {
        id: row_layout_0

        KeyButton {
            id: esc_key
            fontSize: 9
            text: "esc"
            Layout.preferredHeight: 30
            Layout.preferredWidth: 57
        }

        KeyButton {
            id: key_f1
            fontSize: 9
            text: "f1"
            Layout.preferredHeight: 30
            Layout.preferredWidth: 57
        }

        KeyButton {
            id: key_f2
            fontSize: 9
            text: "f2"
            Layout.preferredHeight: 30
            Layout.preferredWidth: 57
        }

       ...
   }
}

Upvotes: 0

Views: 843

Answers (2)

selbie
selbie

Reputation: 104494

While dtech is steering you in a good direciton, here are a handful of basic things you can do to improve application startup performance.

Measure loading performance with a retail production build, not a debug build.

Pre-compile your QML files. This can give a pretty significant speed-up in some cases.

If your keyboard isn't expected to appear right when the app starts up, pre-load the QML into a QQuickView when the app starts up. Then call "show" when it's actually needed. This of course just trades off the 1 second of loading time to make the application take longer to load. YMMV.

Use a Loader in your main qml file to load the actual contents. You can actually have the main qml file just have a Loader that hosts your actual QML content file along with a simple animated "spinner" control to show that the UX is loading up. This won't make your keyboard appear any faster, but your application won't seem stuck or hung.

Upvotes: 0

dtech
dtech

Reputation: 49289

Your buttons have high drawing complexity. The multisampling shadows are especially taxing.

You could bake the shadows as images, at least for the repeating key sizes, that would significantly improve performance.

Or you could use BorderImage to implement the all keys as composite flat raster graphics.

What you say about using images being slow as well doesn't really add up. Images should be fast, you are probably doing something wrong, but absent your actual code, it is not possible to tell what.

Additionally, you could bake the states of repeating key sizes as an image source and use a trivial shader to draw that cached result very efficiently, and only have the key caption dynamic.

Last but not least, you can scrap the shadows altogether, and use a combination of outline and fill color changes, scaling or linear gradients to achieve a depth effect that is not faked by shadows but by bevels. I mean after all, that inner shadow is not all that realistic in representing a pressed key, keys don't really sink like that.

The shadowy stuff has fallen out of trend many years ago. There are much more efficient ways to achieve a depth effect, here's a couple of basic examples:

  Row {
    spacing: 6
    Rectangle {
      width: 100
      height: 100
      radius: 10
      color: "lightgrey"
      border.color: "#666666"
      border.width: 2
      Rectangle {
        width: 90
        height: 90
        x: ma.pressed ? 3 : 6
        y: x
        radius: 10
        color: "darkgrey"
      }
      MouseArea {
        anchors.fill: parent
        id: ma
      }
    }
    Rectangle {
      width: 100
      height: 100
      radius: 10
      color: ma2.pressed ? "darkgrey" : "lightgrey"
      gradient: Gradient {
          GradientStop { position: ma2.pressed ? 0 : 1; color: "#666666" }
          GradientStop { position: ma2.pressed ? 1 : 0; color: ma2.pressed ? "#777777" : "#efefef" }
      }
      border.color: "#666666"
      border.width: 2
      Rectangle {
        width: 90
        height: 90
        anchors.centerIn: parent
        radius: 10
        scale: ma2.pressed ? .98 : 1
        color: "lightgrey"
      }
      MouseArea {
        anchors.fill: parent
        id: ma2
      }
    }
  }

enter image description here

Upvotes: 1

Related Questions