シグナルとハンドラのイベントシステム
アプリケーションとユーザーインターフェースのコンポーネントは、互いに通信する必要がある。例えば、ボタンはユーザーがクリックしたことを知る必要がある。ボタンはその状態を示すために色を変えたり、何らかのロジックを実行したりします。同様に、アプリケーションはユーザーがボタンをクリックしたかどうかを知る必要があります。アプリケーションは、このクリック・イベントを他のアプリケーションに中継する必要があるかもしれません。
QMLにはシグナルとハンドラのメカニズムがあり、シグナルはイベントであり、シグナルはシグナルハンドラを通して応答されます。シグナルが発生すると、対応するシグナルハンドラが呼び出されます。ハンドラにスクリプトなどのロジックを記述することで、コンポーネントはイベントに応答することができます。
シグナルハンドラでシグナルを受け取る
特定のオブジェクトに対して特定のシグナルが発せられたときに通知を受け取るには、オブジェクト定義でon<Signal>というシグナルハンドラを宣言します。シグナルハンドラには、シグナルハンドラが呼び出されたときに実行されるJavaScriptコードを記述する。
例えば、Qt Quick ControlsモジュールのButton タイプにはclicked
シグナルがあります。この場合、このシグナルを受信するためのシグナル・ハンドラはonClicked
になります。以下の例では、ボタンがクリックされるたびに、onClicked
ハンドラが呼び出され、親のRectangle にランダムな色が適用されます:
import QtQuick import QtQuick.Controls Rectangle { id: rect width: 250; height: 250 Button { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter text: "Change color!" onClicked: { rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1); } } }
注意: シグナル・ハンドラはJavaScriptの関数に似ていますが、直接呼び出すべきではありません。シグナルハンドラと他の機能の間でコードを共有する必要がある場合は、別の関数にリファクタリングしてください。そうでなければ、シグナルハンドラを呼び出したい場合は、常にシグナルを発するようにしてください。同じシグナルに対して、異なるスコープで複数のハンドラが存在することがあります。
プロパティの変更シグナルハンドラ
QMLのプロパティの値が変更されると、自動的にシグナルが発せられます。この種のシグナルはプロパティ変更シグナルと呼ばれ、シグナルハンドラは on<Property>Changedという形式で記述されます。
例えば、MouseArea タイプには、pressed プロパティがあります。このプロパティが変更されるたびに通知を受け取るには、onPressedChanged
という名前のシグナル・ハンドラを記述します:
import QtQuick Rectangle { id: rect width: 100; height: 100 TapHandler { onPressedChanged: console.log("taphandler pressed?", pressed) } }
TapHandler のドキュメントにはonPressedChanged
という名前のシグナル・ハンドラは記述されていませんが、pressed
プロパティが存在することによって、シグナルは暗黙のうちに提供されます。
シグナル・パラメータ
シグナルはパラメータを持つことがあります。これらにアクセスするには、ハンドラに関数を割り当てる必要があります。アロー関数も無名関数も機能する。
以下の例では、errorOccurred シグナルを持つ Status コンポーネントを考えてみましょう (QML コンポーネントにシグナルを追加する方法については、カスタム QML タイプへのシグナルの追加を参照してください)。
// Status.qml import QtQuick Item { id: myitem signal errorOccurred(message: string, line: int, column: int) }
Status { onErrorOccurred: (mgs, line, col) => console.log(`${line}:${col}: ${msg}`) }
注意: 関数の正式なパラメータ名は、シグナルのパラメータ名と一致している必要はありません。
すべてのパラメータを処理する必要がない場合は、末尾のパラメータを省略することができる:
Status { onErrorOccurred: message => console.log(message) }
しかし、読者に重要でないことを示すために、何らかのプレースホルダー名を使用することができます:
Status { onErrorOccurred: (_, _, col) => console.log(`Error happened at column ${col}`) }
注: 関数を使用する代わりに、プレーンなコードブロックを使用することも可能ですが、お勧めしません。その場合、すべてのシグナル・パラメータはブロックのスコープにインジェクションされます。しかし、この場合、パラメータがどこから来るのかが不明確になるため、コードが読みづらくなり、QMLエンジンのルックアップが遅くなります。また、QMLエンジンでパラメータを検索する際に時間がかかります。このようなパラメータのインジェクションは非推奨であり、実際にパラメータが使用された場合には実行時に警告が発生します。
コネクション型の使用
シグナルを発するオブジェクトの外からシグナルにアクセスしたい場合があります。このような目的のために、QtQuick
モジュールは、任意のオブジェクトのシグナルに接続するためのConnections 型を提供しています。Connections オブジェクトは、指定されたtarget から任意のシグナルを受け取ることができます。
例えば、先ほどの例のonClicked
ハンドラーは、onClicked
ハンドラーをtarget をbutton
に設定したConnections オブジェクトに配置することで、代わりにルートRectangle で受け取ることができました:
import QtQuick import QtQuick.Controls Rectangle { id: rect width: 250; height: 250 Button { id: button anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter text: "Change color!" } Connections { target: button function onClicked() { rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1); } } }
付属のシグナルハンドラ
アタッチドシグナルハンドラは、ハンドラが宣言されたオブジェクトではなく、アタッチド型からシグナルを受け取ります。
例えば、Component.onCompleted はアタッチドシグナルハンドラです。これは、JavaScriptの作成処理が完了したときに、JavaScriptのコードを実行するためによく使われます。以下はその例です:
import QtQuick Rectangle { width: 200; height: 200 color: Qt.rgba(Qt.random(), Qt.random(), Qt.random(), 1) Component.onCompleted: { console.log("The rectangle's color is", color) } }
onCompleted
ハンドラーは、Rectangle 型からのcompleted
シグナルに応答していません。その代わりに、completed
シグナルを持つComponent
アタッチ型のオブジェクトが、QMLエンジンによって自動的にRectangle オブジェクトにアタッチされています。エンジンはRectangleオブジェクトの生成時にこのシグナルを発行し、Component.onCompleted
シグナルハンドラをトリガします。
シグナルハンドラがアタッチされることで、各オブジェクトにとって重要な特定のシグナルが通知されるようになります。例えば、Component.onCompleted
アタッチド・シグナル・ハンドラがなかった場合、オブジェクトは、特別なオブジェクトからの特別なシグナルに登録することなく、この通知を受け取ることができませんでした。アタッチド・シグナル・ハンドラのメカニズムにより、オブジェクトは余分なコードなしに特定のシグナルを受け取ることができる。
アタッチドシグナルハンドラの詳細については、アタッチドプロパティとアタッチドシグナルハンドラを参照してください。
カスタムQML型にシグナルを追加する
signal
、カスタムQML型にシグナルを追加することができます。
新しいシグナルを定義するための構文は以下の通りです:
signal <name>[([<type> <parameter name>[, ...]])]
シグナルはメソッドとしてシグナルを呼び出すことで発生します。
例えば、以下のコードはSquareButton.qml
という名前のファイルで定義されています。ルートRectangle オブジェクトはactivated
シグナルを持っており、子TapHandler がtapped
になるたびにシグナルが発せられます。この例では、アクティブ化されたシグナルはマウスクリックの x 座標と y 座標で発せられます:
// SquareButton.qml import QtQuick Rectangle { id: root signal activated(real xPosition, real yPosition) property point mouseXY property int side: 100 width: side; height: side TapHandler { id: handler onTapped: root.activated(root.mouseXY.x, root.mouseXY.y) onPressedChanged: root.mouseXY = handler.point.position } }
これで、SquareButton
のどのオブジェクトも、onActivated
シグナル・ハンドラを使用して、activated
シグナルに接続できるようになります:
// myapplication.qml SquareButton { onActivated: (xPosition, yPosition) => console.log(`Activated at {xPosition}, ${yPosition}`) }
カスタムQMLタイプのシグナルの書き方についてはシグナル属性を参照してください。
シグナルをメソッドやシグナルに接続する
シグナルオブジェクトには、シグナルをメソッドや別のシグナルに接続するためのconnect()
メソッドがあります。シグナルがメソッドに接続されると、そのシグナルが発せられるたびに自動的にそのメソッドが呼び出されます。このメカニズムにより、シグナルをシグナル・ハンドラの代わりにメソッドで受け取ることができます。
以下では、connect()
メソッドを使って、messageReceived
シグナルを3つのメソッドに接続しています:
import QtQuick Rectangle { id: relay signal messageReceived(string person, string notice) Component.onCompleted: { relay.messageReceived.connect(sendToPost) relay.messageReceived.connect(sendToTelegraph) relay.messageReceived.connect(sendToEmail) relay.messageReceived("Tom", "Happy Birthday") } function sendToPost(person: string, notice: string) { console.log(`Sending to post: ${person}, ${notice}`) } function sendToTelegraph(person: string, notice: string) { console.log(`Sending to telegraph: ${person}, ${notice}`) } function sendToEmail(person: string, notice: string) { console.log(`Sending to email: ${person}, ${notice}`) } }
多くの場合、connect()関数を使わず、シグナル・ハンドラでシグナルを受け取れば十分です。しかし、connect
メソッドを使うと、先に示したように、シグナルを複数のメソッドで受け取ることができます。これは、シグナル・ハンドラでは一意に命名する必要があるため不可能です。また、connect
メソッドは、動的に生成されるオブジェクトにシグナルを接続するときに便利です。
接続されたシグナルを削除するには、対応するdisconnect()
メソッドがあります:
Rectangle { id: relay //... function removeTelegraphSignal() { relay.messageReceived.disconnect(sendToTelegraph) } }
シグナルとシグナルの接続
信号を他の信号に接続することで、connect()
。
import QtQuick Rectangle { id: forwarder width: 100; height: 100 signal send() onSend: console.log("Send clicked") TapHandler { id: mousearea anchors.fill: parent onTapped: console.log("Mouse clicked") } Component.onCompleted: { mousearea.tapped.connect(send) } }
TapHandler のtapped
シグナルが発信されると、自動的にsend
シグナルも発信される。
output: MouseArea clicked Send clicked
注意: 関数オブジェクトへの接続は、シグナルの送信者が生きている限り、生き続けます。この動作は、C++のQObject::connect ()の3引数バージョンに似ています。
Window { visible: true width: 400 height: 400 Item { id: item property color globalColor: "red" Button { text: "Change global color" onPressed: { item.globalColor = item.globalColor === Qt.color("red") ? "green" : "red" } } Button { x: 150 text: "Clear rectangles" onPressed: repeater.model = 0 } Repeater { id: repeater model: 5 Rectangle { id: rect color: "red" width: 50 height: 50 x: (width + 2) * index + 2 y: 100 Component.onCompleted: { if (index % 2 === 0) { item.globalColorChanged.connect(() => { color = item.globalColor }) } } } } } }
上の作為的な例では、すべての偶数矩形の色を反転させ、あるグローバルな色に従うようにすることが目的である。これを達成するために、すべての偶数矩形について、globalColorChangedシグナルと矩形の色を設定する関数との接続が行われる。これは、矩形が生きている間は期待通りに動作する。しかし、クリアボタンが押されると、矩形は消えてしまいますが、シグナルが発せられるたびにシグナルを処理する関数が呼び出されます。これは、グローバル・カラーを変更するときにバックグラウンドで実行しようとする関数によって投げられるエラー・メッセージでわかる。
現在のセットアップでは、globalColorを保持しているアイテムが破棄されると、コネクションは破棄されます。接続が長引かないようにするには、矩形が破棄されるときに接続を明示的に切断します。
©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。