Qt 5.3:モバイルクロスプラットフォームのロースタート

モバイルデバイス向けのクロスプラットフォーム開発に興味があるすべての人に挨拶します! 最近まで、多くのプラットフォームですぐに動作する非ネイティブアプリケーションを作成できるツールについて非常に懐疑的でした。 しかし、ある時点で、好奇心と何か新しいことを試してみたいという欲求が抑制心を上回っていました。 選択肢はQt 5.3に落ちました。 なんで? Qtはオープンで非商用および商用(LGPLライセンスの下)での使用が無料であるため、長い歴史(1996年に公開)とこれらのライブラリを使用して実装された十分な数の高品質プロジェクト(オフハンド-Skype、2GISアプリケーション)があります。

この出版物の目的は、 Qtの最新バージョンでのモバイル開発について読者に知らせることです。クライアントサーバーアプリケーションの主要な要素の実装を示し、Qtライブラリを操作する際に考えられる「落とし穴」を強調します。
必要なトレーニングと経験のレベルは最小限です(C ++の基本に関する知識、ローカルサーバーを構成する基本的な能力)。
認識を容易にするための資料は、「落とし穴」、1つのプロジェクトの歴史とエラーに関する作業、クライアント/サーバー開発の基本、要約の4つの部分に分かれています。

1.「落とし穴」
1.1。 異なるバージョンのライブラリの非互換性。

Qtは非常に頻繁に更新されますが、これは一方では良いことですが、一方では開発が悪夢になることもあります。 これは、新しいバージョンには前バージョン(メジャーバージョンを意味する)との互換性がなく、その機能の一部は最良の場合は廃止され、最悪の場合はアクセスできなくなるためです。 その結果、バージョン5.3で使用できないWeb上の無関係な資料/例が豊富にあります。 一般に、残念なことに、Qt 5.3の有用な情報はごくわずかです。検索エンジンの問題を説明し、ドキュメント(ドキュメントは高品質ですが、一般に十分な一般理論はありません)またはバージョン4.6または4.8の例を参照します。 Qt 5.3には役に立たない。 公式のガイドと例は、基本的に非常に些細なケースを基本的に説明していますが、原則として、ほとんど遭遇することはありません。 文献については触れませんが、物事はそれに似ています(古いバージョンに関連)。 ただし、これらの欠点は、活発なコミュニティによって部分的に相殺されています(公式フォーラムはqt-project.org/forumsです )。

1.2。 Android / iOS機能の生のサポート。

直接言えば、現在これらのプラットフォーム用のWebView実装はありません。 おそらく最悪の欠点ではないかもしれませんが、今のところは、ジオマップの簡単な作業を忘れる価値があります。 2番目の問題は、IMEIデバイスを取得できないことです(たとえば、サーバーに認証を実装できません)。古いバージョンでは、QtMobilityライブラリがこの目的で使用されていましたが、新しいバージョンでは、以前のデバイスの完全な置き換えはまだ実装されていません。 ただし、これらの欠点でも本当に解決できますが、これには各プラットフォームのネイティブレベルでの「没入」が必要です(たとえば、AndroidでIMEIを取得する-habrahabr.ru/sandbox/77966 )が、これは、プラットフォームに依存しない開発の元のアイデアと矛盾します。 また、開発環境(Qt Creator)にいくつかの小さなバグがあることに注意してください。たとえば、IDEを再起動しないとQMLファイルへの変更が適用されないことがありました。

1.3。 バグの追跡。

他の開発者(Android、iOS、Web)とコミュニケーションをとりながら、C ++をメイン言語として使用するライブラリを使用したくないプログラマーもいるという印象を受けました。 実際、小さなアプリケーションを開発するとき、C ++ではなくQt自体の特性のために不快な状況の大部分が発生しました。 原則として、エラーメッセージはあまり情報的ではありません。「ファイルが見つかりません」、「未定義の参照」などのメッセージの出力は、問題が存在する可能性がある場合(たとえば、モジュールが* .proファイルに接続されていない場合) 、プログラム内のまったく異なる場所での間違ったメソッド呼び出しなど。 エラーを追跡するには(後者をほとんど盲目的に探しないように)、ログは非常に役立ちます。qDebug()関数はC ++コードにあり、console.log()はQMLにあります。 エラーのトピックは、出版物の次のほぼ全体に当てられています。

2. 1つのプロジェクトの歴史とバグに関する作業
この部分は、小さなクライアント/サーバーアプリケーションを開発するときに犯した間違いについてです。 次のパートで提案するソリューションへのパスをここで説明します。 このセクションは読み飛ばしても問題なくスキップできます。

目標に関する論文:以下を可能にするアプリケーション:
-サービスに登録します。
-ログイン;
-表示リスト;
-サーバーに要求を送信し、受信した応答を解析します。

一般的に、すべてが非常に簡単です。 Androidでの開発について知っていたイディオムを使用したかったのです。つまり、ロジックとUIを分離し、すべてを1つのコードで記述しないようにしました。 QtのUIフォーム[1]を見て、このような「デスクトップ」ビューはユーザーインターフェイスには適さないと判断しました。大きな画面を備えたデバイスでも、一部の要素に到達するのは困難です。 UIフォームのスタイルに没頭したくなかったので、レイアウトとデザインのためにQMLファイルを使用することにしました(詳細については次のパートで説明します)。

画像
1. AndroidデバイスでのUIフォームの外観。

QMLで作成されたインターフェースをC ++バックエンドに接続するために、この例-habrahabr.ru/post/138837を使用しました 。 良い動作例(ほとんどの場合、著者はQtに出会ったときと同じ問題に遭遇しました)。 ただし、この例で使用されているQtバージョン(4.7.4)が廃止されたため、将来、いくつかの問題が発生しました。 それらの最初のものは、Androidで実行しているときの巨大なListViewブレーキです。 その理由は、古いバージョンのQML(QtQuick 1.0)です。

修正し、QMLのインポートをQtQuick 1.0から2.2に変更します。 QtDeclarative / QDeclarativeViewを使用する古いバックエンドはQtQuick 1.0にのみ関連するため、すべてがそれほど単純ではありません。 やり直し-qt-project.org/doc/qt-5/qtquick-porting-qt5.html 一般に、QtQuick 1.0はモバイルプラットフォームにあまり馴染みがありません。たとえば、入力中にキーボードが表示されたときなど、奇妙な点がありました。インターフェイスは、キーボードと同時に完全に見えるように2回圧縮されるか、そのサイズを維持しましたそのため、キーボードの後ろの画面の半分が見えなくなりました。 QtQuick 2.2はこの点で嬉しく驚きました。入力を開いたときに、歪みがなく、入力フィールドが視界から失われないように、関心が穏やかに引き上げられたためです。

これですべてがうまくいくように見え、ListViewは飛びますが、新しい驚きが現れます。 最初は、各ウィンドウに(QMLファイルを使用して)バインドされたインターフェイスを持つ独自のクラスが含まれるようにアプリケーションを作成しました。 新しいウィンドウを開くと、対応するクラスのインスタンスが作成され、このウィンドウが上部に表示され、古いウィンドウはどこにも消えません。 現在のウィンドウを閉じると、クラスインスタンスが削除され、前のウィンドウが再び表示されます。 Androidのいくつかのバックスタックアクティビティのようなもの。 驚いたことに、QtQuick 2.2ではこれは不可能になりました(この場合、Androidで静的な灰色の画面が表示されるだけです)。 理由はbugreport-bugreports.qt-project.org/browse/QTBUG-39454にあります。 一番下の行:Androidで使用できるOpenGLウィンドウは1つだけです(QtQuick 2.2はそれを使用するだけです)。 iOSでは、ウィンドウが表示されましたが、その横枠が表示されました。 残念ながら、私は望ましいイディオムを拒否しなければなりませんでした。 これで、1つのメインQMLファイルが(QQmlApplicationEngineを介して)使用され、異なるウィンドウとそれらの間の遷移がローダーによって実行されます(次の部分でそれについて)。 これ以上詳しくは説明しません。JSON、HttpPostリクエストの解析方法、キーボードでは表示されない/不本意なジャムの解決方法、QMLでバックスタックのようにナビゲーションを作成する方法(「手動」プレス処理ボタン「戻る」)およびいくつかのエンティティ。

3.クライアント/サーバー開発の基本
ここでは、Qtで頭を壁に少しパンチした後の解決方法を説明します。 これは本書の主要部分です。

これで開始する準備ができました! Qt 5.3が既にインストールされていることを前提としています-qt-project.org/downloads、JDK、Android SDK、NDK、apache-antはAndroidでコンパイルするためにダウンロードされます(Qt Creator設定でパスを指定する必要があります)、iOSの場合-XCODE 。

3.1。 プロジェクトの作成と準備
Qt Creatorを起動し、[新しいプロジェクト]をクリックします。 「Qt Quick Application」を選択します:
画像

名前に違いがないように、プロジェクトを「サンプル」と呼びます(プロジェクトの名前とそのパスにキリル文字を使用することはできません)。
画像

Qt Quickコンポーネントのセットは、Qt Quick Controls 1.1に残ります。
画像

Qt Quick Controls 1.1は、Qt Quick 2.2の基本要素に加えて、さまざまな機能インターフェイス要素(コントロール)を含むライブラリのセットです。 ここでは、コントロールを使用するとアプリケーションのサイズが3メガバイト増加することをすぐに予約します。私の場合、コントロールを含む収集されたapkファイルの重量は11.6 MBでした。 私の意見では、プラスコントロールはアプリケーションの起動を遅くします(ただし、問題はローダーまたはスプラッシュスクリーンで解決できます)。 ただし、それらを使用すると開発が簡素化されるため、それらを放棄しません。

次に-アプリケーションを実行するプラットフォームを選択します。
画像

必要なプラットフォームのチェックボックスをオンにします。インストールしたプラットフォームに関係なく、デスクトップもチェックすることをお勧めします。アプリケーションはデスクトップで最も速く実行されるため、テストに便利です。

バージョン管理の接続は停止しません(この例では簡単に実行できますが、この例では必要ありません)。[完了]をクリックするだけです。
画像

プロジェクトが作成されました! 何で構成されていますか?
画像

プロジェクトツリーを見てみましょう。 まず第一に、私たちが必要とする作業では:
-構成* .proファイル(Sample.pro):Androidのマニフェストファイルに似たもの。
-ソースコードとヘッダーファイル(後で作成します)。アプリケーションバックエンドがあります。
-* .qrc-file(qml.qrc):その中に、アプリケーションに必要なすべてのリソース(QML-ファイル、イメージなど)を示します。
-インターフェイスのQMLファイル(これまでのところ、main.qmlのみがあります)。

アプリケーションを保存して実行すると、デスクトップに次のようなものが表示されます。
画像

3.2。 インターフェイス作成
3.2.1。 ボタンを作成する
main.qmlファイルを開きます。 タイトル、メニューバー、テキストを削除します。 その結果、次のことができるようになります。

import QtQuick 2.2 import QtQuick.Controls 1.1 ApplicationWindow { visible: true width: 640 height: 480 } 


ApplicationWindowは、子が追加されるルート要素です。 メインQMLファイルに存在する必要があります。 ApplicationWindow内にidとobjectNameの2つのフィールドをすぐに書き込みます。

 ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 } 


idはQMLファイル内のApplicationWindowにアクセスするために使用されます。objectNameはC ++コードでQMLからメインウィンドウを見つけるために必要です。

固定値の幅と高さのパラメーターは今のところ混同しないでください。 インターフェースは、最初にそのような寸法でのみ作成され、その後スマートフォンの画面サイズまで拡大されます。 このため、Androidでは、アプリケーションの起動時に画面がわずかにぎくしゃくしており、修正方法があります。最後に説明します。

一般的に、構文ベースのQMLファイルは、Webレイアウトとjavascript-logicの揮発性の混合を連想させますが、類似性は部分的です。 QMLでは、インターフェイスだけでなく、ロジック(たとえば、フォームの完了を確認するのに便利)も作成できますが、これはプラスです。

ウィンドウの色を指定しましょうこれを行うには、ApplicationWindowの「height:480」行のすぐ下に行を追加します

 color: "#F0F0FF" 


16進コードで色を設定できます。一部の色は、たとえば「赤」、「透明」などの名前で指定できます。 作成した定数を使用することもできます。詳細については、後で説明します。 次に、インポートに追加します。

 import QtQuick.Controls.Styles 1.2 


コントロールをカスタマイズするにはスタイルが必要になります。 ApplicationWindow内にボタンを作成します。 一般的なコード:

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height } } 


ボタンの幅は、メインウィンドウの幅、高さ-メイン画面の高さを基準にして計算されます。 ここでは、ボタンのサイズと位置を計算するためのid、基本式によって要素にアクセスする例を示します[参照 サプリメント1] 。 式のパラメーター(yによって計算される高さ)の前にidを指定しない場合、内部にある現在の要素のパラメーターが使用されていると見なされます。 すべてを保存して実行します。
画像

ボタンが表示され、そのサイズは開いているウィンドウ(デスクトップ)またはモバイルデバイスの画面のサイズに対して計算されます。これにより、さまざまなデバイス(UIを作成するためにカスタムクラスを使用しない場合のAndroidのusually核と同じ)の同じ表示の問題を解決できます。

新しいボタンのスタイルを作成しましょう! これを行うには、Button内に以下を追加します。

 style: ButtonStyle { } 


ボタンのスタイルは、背景とラベルの2つの部分で構成されます。 これらの部分を別のQMLファイルに作成して、メインの部分が乱雑にならないようにします。 これを行うには、Qt Creatorを折りたたんでプロジェクトフォルダーに移動し、そこに「QML」フォルダーを作成して、作業ファイルが整然と配置されるようにします。

Qt Creatorに戻ります。 ツリー(プロジェクト)でプロジェクト名を右クリックし、コンテキストメニューで[新規追加...]を選択します。 次に、Qt、「QMLファイル(Qt Quick 2)」を選択します。
画像

名前は「ButtonBackground」、パスは新しく作成された「QML」フォルダーへのパスです。
画像

プレフィックスを確認します。バージョン管理は必要ないため、[完了]をクリックします。
画像

同様に、「ButtonLabel.qml」ファイルを作成します。 プロジェクトは次のようになりました。
画像

「QML / ButtonBackground.qml」をダブルクリックして開きます。 次のように編集します。

 import QtQuick 2.2 Rectangle { anchors.fill: parent anchors.margins: 4 border.width: 2 border.color: "#FFFFFF" radius: height / 4 gradient: Gradient { GradientStop { position: 0; color: control.pressed ? "#00AAAA" : "#AAFFFF" } GradientStop { position: 1; color: "#00CCCC" } } // color: "#00AAAA" // opacity: 0.25 } 


順番に:
-anchors.fill:parent-ボタンの背景が完全に塗りつぶされるようにするparent-現在の要素が配置されている親要素へのアクセス。
-anchors.margins:4-すべての側面から小さなインデントを設定します。
-ボーダー-ストローク設定[参照 サプリメント8] ;
-半径:高さ/ 4-ここではすでに興味深いです。 この場合、角を丸くするために使用されます(値はボタンの高さに対して広がります)が、Rectangleの幅と高さを同じに設定すると、十分に大きい半径値を使用してRectangleから円を取得できます。
-勾配は2点で構成されます。 最初のポイント(ゼロ位置)に注意してください-ここでは、ボタンの状態(control.pressed)に切り替えて、押されたボタンに応じて色を決定します。単純な条件演算子を使用します。 真実の場合のアクション:falseの場合のアクション。
-グラデーションの代わりに色を設定できます。たとえば、透明度(不透明度:0〜1)を使用することもできます。

「ButtonBackground.qml」を保存し、「main.qml」に戻ります。 作成したQMLフォルダーをインポートに追加します。

 import "QMLs" 


これで、このフォルダー内のすべてのQMLファイルにアクセスできます。 ボタンスタイルに戻り、作成したばかりの背景を指定します。 結果は次のようになります。

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 import "QMLs" ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} } } } 


すべてを保存して実行します。
画像

ボタンは少し明るくなりましたが、署名がありません。 修正してください。 「ButtonLabel.qml」を開き、次のように編集します。

 import QtQuick 2.2 Text { anchors.fill: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.bold: true font.pixelSize: (mainWindow.width>mainWindow.height) ? parent.height/1.75 : parent.height / 2.5 color: "#005050" // text: "" } 


署名はボタン本体を完全に埋め、中央に配置され、大胆な顔をしています。 署名のサイズは、現在の画面の向きを基準にして計算されます。幅が大きいほど横向きになり、高さ-縦向きになるのは論理的です[参照 補足2] 。 ここではボタンテキストをインストールしません。これは不便です。

再び「main.qml」を開きます。 作成したばかりの「ButtonLabel」をラベルスタイルに追加します。ここでは、ボタンテキストをすぐに示します[参照 サプリメント3] 。 次のようになります。

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 import "QMLs" ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "" } } } } 


すべてを保存して実行します。
画像

クリック可能なボタンがあるので、クリックハンドラーを追加して、ログにメッセージを出力します。 これを行うには、ボタン内に行を追加します。

 onClicked: console.log("Button '' clicked") 


合計で:

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 import "QMLs" ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "" } } onClicked: console.log("Button '' clicked") } } 


実行して確認してください。 「アプリケーション出力」パネル(Qt Creatorウィンドウの一番下)のボタンをクリックすると、新しい行が表示されます:「qml:Button 'Test' clicked」:
画像

これで、今のところボタンで作業を終了します。

3.2.2。 テキスト入力フィールドを作成する
「main.qml」を開きます。 「ボタン」の下に追加:

 TextField { id: textField width: parent.width height: parent.height / 10 horizontalAlignment: Text.AlignHCenter placeholderText: " " validator: RegExpValidator { regExp: /[--a-zA-Z]{16}/ } style: TextFieldStyle { background: Rectangle {color: "white"} textColor: "#00AAAA" placeholderTextColor: "#00EEEE" font: font.capitalization = Font.Capitalize, font.bold = true, font.pixelSize = mainWindow.height / 25 } Keys.onPressed: { if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { Qt.inputMethod.hide() event.accepted = true } } } 


サイズを設定します。フィールドからテキストを読み取るにはidが必要です。 中央に配置を配置し、プレースホルダーを指定します。

また、この入力に単純な正規表現を追加します(いずれの場合も「a」から「i」、および「a」から「z」の文字、合計16文字以内)。

スタイルは別のファイルに移動されません。 背景(背景)に白い長方形を置き、入力テキストとプレースホルダーの色を設定します。 フォントでは、単語を大文字(font.capitalization = Font.Capitalize)、太字、およびフォントサイズで構成します(ここでは親要素にアクセスできないため、画面の高さに対するサイズを計算します)。

「TextField」の場合、inputMethodHints:フィールドに値を追加することもできます(Qt.ImhPreferNumbersなど)。 値に応じて、入力用のさまざまなキーボードが表示されます(Qt.ImhPreferNumbersの場合、数字を入力するためのキーボードが表示されます[付録6を参照] )。 この場合、このフィールドは追加しません。 また、echoModeなどの他の便利なパラメーターもあります(パスワードを入力するには、TextInput.Passwordに設定できます)。

別に、Keys.onPressed内の式について言及する必要があります。 ここでは、完了ボタンのクリックを追跡して、クリックしたときにAndroidデバイスのキーボードを閉じるようにします。 したがって、「main.qml」は最終的に次のようになります。

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 import "QMLs" ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "" } } onClicked: console.log("Button '' clicked") } TextField { id: textField width: parent.width height: parent.height / 10 horizontalAlignment: Text.AlignHCenter placeholderText: " " validator: RegExpValidator { regExp: /[--a-zA-Z]{16}/ } style: TextFieldStyle { background: Rectangle {color: "white"} textColor: "#00AAAA" placeholderTextColor: "#00EEEE" font: font.capitalization = Font.Capitalize, font.bold = true, font.pixelSize = mainWindow.height / 25 } Keys.onPressed: { if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { Qt.inputMethod.hide() event.accepted = true } } } } 


AndroidタブレットとiPhoneシミュレーターでは、現在のアプリケーションは次のようになります。
画像

画像

論理的な結論を達成するために、ボタンがクリックされたときに私たちの名前の挨拶が表示される署名を追加しましょう。 「main.qml」の「TextField」の後に追加します。

 Text { id: text y: textField.height width: parent.width height: parent.height / 10 font.pixelSize: height / 2 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap } 


署名は、テキスト入力フィールド(y:textField.height)のすぐ下に配置されます。このため、ワードラップも設定します。 繰り返しますが、便宜上「テキスト」全体を別のQMLファイルに移動できますが、この場合はこれを行いません。

ボタンを押すためのロジックを規定することは残っています。 ボタンのonClickedフィールドに目的のアクションをすべて記述できますが、今回はこのための関数を作成しましょう。 これは、ボタンに追加された関数を使用した場合の「main.qml」の外観です。

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 import "QMLs" ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { function hello() { if (textField.text != "") { text.text = ", <b>" + textField.text.toUpperCase() + "</b>!" } } width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "" } } onClicked: hello() } TextField { id: textField width: parent.width height: parent.height / 10 horizontalAlignment: Text.AlignHCenter placeholderText: " " validator: RegExpValidator { regExp: /[--a-zA-Z]{16}/ } style: TextFieldStyle { background: Rectangle {color: "white"} textColor: "#00AAAA" placeholderTextColor: "#00EEEE" font: font.capitalization = Font.Capitalize, font.bold = true, font.pixelSize = mainWindow.height / 25 } Keys.onPressed: { if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { Qt.inputMethod.hide() event.accepted = true } } } Text { id: text y: textField.height width: parent.width height: parent.height / 10 font.pixelSize: height / 2 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap } } 


ご覧のとおり、テキストをフォーマットすることができます(この場合はタグを使用)。保存、実行、および確認します。
画像

3.2.3。 ListViewでの作業
この例では、2つのリストを一度に作成して、ListViewの機能をさらに表示します。 「テキスト」の下の「main.qml」に追加します。

 ListView { id: lvList objectName: "lvList" y: text.y + text.height width: parent.width height: parent.height * 0.3 clip: true spacing: 8 model: ListModel { ListElement { name: " 0" } ListElement { name: " 1" } ListElement { name: " 2" } } delegate: Rectangle { width: lvList.width height: lvList.height / 3 color: "#00AAAA" Text { text: name } MouseArea { anchors.fill: parent onClicked: console.log("ListView el#" + index + " clicked!") } } } 


ListViewの識別子とサイズを設定します。 ListView自体は透明なので、そのための基盤を作成しません。

クリップパラメータはtrueに設定されています。これは、 それ以外の場合、リストをスクロールすると、リストに表示されるようになる要素がListViewからクロールされます。

間隔パラメーターは、リスト項目間のインデントを設定します。

さらに、モデルは基本的にデータモデルです。 複数のListElementを含むListModelが入力されます。 「ListElement」は実際にはリストの要素であり、内部に目的の名前と値のペアを登録できます。この場合、ペアは1つだけです。

デリゲートパラメーターは、リスト内の各アイテムの外観を決定します。 テキストが入った長方形ができます。 テキストパラメーターの値については、モデルに以前に登録された名前パラメーターを示します。

「MouseArea」は、マウスクリックを追跡したり、画面に触れたりできる領域です。 デリゲートのサイズを入力します。 を押すと、ログに要素のインデックスが表示されます。 MouseAreaには便利なonPressedフィールドもあります。
そしてonReleased。

一般に、QMLで規定されているモデルは将来必要ありません。 バックエンドが充填を行います。 ただし、このようなスタブを作成してリストの外観をカスタマイズすると便利です。 また、デリゲートのデザインを別のQMLファイルで作成することも便利です。 また、ListViewにはさまざまなパラメーターがあり、微調整を試みることができることも追加します。 しかし、ここでは他に何も複雑にしません。 何が起こったのか見てみましょう。 保存して実行:
画像

リスト項目をクリックしてログを確認してみましょう。「qml:ListView el#0 clicked!」のような行が表示されます。

2番目のリストに移動します。 最初のリストの下に追加します。

 ListView { id: lvPager y: lvList.y + lvList.height width: parent.width height: parent.height * 0.2 clip: true model: ListModel { ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=Moscow" } ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=London" } ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=Rio" } } delegate: Image { width: lvPager.width height: lvPager.height source: map_url fillMode: Image.PreserveAspectFit } orientation: ListView.Horizontal snapMode: ListView.SnapOneItem } 


モデルとして、リクエストに応じて返される画像をマップするためにリンクを使用します。 デリゲートはイメージ自体であり、そのソース(ソース)はモデルで指定されたリンクになります。 ローカルファイルをソースとして指定することもできます。 これを行うには、次のものが必要です。
-プロジェクトフォルダーに画像を追加します。
-Qt Creatorのプロジェクトツリーで、「qml.qrc」を右クリックし、「エディターで開く」を選択します。
-ウィンドウの「追加」->「ファイルの追加」->追加した画像を選択します。
-QMLファイルの「イメージ」で、ソースとしてイメージへのパスを次のように示します:「qrc:/ full_file_name」。

「画像」の場合、縦横比を維持しながら画像自体が目的の幅/高さの寸法に調整されるように、fillModeも設定します。

方向パラメーター(水平方向に設定)とsnapMode:ListView.SnapOneItemを一緒に使用すると、ListViewがAndroidのViewPagerのようになるという効果を実現できます。

ほぼ完了。 プロジェクトツリーで、ファイル「Sample.pro」を開き、「ネットワーク」を「QT + = qml quick widgets」行に追加します。そうしないと、ネットワーク接続が機能しません。
画像

念のため、すべての「main.qml」コードのリストを投稿しています。

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 import "QMLs" ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { function hello() { if (textField.text != "") { text.text = ", <b>" + textField.text.toUpperCase() + "</b>!" } } width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "" } } onClicked: hello() } TextField { id: textField width: parent.width height: parent.height / 10 horizontalAlignment: Text.AlignHCenter placeholderText: " " validator: RegExpValidator { regExp: /[--a-zA-Z]{16}/ } style: TextFieldStyle { background: Rectangle {color: "white"} textColor: "#00AAAA" placeholderTextColor: "#00EEEE" font: font.capitalization = Font.Capitalize, font.bold = true, font.pixelSize = mainWindow.height / 25 } Keys.onPressed: { if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return) { Qt.inputMethod.hide() event.accepted = true } } } Text { id: text y: textField.height width: parent.width height: parent.height / 10 font.pixelSize: height / 2 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap } ListView { id: lvList objectName: "lvList" y: text.y + text.height width: parent.width height: parent.height * 0.3 clip: true spacing: 8 model: ListModel { ListElement { name: " 0" } ListElement { name: " 1" } ListElement { name: " 2" } } delegate: Rectangle { width: lvList.width height: lvList.height / 3 color: "#00AAAA" Text { text: name } MouseArea { anchors.fill: parent onClicked: console.log("ListView el#" + index + " clicked!") } } } ListView { id: lvPager y: lvList.y + lvList.height width: parent.width height: parent.height * 0.2 clip: true model: ListModel { ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=Moscow" } ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=London" } ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=Rio" } } delegate: Image { width: lvPager.width height: lvPager.height source: map_url fillMode: Image.PreserveAspectFit } orientation: ListView.Horizontal snapMode: ListView.SnapOneItem } } 


実行して何が起こったかを確認できます。
画像

私の意見では、QMLでListViewを操作するのは非常に簡単で便利であり、カスタマイズの可能性はたくさんあります。 ListViewを使用すると、たとえば、スピナー(ポップアップリスト)を実装して、リストの可視性(フィールドの表示)を操作することもできます。

重要な注意:何らかの理由で、Habrは "map_url"の¢erをセント記号に置き換えます。 リクエストリンクでは、&center = ...をスペースなしでのみ使用する必要があります。

3.2.4。 ローダーといくつかのアニメーションを使用する
「ローダー」は、QMLでマルチウィンドウを実装できるようにするエンティティです。 中核となる「ローダー」は、QMLファイルをソースとして持つ単なるコンテナです(理論的には、このファイルを外部サーバーのどこかに配置することもできます)。 ここで非常に重要です。QMLまたはC ++コードからLoader内の要素にアクセスする場合、この要素を含むコンテナを現時点でLoaderにロードする必要があります。そうしないと、アプリケーションがクラッシュします。 現時点では存在しない要素を参照することはできず、Qtにそのプロパティの一部を変更するように依頼することはできません。 問題を解決するには、特定のコンテナのアクティビティに関するブール値フラグを保存するか、危険な時間(たとえば、サーバーへの接続の進行中)にローダーのコンテンツの切り替えを禁止します(ナビゲーションボタンで有効:falseを設定できます)。

したがって、プロジェクトのQMLフォルダーに2つのQMLファイルを作成します。 それらを「Loader1.qml」および「Loader2.qml」と呼びましょう。 「Loader1.qml」:

 import QtQuick 2.2 Rectangle { anchors.fill: parent color: "green" opacity: 0.2 } 


ただの緑色の長方形。 今「Loader2.qml」:

 import QtQuick 2.2 Rectangle { id: rect anchors.fill: parent focus: true color: "blue" opacity: 0.2 SequentialAnimation { running: true loops: SequentialAnimation.Infinite PropertyAnimation { target: rect; property: "opacity"; to: 0; duration: 500 } PropertyAnimation { target: rect; property: "opacity"; to: 0.2; duration: 500 } } Keys.onPressed: { if (event.key == Qt.Key_Back || event.key == Qt.Key_Escape) { loader.source = "qrc:/QMLs/Loader1.qml" event.accepted = true } } } 


focus:trueを指定してください。 そうでない場合、Keys.onPressedセクションは起動しません。

SequentialAnimationは、一連のアニメーションです。 running:trueパラメーターを指定して、アニメーションがすぐに機能するように、ループ(繰り返しの数)を無限に設定します。

内部では、SequentialAnimationには2つのPropertyAnimationsがあります。 特定の時間(期間)の間、ターゲット要素(ターゲット)のパラメーター(プロパティ)を目的の値(に)に変更できます。

アニメーションが停止した後(running:falseに設定した場合)、変更されたすべてのターゲットパラメータを元のパラメータに手動で戻す必要があることに注意してください。

「main.qml」内にローダー自体を作成します。

 Loader { id: loader y: lvPager.y + lvPager.height width: parent.width height: parent.height * 0.2 focus: true source: "qrc:/QMLs/Loader1.qml" } 


内部要素(ソース)がボタンのクリックを監視するように、必ずfocus:trueを指定してください。

: . , Keys.onPressed :

 Keys.onPressed: { if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return || event.key == Qt.Key_Back) { Qt.inputMethod.hide() loader.forceActiveFocus() event.accepted = true } } 


«main.qml», :

 Button { width: mainWindow.width / 4.5 height: mainWindow.height / 10 x: mainWindow.width / 2 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "Loader" } } onClicked: loader.setSource("qrc:/QMLs/Loader2.qml") } 


«main.qml»:

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 import "QMLs" ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { function hello() { if (textField.text != "") { text.text = ", <b>" + textField.text.toUpperCase() + "</b>!" } } width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "" } } onClicked: hello() } TextField { id: textField width: parent.width height: parent.height / 10 horizontalAlignment: Text.AlignHCenter placeholderText: " " validator: RegExpValidator { regExp: /[--a-zA-Z]{16}/ } style: TextFieldStyle { background: Rectangle {color: "white"} textColor: "#00AAAA" placeholderTextColor: "#00EEEE" font: font.capitalization = Font.Capitalize, font.bold = true, font.pixelSize = mainWindow.height / 25 } Keys.onPressed: { if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return || event.key == Qt.Key_Back) { Qt.inputMethod.hide() loader.forceActiveFocus() event.accepted = true } } } Text { id: text y: textField.height width: parent.width height: parent.height / 10 font.pixelSize: height / 2 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap } ListView { id: lvList objectName: "lvList" y: text.y + text.height width: parent.width height: parent.height * 0.3 clip: true spacing: 8 model: ListModel { ListElement { name: " 0" } ListElement { name: " 1" } ListElement { name: " 2" } } delegate: Rectangle { width: lvList.width height: lvList.height / 3 color: "#00AAAA" Text { text: name } MouseArea { anchors.fill: parent onClicked: console.log("ListView el#" + index + " clicked!") } } } ListView { id: lvPager y: lvList.y + lvList.height width: parent.width height: parent.height * 0.2 clip: true model: ListModel { ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=Moscow" } ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=London" } ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=Rio" } } delegate: Image { width: lvPager.width height: lvPager.height source: map_url fillMode: Image.PreserveAspectFit } orientation: ListView.Horizontal snapMode: ListView.SnapOneItem } Loader { id: loader y: lvPager.y + lvPager.height width: parent.width height: parent.height * 0.2 focus: true source: "qrc:/QMLs/Loader1.qml" } Button { width: mainWindow.width / 4.5 height: mainWindow.height / 10 x: mainWindow.width / 2 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "Loader" } } onClicked: loader.setSource("qrc:/QMLs/Loader2.qml") } } 


url (& center) , . , :
画像

画像

«Loader» . Android- «Back», .

UI QML C++ .


3.3.
, QML- C++ , QML C++ , GET/POST- JSON-.

-, // , .

«qt_api», «test.php». :

 <? echo json_encode(json_decode( '{ "done" : { "boolean" : true, "number" : 123, "list" : [ "field1", "field2", "field3", "field4", "field5" ] } }' )); ?> 


JSON, «test.php» (http://localhost/qt_api/test.php) .

C++ Qt Creator: «Sample» -> « …» -> «C++» / « C++». «Backend», «QQuickItem» [. 4] , :
画像

«», «». «backend.h». :

 #ifndef BACKEND_H #define BACKEND_H #include <QQmlApplicationEngine> #include <QQuickItem> #include <QtQuick> #include <QNetworkAccessManager> class Backend : public QQuickItem { Q_OBJECT public: explicit Backend(QQuickItem *parent = 0); Q_INVOKABLE void makeRequest(int id); private: QQmlApplicationEngine engine; QObject * mainWindow; QObject * lvList; QObject * btnRequest; QNetworkAccessManager * namRequest; static const QString color_example; signals: private slots: void slotRequestFinished(QNetworkReply*); }; #endif // BACKEND_H 


.

. ( explicit). , , . Qt . , ( , , ), , Qt .

makeRequest. QML-, Q_INVOKABLE. id ( ).

(private) . QQmlApplicationEngine QML-. QObject QML [. 5] . QNetworkAccessManager (- DefaultHttpClient Android). , QML.

. , -. / — Qt, , , . :

 void mySignal(); 


:

 emit mySignal(); 


, ( connect()) . , , , - -. , namRequest. (slotRequestFinished(QNetworkReply*)) — - QNetworkAccessManager: , namRequest finished(QNetworkReply*), , (QNetworkReply).

«backend.cpp», :

 #include "backend.h" const QString Backend::color_example = "#000000"; Backend::Backend(QQuickItem *parent) : QQuickItem(parent) { engine.rootContext()->setContextProperty("color_example", color_example); engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); mainWindow = engine.rootObjects().value(0); lvList = mainWindow->findChild<QObject*>("lvList"); btnRequest = mainWindow->findChild<QObject*>("btnRequest"); engine.rootContext()->setContextProperty("backend", this); namRequest = new QNetworkAccessManager(this); connect(namRequest, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotRequestFinished(QNetworkReply*))); } void Backend::makeRequest(int id) { btnRequest->setProperty("enabled", "false"); // btnRequest->property("enabled"); QString prepareRequest("http://localhost/qt_api/test"); // HttpGet prepareRequest.append("?id="); prepareRequest.append(QString::number(id)); qDebug(prepareRequest.toUtf8()); QNetworkRequest request(QUrl(prepareRequest.toUtf8())); namRequest->get(request); // HttpPost /*QNetworkRequest request(QUrl(prepareRequest.toUtf8())); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); QString params("id="); params.append(QString::number(id)); qDebug(params.toUtf8()); namRequest->post(request, QByteArray(params.toUtf8()));*/ } void Backend::slotRequestFinished(QNetworkReply * reply) { if (reply->error() != QNetworkReply::NoError) { qDebug(reply->errorString().toUtf8()); } else { QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll()); QJsonObject jsonObj; QJsonValue jsonVal; QJsonArray jsonArr; jsonObj = jsonDoc.object(); jsonVal = jsonObj.value("done"); if (!jsonVal.isNull() && jsonVal.isObject()) { jsonObj = jsonVal.toObject(); jsonVal = jsonObj.value("number"); if (!jsonVal.isNull() && jsonVal.isDouble()) { qDebug(QString::number(jsonVal.toDouble(), 'f', 3).toUtf8()); } } if (jsonDoc.object().value("done").toObject().value("boolean").toBool()) { qDebug("json true"); } else { qDebug("json false"); } jsonArr = jsonDoc.object().value("done").toObject().value("list").toArray(); QMetaObject::invokeMethod(lvList, "clear"); for (int i=0; i<jsonArr.size(); i++) { QVariantMap map; map.insert("name", jsonArr.at(i).toString()); QMetaObject::invokeMethod(lvList, "append", Q_ARG(QVariant, QVariant::fromValue(map))); } } btnRequest->setProperty("enabled", "true"); reply->deleteLater(); } 


, . : .

QML- QQmlApplicationEngine.

QML:

 mainWindow = engine.rootObjects().value(0); 


objectName, QML. lvList:

 lvList = mainWindow->findChild<QObject*>("lvList"); 


«backend» Backend. ( Q_INVOKABLE-) QML:

 engine.rootContext()->setContextProperty("backend", this); 


«color_example» QML:

 engine.rootContext()->setContextProperty("color_example", color_example); 


namRequest:

 namRequest = new QNetworkAccessManager(this); 


finished(QNetworkReply*) slotRequestFinished(QNetworkReply*):

 connect(namRequest, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotRequestFinished(QNetworkReply*))); 


connect 4 :
— , ;
— ;
— , ( );
— , .

makeRequest. , :

 btnRequest->setProperty("enabled", "false"); 


QML ( ). QString:

 QString prepareRequest(«http://localhost/qt_api/test"); 


GET- , :

 prepareRequest.append("?id="); prepareRequest.append(QString::number(id)); 


int- ( makeRequest) QString. QString::number().

(QString QByteArray):

 qDebug(prepareRequest.toUtf8()); 


QNetworkRequest QNetworkAccessManager GET-:

 QNetworkRequest request(QUrl(prepareRequest.toUtf8())); namRequest->get(request); 


POST- , . , POST-, , CodeIgniter', «Disallowed Characters Error», .

, , .

, , , :

 if (reply->error() != QNetworkReply::NoError) { qDebug(reply->errorString().toUtf8()); } 


, . QJsonDocument JSON-. QJsonDocument QJsonDocument::fromJson(reply->readAll()), : reply->readAll(). , QNetworkReply, , ( - ).

JSON- QJsonDocument:

 jsonObj = jsonDoc.object(); 


QJsonValue «done» JSON-:

 jsonVal = jsonObj.value("done"); 


QJsonValue — JSON-, ( , , ..). QJsonValue . , QJsonValue JSON- QJsonValue ( «number») . , , QJsonValue double. «number» (3 ).
«boolean» «done» .

JSON- Qt: JSON- , ( Android, , JSONException). (, ), .

, Qt «» JSON-, , , JSON' .

QML JSON-, .

JSON- «list» QJsonArray:

 jsonArr = jsonDoc.object().value("done").toObject().value("list").toArray(); 


«clear» «lvList» ( «clear» ListView QML- ):

 QMetaObject::invokeMethod(lvList, "clear"); 


«lvList» JSON- — -, , «name» (, ListView?):

 QVariantMap map; map.insert("name", jsonArr.at(i).toString()); 


«append» «lvList» ( ):

 QMetaObject::invokeMethod(lvList, "append", Q_ARG(QVariant, QVariant::fromValue(map))); 


QMetaObject::invokeMethod() :
— — , ;
— — , «lvList»;
— — , «append», «map».

:

 btnRequest->setProperty("enabled", «true"); 


:

 reply->deleteLater(); 


. «main.cpp»:

 #include <QApplication> #include "backend.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); new Backend(); return app.exec(); } 


«Backend», .

«clear», «append» ListView («lvList») «main.qml»:

 function clear() { lvList.model.clear() } function append(newElement) { lvList.model.append(newElement) } 


C++ «append» ( ), .

:

 model: ListModel { //ListElement { name: " 0" } //ListElement { name: " 1" } //ListElement { name: " 2" } } 


, makeRequest() Backend:

 Button { objectName: "btnRequest" property int _id: 3 width: mainWindow.width / 4.5 height: mainWindow.height / 10 x: mainWindow.width - width y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground { border.color: color_example } label: ButtonLabel { text: "Request" } } onClicked: backend.makeRequest(_id) } 


«btnRequest», , C++ ,

 property int _id: 3 


— - QML-: «property», , . : id_._.

. -, :

 background: ButtonBackground { border.color: color_example } 


, «color_example» «Backend»? -, :

 onClicked: backend.makeRequest(_id) 


, , , ( property «_id», ).

«main.qml»:

 import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.2 import "QMLs" ApplicationWindow { id: mainWindow objectName: "mainWindow" visible: true width: 640 height: 480 color: "#F0F0FF" Button { function hello() { if (textField.text != "") { text.text = ", <b>" + textField.text.toUpperCase() + "</b>!" } } width: mainWindow.width / 2.5 height: mainWindow.height / 10 x: 0 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "" } } onClicked: hello() } TextField { id: textField width: parent.width height: parent.height / 10 horizontalAlignment: Text.AlignHCenter placeholderText: " " validator: RegExpValidator { regExp: /[--a-zA-Z]{16}/ } style: TextFieldStyle { background: Rectangle {color: "white"} textColor: "#00AAAA" placeholderTextColor: "#00EEEE" font: font.capitalization = Font.Capitalize, font.bold = true, font.pixelSize = mainWindow.height / 25 } Keys.onPressed: { if (event.key == Qt.Key_Enter || event.key == Qt.Key_Return || event.key == Qt.Key_Back) { Qt.inputMethod.hide() loader.forceActiveFocus() event.accepted = true } } } Text { id: text y: textField.height width: parent.width height: parent.height / 10 font.pixelSize: height / 2 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap } ListView { id: lvList objectName: "lvList" y: text.y + text.height width: parent.width height: parent.height * 0.3 clip: true spacing: 8 model: ListModel { //ListElement { name: " 0" } //ListElement { name: " 1" } //ListElement { name: " 2" } } delegate: Rectangle { width: lvList.width height: lvList.height / 3 color: "#00AAAA" Text { text: name } MouseArea { anchors.fill: parent onClicked: console.log("ListView el#" + index + " clicked!") } } function clear() { lvList.model.clear() } function append(newElement) { lvList.model.append(newElement) } } ListView { id: lvPager y: lvList.y + lvList.height width: parent.width height: parent.height * 0.2 clip: true model: ListModel { ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=Moscow" } ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=London" } ListElement { map_url: "http://maps.googleapis.com/maps/api/staticmap?size=640x320&scalse=2&sensor=false¢er=Rio" } } delegate: Image { width: lvPager.width height: lvPager.height source: map_url fillMode: Image.PreserveAspectFit } orientation: ListView.Horizontal snapMode: ListView.SnapOneItem } Loader { id: loader y: lvPager.y + lvPager.height width: parent.width height: parent.height * 0.2 focus: true source: "qrc:/QMLs/Loader1.qml" } Button { width: mainWindow.width / 4.5 height: mainWindow.height / 10 x: mainWindow.width / 2 y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground {} label: ButtonLabel { text: "Loader" } } onClicked: loader.setSource("qrc:/QMLs/Loader2.qml") } Button { objectName: "btnRequest" property int _id: 3 width: mainWindow.width / 4.5 height: mainWindow.height / 10 x: mainWindow.width - width y: mainWindow.height - height style: ButtonStyle { background: ButtonBackground { border.color: color_example } label: ButtonLabel { text: "Request" } } onClicked: backend.makeRequest(_id) } } 


, . «Request» ( iPhone ) :
画像

:

localhost/qt_api/test?id=3
123.000
json true


そうです。 :). (https://www.dropbox.com/s/to9kk0l71d6ma4h/Sample.zip).

, Qt:

 //   QSettings,   ,      QSettings settings("settings.ini", QSettings::IniFormat); //  ,      QString stringToSave; //     ,    QDate QDate date = QDate::currentDate(); //        stringToSave = date.toString("ddd-dd-MM-yyyy"); //   id   QUuid QUuid uniq_id = QUuid::createUuid(); //   id   stringToSave.append(" id="); stringToSave.append(uniq_id.toByteArray()); //      settings.setValue("value1", stringToSave); settings.sync(); //        qDebug(settings.value("value1").toString().toUtf8()); 


, . , — Android . — , ApplicationWindow [. 7] . — «Backend» QML :

 engine.rootContext()->setContextProperty("screen_width", this->width()); engine.rootContext()->setContextProperty("screen_height", this->height()); 


QML- «ApplicationWindow» :

 ApplicationWindow { . . . width: screen_width height: screen_height color: "#F0F0FF" . . . 


:). , . Android ( , , Qt, , ), . «» -> «Android» -> «».


4.
, , . Qt, , . , , - . — .

, , , - , .

, , — Qt , QtWebEngine (, , WebView ), BLE — qt-project.org/wiki/New-Features-in-Qt-5.4 .

Qt , . , Qt .

, , !

1. UI , .. «anchors». , , :
 anchors.bottom: some_element_id.top 

2. , -, property, -, / UI-, .. Screen.primaryOrientation ( QtQuick.Window 2.0).

3. ( «text:»), . , .

4. C++ , QObject. QQuickItem .

5. C++ QML ( UI- ). :
— :
 public: signals: void testSignal(); 

— - :
 emit testSignal(); 

— QML «Connections» «ApplicationWindow», C++:
 Connections{ target: backend; onTestSignal: console.log("emited!") } 

«target» — ContextProperty, , «onTestSignal:» — emit' «testSignal()».

6. «inputMethodHints» «Qt.ImhDialableCharactersOnly», .. «Qt.ImhPreferNumbers» .

7. UI «» Android QML ( ApplicationWindow) Screen.desktopAvailableWidth/Screen.desktopAvailableHeight ( QtQuick.Window 2.0).

8. :
 border { width: 2 color: "#FFFFFF" } 

Source: https://habr.com/ru/post/J231143/


All Articles