QML の一部を別の QML で差し替える方法

この記事は、Qt Advent Calendar 2019 17日目の記事です。

はじめに

デザイン由来の QML ツリーがあり、プログラミング的に直接そこはいじりたくない場合が最近よくあります。

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    id: window
    ....
    Design {
        id: design        
    }
}

ここで、Design エレメントは Photoshop や Sketch、エクセルなどで定義された UI の情報を QML になんらかの手段で(自動的に)変換したものだと思ってください。

Design の中では各要素が alias されていて、外部からアクセスはできるように作られています。

Design およびその中身はデザインの更新に伴い定期的に自動的にアップデートされます。

Design の中身はほぼ Text と Image でべたべたに記述されていますが、マルっと差し替えたいものは独自コンポーネントにする場合もあります(後述)。

中身の一部を独自のエレメントで上書きする

design の中の目的のエレメントを親に指定して新しい QML を作成する方法です。

Component.onCompleted: {
    var component = Qt.createComponent('CustomElement.qml')
    component.createObject(design.dummy1)
}

dummy1 上に CustomElement を表示します。dummy1 自体は残り、表示され、操作も可能です。

中身の一部の描画を独自のエレメントで上書きする

Design {
    id: design
    dummy2.layer.enabled: true
    dummy2.layer.effect: CustomElement {
        // property bindings
    }
}

Item のレイヤー機能利用し、独自のエレメントで描画を上書きする方法です。

もちろん、元のデザインに対して(スクロールループなどの)エフェクトをかけたい場合もこの方法を採用します。

コンポーネント単位で差し替える方法

上記2つは割とライトな差し替えを想定していましたが、動画の再生 UI など少々複雑なデザインを差し替えたい場合もあります。

その場合は、QQmlFileSelector の機能を利用することで、ロードする QML ファイルを別のパスにあるもので差し替えることが可能です。

例えば、Linux でしか利用しないアプリケーションであれば、

  • design.qrc … こちらはデザイナーが変えるファイル群
    • /Design.ui.qml
    • /VideoPlayer.ui.qml
  • program.qrc … こちらはプログラマーが変えるファイル群
    • /+unix/VideoPlayer.ui.qml

のような感じでプラットフォーム固有の名前のディレクトリを1つ作り、同名のファイルをおいておくことで、実行時にそちらが優先的にロードされます。

デザイン側で用意されたアセットを利用しつつ、プログラマーがコードを書いた QML にごっそり差し替えることが可能です。

また、様々なプラットフォームで動かす際には、main.cpp 側に以下の記載をしておくと、

QQmlApplicationEngine engine;
QQmlFileSelector selector(&engine);
selector.setExtraSelectors({"programmer"});

プラットフォームによらず /+programmer/ 以下のファイルで実行時に差し替えることが可能になります。

終わりに

こういうずる賢い設計を取り入れると、お前自分で全然コード書いてないじゃないか!となる可能性もあるので気をつけましょう。