C
カスタム入力デバイスの処理
タッチやキー入力のイベント以外にも、アプリケーションで処理しなければならない他の種類の入力がシステムにある場合があります。例えば、温度センサーや、システムへの入力を可能にするロータリーノブなどです。以下のセクションでは、これらの種類の入力を処理する方法について説明します。
温度センサーからの入力
まず、Qul::EventQueue を使用して、プラットフォームからアプリケーションへ温度測定値を送信するためのC++ から QML へのインターフェースを実装します。このインターフェースは `Qul::Singleton ` であり、instance() 関数を使用してアクセスできます。さらに、以下のプロパティとメソッドを定義します。
temperatureChanged-Qul::Signal :温度の測定値を受け取ります。Unit- さまざまな温度単位に対応する列挙型- Qul::Property - 単位の変更を追跡するため。
setUnit()- 新しい温度単位を設定し、現在の値を新しい単位に変換するためのセッターメソッド。onEvent()- `Qul::EventQueue::onEvent()` メソッドを再実装し、temperatureChangedシグナルを使用して温度の測定値を通知します。
定義されたインターフェースがQul::Singleton であるため、外部からのコンストラクタ呼び出しやコピーが防止されます。
struct TemperatureInput : public Qul::Singleton<TemperatureInput>, public Qul::EventQueue<double> { Qul::Signal<void(double value)> temperatureChanged; enum Unit { Celsius, Fahrenheit, Kelvin }; Qul::Property<TemperatureInput::Unit> unit; void setUnit(TemperatureInput::Unit newUnit) { switch (unit.value()) { case Celsius: if (newUnit == Celsius) return; newUnit == Kelvin ? cachedValue += 273.15 : cachedValue = cachedValue * 1.8 + 32.0; break; case Fahrenheit: if (newUnit == Fahrenheit) return; newUnit == Kelvin ? cachedValue = (cachedValue - 32.0) / 1.8 + 273.15 : cachedValue = (cachedValue - 32.0) / 1.8; break; case Kelvin: if (newUnit == Kelvin) return; newUnit == Celsius ? cachedValue -= 273.15 : cachedValue = (cachedValue - 273.15) * 1.8 + 32.0; break; } unit.setValue(newUnit); temperatureChanged(cachedValue); } void onEvent(const double &temperature) QUL_DECL_OVERRIDE { temperatureChanged(temperature); cachedValue = temperature; } private: // Create friendship for a base class to be able to access private constructor friend struct Qul::Singleton<TemperatureInput>; TemperatureInput() {} TemperatureInput(const TemperatureInput &); TemperatureInput &operator=(const TemperatureInput &); double cachedValue; };
これで温度イベントを送信する準備が整いましたので、TemperatureInput シングルトンインスタンスを介してイベントを投稿してください。 イベントは、温度センサー用のプラットフォーム固有の割り込みハンドラであるtemperatureSensorISR() 関数から投稿されます。ここで、unit Qul::Property が、アプリケーションで要求された単位で測定値を送信するためにどのように使用されているかに注目してください。これは、センサーが複数の単位で温度測定値を提供できる場合に、余分な変換を回避できるため便利です。
void temperatureSensorISR() { // Here would be platform specific code to read temperature sensor value. switch (TemperatureInput::instance().unit.value()) { case TemperatureInput::Celsius: TemperatureInput::instance().postEventFromInterrupt(30.0); break; case TemperatureInput::Fahrenheit: TemperatureInput::instance().postEventFromInterrupt(86.0); break; case TemperatureInput::Kelvin: TemperatureInput::instance().postEventFromInterrupt(303.15); } }
C++ から QML へのインターフェースクラスを含むヘッダーは、QmlProject ファイル内の `InterfaceFiles.files` を使用して QML に導入する必要があります:
Project {
...
InterfaceFiles {
files: ["temperatureinput.h"]
}
}以下は、簡単な温度測定用QMLアプリケーションの例です:
import QtQuick 2.15 Rectangle { Text { id: temperature anchors.centerIn: parent text: "0" Text { id: temperatureUnit property TemperatureInput.Unit unit: TemperatureInput.unit anchors.left: temperature.right anchors.top: temperature.top onUnitChanged: { if(unit == TemperatureInput.Celsius) { text = "°C" } else if(unit == TemperatureInput.Fahrenheit) { text = "°F" } else if(unit == TemperatureInput.Kelvin) { text = "K" } } Component.onCompleted: { TemperatureInput.unit = TemperatureInput.Celsius } } TemperatureInput.onTemperatureChanged: { temperature.text = value } } MouseArea { anchors.fill: parent onReleased: { if(temperatureUnit.unit == TemperatureInput.Celsius) { TemperatureInput.setUnit(TemperatureInput.Fahrenheit) } else if(temperatureUnit.unit == TemperatureInput.Fahrenheit) { TemperatureInput.setUnit(TemperatureInput.Kelvin) } else if(temperatureUnit.unit == TemperatureInput.Kelvin) { TemperatureInput.setUnit(TemperatureInput.Celsius) } } } }
アプリケーションの UI はシンプルで、ルートアイテムとして `Rectangle ` があり、温度値と単位を表示するための `Text ` アイテムがいくつかあることに注目してください。`MouseArea ` アイテムを使用すると、タッチ操作で温度単位を切り替えることができます。また、このアイテムは、シングルトン `TemperatureInput ` からの `temperatureChanged ` シグナルを監視しています。temperatureUnit のText 項目のunitプロパティは、TemperatureInputシングルトンのunitQul::Property インスタンスにバインドされています。これにより、QMLとC++の両方からこのプロパティを変更できます。最後に、画面をタッチするとsetUnit 関数を通じて単位が変更され、センサーからの次の読み取りを待つことなく、現在の温度値を即座に変換できるようになります。
ロータリーノブコントロールの入力
この例では、ロータリーノブ制御入力の処理方法を示します。ロータリーノブ制御は、例えば自動車のユーザーインターフェースを操作するために見られるようなコントロールノブのようなものです。 この例では、ロータリーノブは下、上、左、右の4方向に微調整できます。押し込むことも、左右に回転させることもでき、戻るボタンも備わっています。簡略化のため、回転にはダイナミックレンジがなく、360°まで完全に回転させることはできません。
ここで、カスタム型RotaryKnobInputEvent を導入します。これは、QMLでこの型を使用できるようにするためにQul::Object を継承した単純な構造体です。この型では、ユーザーがロータリーノブで行えるさまざまな操作を表すために、Action 列挙型を導入しています。また、開始状態と停止状態、および繰り返し操作のためのrepeat状態を用いて操作のライフサイクルを追跡するために、ActionState も導入しています。 このカスタム型のコンストラクタは、action および state パラメータを受け取ります。最後に、イベントデータを格納するための action および state メンバーが用意されています。
struct RotaryKnobInputEvent : public Qul::Object { enum Action { NudgeDown, NudgeUp, NudgeLeft, NudgeRight, Push, Back, RotateLeft, RotateRight }; enum ActionState { ActionStart, ActionRepeat, ActionStop }; RotaryKnobInputEvent(Action action, ActionState state) : action(action) , actionState(state) {} RotaryKnobInputEvent() : action(NudgeDown) , actionState(ActionStart) {} Action action; ActionState actionState; };
RotaryKnobInputEvent を含むヘッダーファイルは、QmlProjectファイル内のInterfaceFiles.filesを使用してQMLに導入する必要があります:
Project {
...
InterfaceFiles {
files: ["rotaryknobinputevent.h"]
}
}次に、ノブ入力用のC++からQMLへのインターフェースを導入します。これは、Qul::EventQueue を持つQul::Singleton です。このインターフェースは、ロータリーノブのaction およびstate をアプリケーションに送信するためのQul::Signal を1つ導入しています。また、Qul::EventQueue のonEvent() 関数をオーバーライドし、プラットフォームからのイベントを受信して、rotaryKnobEvent シグナルを介してアプリケーションに転送します。このインターフェースはQul::Singleton であるため、外部からのコンストラクタ呼び出しやコピーは禁止されています。
struct RotaryKnobInput : public Qul::Singleton<RotaryKnobInput>, public Qul::EventQueue<RotaryKnobInputEvent> { Qul::Signal<void(int action, int state)> rotaryKnobEvent; void onEvent(const RotaryKnobInputEvent &event) { rotaryKnobEvent(event.action, event.actionState); } private: // Create friendship for a base class to be able to access private constructor friend struct Qul::Singleton<RotaryKnobInput>; RotaryKnobInput() {} RotaryKnobInput(const RotaryKnobInput &); RotaryKnobInput &operator=(const RotaryKnobInput &); };
RotaryKnobInput インターフェースを含むヘッダーは、InterfaceFiles.filesを介してQMLに導入する必要があります:
Project {
...
InterfaceFiles {
files: ["rotaryknobinputevent.h",
"rotaryknobinput.h"
]
}
}これで、ロータリーノブのイベントをアプリケーションに送信できるようになります。以下は、ロータリーノブ入力デバイスのISRとして機能するダミーのrotaryKnobISR() 関数です。この関数は、RotaryKnobInput シングルトンインスタンスを使用してイベントを転送します。テスト目的で、ロータリーノブのステートやアクションを切り替えています。 実際のロータリーノブデバイスがない場合は、ボタンなどを接続して試してみてください。
void rotaryKnobISR() { // Here would be platform specific code to read state from rotary knob device. static int action = RotaryKnobInputEvent::ActionStart; static int state = RotaryKnobInputEvent::NudgeDown; RotaryKnobInput::instance().postEventFromInterrupt( RotaryKnobInputEvent(RotaryKnobInputEvent::Action(action), RotaryKnobInputEvent::ActionState(state))); if (++state > RotaryKnobInputEvent::ActionStop) { state = RotaryKnobInputEvent::ActionStart; if (++action > RotaryKnobInputEvent::RotateRight) action = RotaryKnobInputEvent::NudgeDown; } }
最後に、ロータリーノブからのイベントを受信し、受信したイベントをText アイテムに表示する簡単なテストアプリケーションを以下に示します。
import QtQuick 2.15 Rectangle { Text { id: knobState anchors.centerIn: parent text: "Waiting for rotary knob..." } RotaryKnobInput.onRotaryKnobEvent: { knobState.text = knobStateToString(state) + ": " + knobActionToString(action) } function knobActionToString(action : int) : string { if(action == RotaryKnobInputEvent.NudgeDown) return "NudgeDown" else if(action == RotaryKnobInputEvent.NudgeUp) return "NudgeUp" else if(action == RotaryKnobInputEvent.NudgeLeft) return "NudgeLeft" else if(action == RotaryKnobInputEvent.NudgeRight) return "NudgeRight" else if(action == RotaryKnobInputEvent.Push) return "Push" else if(action == RotaryKnobInputEvent.Back) return "Back" else if(action == RotaryKnobInputEvent.RotateLeft) return "RotateLeft" else if(action == RotaryKnobInputEvent.RotateRight) return "RotateRight" } function knobStateToString(state : int) : string { if(state == RotaryKnobInputEvent.ActionStart) return "ActionStart" else if(state == RotaryKnobInputEvent.ActionRepeat) return "ActionRepeat" else if(state == RotaryKnobInputEvent.ActionStop) return "ActionStop" } }
特定のQtライセンスの下で利用可能です。
詳細はこちらをご覧ください。