Calqlatr
横向きと縦向きのデバイス用にデザインされた電卓。ユーザーインターフェイスにはQt Quick カスタムコンポーネントとレスポンシブレイアウトを、アプリケーションロジックには JavaScript を使用しています。

Calqlatrは様々なQMLと Qt Quickカスタムコンポーネントの表示やレスポンシブレイアウトの使用など、さまざまなQMLとその特徴を示しています。アプリケーションロジックはJavaScript、UIはQMLで実装されています。
サンプルを実行する
からサンプルを実行するには Qt Creatorからサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Qt Creator:Tutorialを参照してください:ビルドと実行サンプルを実行すると、標準的な電卓としてアプリケーションを使用できるようになります。レスポンシブレイアウトを有効にするには、携帯電話を縦向きから横向きに変えるか、デスクトップではメインウィンドウのサイズを変更してください。
カスタムコンポーネントの使用
Calqlatrアプリケーションはカスタムタイプを使用します。そのほとんどは別の.qmlファイルで定義されています:
Main.qmlcontent/ApplicationState.qmlcontent/BackspaceButton.qmlcontent/CalculatorButton.qmlcontent/Display.qmlcontent/NumberPad.qml
Main.qml にはトップレベルウィンドウとルートアイテムが含まれています。content ディレクトリにある他のカスタムタイプを利用します。例えば、NumberPad 型(content/NumberPad.qml で定義)は、Main.qml で電卓の数字パッドの作成に使用されます:
NumberPad { id: numberPad Layout.margins: root.margin isPortraitMode: root.isPortraitMode applicationState: state }
インラインコンポーネントでは、.qml ファイル内で複数のコンポーネントを宣言できます。この例では、NumberPad.qml でインライン・コンポーネントを使用し、DigitButton とOperatorButton の2つのコンポーネントを定義しています:
component DigitButton: CalculatorButton { onClicked: { controller.applicationState.digitPressed(text); controller.updateDimmed(); } } component OperatorButton: CalculatorButton { dimmable: true implicitWidth: 48 textColor: controller.qtGreenColor onClicked: { controller.applicationState.operatorPressed(text); controller.updateDimmed(); } }
どちらのコンポーネントもCalculatorButton 型(CalculatorButton.qml で定義)ですが、クリックされたシグナルのカスタムハンドラを提供し、いくつかのプロパティのデフォルトを微調整しています。DigitButton とOperatorButton は、後でNumberPad.qml でインスタンス化されます:
DigitButton { text: "e" dimmable: true implicitWidth: 48 } OperatorButton { text: "ln" }
カスタムQMLコンポーネントの定義の詳細については、「QMLドキュメントによるオブジェクトタイプの定義」を参照してください。
レスポンシブレイアウト
この例では、レスポンシブ・レイアウトによって、縦向きと横向きの両方に異なるUIコンポーネントを配置しています。このことは、Main.qml でわかります。ColumnLayout はポートレートモード用、RowLayout はランドスケープモード用です:
ColumnLayout { id: portraitMode anchors.fill: parent visible: root.isPortraitMode LayoutItemProxy { target: display Layout.minimumHeight: display.minHeight } LayoutItemProxy { target: numberPad Layout.alignment: Qt.AlignHCenter } } RowLayout { id: landscapeMode anchors.fill: parent visible: !root.isPortraitMode LayoutItemProxy { target: display } LayoutItemProxy { target: numberPad Layout.alignment: Qt.AlignVCenter } }
ColumnLayout はアプリケーションのポートレート・レイアウトを表し、RowLayout はランドスケープ・レイアウトを表します。visible プロパティは、ある時点でどのレイアウトが使用されるかを処理します。NumberPad とDisplay コンポーネントのid属性は、LayoutItemProxy タイプのtarget プロパティを設定するために使用されます。これにより、両方のレイアウトで同じコンテンツ・アイテムを使用することができます。さらに、LayoutItemProxy アイテム内のプロパティをtarget 自体に転送することもできます。例えば、NumberPad がインスタンス化されたとき、両方のレイアウトは異なるLayout.alignment を必要とします。
レスポンシブ・レイアウトは、NumberPad 自体のポートレート・レイアウトとランドスケープ・レイアウトを定義する際にも、NumberPad.qml で使用されます:
RowLayout { spacing: controller.spacing GridLayout { id: scientificGrid columns: 3 columnSpacing: controller.spacing rowSpacing: controller.spacing visible: !controller.isPortraitMode OperatorButton { text: "x²" Accessible.name: "x squared" } OperatorButton { text: "⅟x" Accessible.name: "one over x" } OperatorButton { text: "√" } OperatorButton { text: "x³" Accessible.name: "x cubed" } OperatorButton { text: "sin" Accessible.name: "sine" } OperatorButton { text: "|x|" Accessible.name: "absolute value" } OperatorButton { text: "log" } OperatorButton { text: "cos" Accessible.name: "cosine" } DigitButton { text: "e" dimmable: true implicitWidth: 48 } OperatorButton { text: "ln" } OperatorButton { text: "tan" } DigitButton { text: "π" dimmable: true implicitWidth: 48 } } GridLayout { id: mainGrid columns: 5 columnSpacing: controller.spacing rowSpacing: controller.spacing BackspaceButton { onClicked: { controller.applicationState.operatorPressed(this.text); controller.updateDimmed(); } } DigitButton { text: "7" } DigitButton { text: "8" } DigitButton { text: "9" } OperatorButton { text: "÷" implicitWidth: 38 } OperatorButton { text: "AC" textColor: controller.backspaceRedColor accentColor: controller.backspaceRedColor } DigitButton { text: "4" } DigitButton { text: "5" } DigitButton { text: "6" } OperatorButton { text: "×" implicitWidth: 38 } OperatorButton { text: "=" implicitHeight: 81 Layout.rowSpan: 2 } DigitButton { text: "1" } DigitButton { text: "2" } DigitButton { text: "3" } OperatorButton { text: "−" implicitWidth: 38 } OperatorButton { text: "±" implicitWidth: 38 } DigitButton { text: "0" } DigitButton { text: "." dimmable: true } OperatorButton { text: "+" implicitWidth: 38 } } } // RowLayout
controller.isPortraitMode に応じて、数字パッドはシンプルな電卓のボタン(mainGrid )だけを含んでいたり、scientificGrid を介してより高度な機能を含んでいたりします。
ボタンテキストの色のアニメーション
CalculatorButton.qml ファイルでは、数字パッドのボタンの文字色もアニメーションします。
...
color: button.getTextColor()
Behavior on color {
ColorAnimation {
duration: 120
easing.type: Easing.OutElastic
}
}
}色の変更は、color プロパティにBehavior を定義することでアニメーション化されます。ボタンがdimmed = true に設定されると、ボタンは暗く表示されます。ボタンが押されると、緑色に点灯します。NumberPad 上のすべてのボタンのdimmed プロパティを動的に変更するには、buttonPressed シグナルがNumberPad のupdateDimmed() 関数を呼び出します。
function updateDimmed() { for (let i = 0; i < mainGrid.children.length; i++) { mainGrid.children[i].dimmed = applicationState.isButtonDisabled(mainGrid.children[i].text); } for (let j = 0; j < scientificGrid.children.length; j++) { scientificGrid.children[j].dimmed = applicationState.isButtonDisabled( scientificGrid.children[j].text); } }
計算の実行
calculator.js とApplicationState.qml ファイルは電卓のエンジンを定義しています。calculator.js には電卓の論理状態と、状態を変更するための操作が含まれています。ApplicationState.qml はQML型を通してこのAPIを公開しています。
まず、calculator.js を見てみましょう:
let accumulator = 0 let pendingOperator = "" let lastButton = "" let digits = ""
accumulator pendingOperator, , 電卓の論理状態。lastButton represent
function isOperationDisabled(op, display) {
...
}isOperationDisabled() 現在のエンジンと表示状態に基づいて、op 操作を無効にする必要がある場合はtrue を返します。そうでない場合は、false を返します。
function digitPressed(op, display) {
...
}
function operatorPressed(op, display) {
...
}digitPressed() とoperatorPressed() 関数は、押されたボタンに基づいてエンジンとディスプレイの状態を更新します。
ApplicationState.qml は、calculator.js で定義されたJavaScript関数を、より洗練されたQML APIで公開しています:
import QtQml import "calculator.js" as CalcEngine
calculator.js はCalcEngine という名前でインポートされています。
QtObject { required property Display display function operatorPressed(operator) { CalcEngine.operatorPressed(operator, display); } function digitPressed(digit) { CalcEngine.digitPressed(digit, display); } function isButtonDisabled(op) { return CalcEngine.isOperationDisabled(op, display); } }
required property Display.qml のインスタンスが の という名前で常に利用できるようにしています。 は 、 、 の関数で利用されます。ApplicationState display display operatorPressed() digitPressed() isButtonDisabled()
ソースファイル
Squish GUIのテスト
このアプリケーションには、Qt for Androidを対象としたSquish GUIテストが付属しています。テストはSquish for Qtで作成され、Pythonで書かれており、アプリケーションのtestディレクトリにあります。
Squish for Qt for Androidを使用する場合は、必ずQt組み込みのフックを使用し、adbでSquishポートを転送してください。dlopen が失敗して "Cannot load library" というエラーが表示される場合は、CMake 設定からQT_USE_TARGET_ANDROID_BUILD_DIR を無効にするか、Qt Creator のProjects->Build Settings->CMake->Current Configuration からクリアしてください。
Squishのライセンスを持っていない場合は、無料トライアルを入手できます。
QMLアプリケーションも参照してください 。
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.