なんちゃって翻訳の下書きの下書きみたいなページ。
暇な時に時々更新。


■QtのQTouchEventのページの解説(Qt 4.8)

詳細説明

QTouchEventクラスは、タッチイベントを記述するパラメーターを含んでいます。

タッチイベントの有効化

タッチイベントは、タッチデバイス(タッチスクリーンやトラックパッドのような)上の一つ以上のタッチポイントのプレス、リリース、移動があるとき発生します。

タッチイベントを受け取るために、ウィジェットはQt::WA_AcceptTouchEventsアトリビュートを持つ必要があり、グラフィックスアイテムは、acceptTouchEventsアトリビュートをtrueにセットする必要があります。

QAbstractScrollAreaベースのウィジェットを使用する時は、スクロールエリアのビューポートのQt::WA_AcceptTouchEventアトリビュートを有効にするべきです。

QMouseEventのように、Qtは最初にプレスされたウィジェット内の各タッチポイントを自動的に捕捉し、ウィジェットはタッチポイントがリリースされるまでアップデートを受信します。

一つのウィジェットは複数のタッチポイントのイベントを受け取ることが可能で、複数のウィジェットは同時にタッチイベントを受信するかもしれないということに注意してください。

イベント処理

QEvent::TouchBegin、QEvent::TouchUpdate、QEvent::TouchEndがすべてのタッチイベントです。

タッチイベントを受け取るために、ウィジェットではQWidget::event()かQAbstractScrollArea::viewportEvent()を再実装します。

QEvent::TouchUpdateとQEvent::TouchEndイベントは、QEvent::TouchBeginイベントを受け入れたウィジェットまたはアイテムに送られます。

QEvent::TouchBeginイベントを受け入れないか、イベントフィルターによってフィルターされない場合、次のQEvent::TouchBeginまでタッチイベントは送られません。

touchPoints()関数はイベントに含まれたすべてのタッチポイントのリストを返します。

各タッチポイントについての情報は、QTouchEvent::TouchPointクラスを使って取得可能です。

Qt::TouchPointState enumは、タッチポイントが持ち得る様々な状態を記述しています。

イベントの配信と伝達

デフォルトでは、QWidget::event()はQMouseEvent内にQTouchEventの最初の非プライマリータッチポイントを変換します。

これによって通常QTouchEventを処理しない既存のウィジェットでタッチイベントを有効にすることができます。

これを行うときに必要な一部の特別に必要な考慮事項は次の情報を参照してください。

QEvent::TouchBeginはウィジェットへ送られるの最初のタッチイベントです。

QEvent::TouchBeginイベントは受信側がイベントを必要とするかどうかを示す特別な受け入れフラグを含んでいます。

デフォルトでは、イベントは受け入れられます。

あなたのウィジェットでタッチイベントを処理しない場合はignore()を呼び出すべきです。

QEvent::TouchBeginイベントはウィジェットがaccept()でそれを受け入れるか、イベントフィルターで処理されるまで親のウィジェットを伝って伝達されます。

QGraphicsItemsでは、QEvent::TouchBeginイベントはマウスの下のアイテムに伝達されます(QGraphicsItemsにマウスイベントが伝達されるのに似ています)。

タッチポイントのグルーピング

上で述べたように、幾つかのウィジェットはQTouchEventsを同時に受け取ることが可能です。

しかしながら、Qtは同じウィジェットに重複してQEvent::TouchBeginイベントを送らないことを確認しますが、例えば、ユーザーがQGroupBoxの2つの異なるウィジェットをタッチして両方のウィジェットのQEvent::TouchBeginイベントを無視した場合、理論的に伝達中に発生する可能性があります。

これを避けるために、Qtは新しいタッチポイントと共に次のルールを使ってグループ化します:

これは兄弟ウィジェットでタッチイベントを独立的に処理する間QTouchEventsのシーケンスが常に正しいことを保証します。

マウスイベントとプライマリータッチポイント

QTouchEventはQMouseEventからは独立して送られます。

一部のウィンドウシステム上では、マウスイベントは主タッチポイントのためにも送られます。

これはあなたのウィジェットが同じユーザー操作ポイントのためにQTouchEventとQMouseEvent両方を受け取ることが可能であることを意味しています。

あなたは主タッチポイントを識別するためにQTouchEvent::TouchPoint::isPrimary()関数を使用出来ます。

一部のシステム上では、主タッチイベントを除くタッチイベントを受信可能であることに注意してください。

これはすべてのQTouchEventのタッチポイントのためにマウスイベントが生成されないことを意味します。

注意

QTouchEvent::TouchPoint、Qt::TouchPointState、Qt::WA_AcceptTouchEvent、QGraphicsItem::acceptTouchEvent()も参照してください。


Gestures Programmingもついでに。(Qt 4.8)

ジェスチャープログラミング

Qtは独立した入力メソッドを使用し、一連のイベントからのフォームジェスチャのための機能を持つジェスチャプログラミングのためのフレームワークを含みます。
ジェスチャはマウスの移動、タッチスクリーンアクション、一部の他のソースからの一連のイベントを判定することができます。
自然な入力、ジェスチャの解釈と取るアクションは開発者の選択で行います。

概要

QGestureはユーザーによるジェスチャの実行についての情報のためのコンテナを提供する、Qtのジェスチャフレームワークの中心のクラスです。
QGestureはすべてのジェスチャに共通の一般的な情報を与えるプロパティを公開し、それらは追加のジェスチャ指定情報を提供して拡張することができます。
共通のパン、ピンチ、スワイプジェスチャは次の特別なクラスによって示されます: QPanGesture、QPinchGesture、QSwipeGesture。

開発者はQGestureRecognizerクラスをサブクラス化して拡張する事によって新しいジェスチャを実装することも可能です。
新しいジェスチャのサポートを追加することは入力イベントからジェスチャを認識するためのコード実装も含みます。
これはCreating Your Own Gesture Recognizerセクションで説明されます。

ウィジェットでの標準ジェスチャの使用

ジェスチャはQWidgetとQGraphicsObjectサブクラスのインスタンスで有効にすることができます。
入力ジェスチャを受け入れるオブジェクトはターゲットオブジェクトとしてドキュメントを通して言及されます。

ターゲットオブジェクトのためのジェスチャを有効にするために、要求されたジェスチャタイプを記述する引数でQWidget::grabGesture()かQGraphicsObject::grabGesture()関数を呼び出します。
標準のタイプはQt::GestureType列挙子によって定義され多くの一般的に使用されるジェスチャを含んでいます。

          grabGesture(Qt::PanGesture);
     grabGesture(Qt::PinchGesture);
     grabGesture(Qt::SwipeGesture);

上記のコードでは、ジェスチャはターゲットオブジェクト自身のコンストラクタでセットアップされます。

イベント処理

ユーザーがジェスチャを実行した時、QGestureEventイベントがターゲットオブジェクトに送られ、そしてこれらはウィジェットのための再実装されたQWidget::event()ハンドラ関数かグラフィックスオブジェクトのためのQGraphicsItem::sceneEvent()によって処理することが可能です。

一つのターゲットオブジェクトは複数のジェスチャタイプを記述できるので、QGestureEventは幾つかの可能なジェスチャが同時にアクティブなことを示す、複数のQGestureを含めることができます。
それはその後それらの複数のジェスチャの処理方法と幾つかの他の候補をキャンセルすべき候補をウィジェット上で決定します。

QGestureEventオブジェクト内に含まれた各QGestureは個別または全て一緒にaccepted()かignored()することが可能です。
加えて、幾つかのゲッターを使って個別のQGestureデータオブジェクト(状態)を問い合わせることが可能です。

イベント処理のための標準プロデューサー

QGestureはそれがあなたのウィジェットに届いた時デフォルトでは許可されます。
しかしながら、それはジェスチャを常に明示的に許可か拒否するためには良い方法です。
一般的なルールはあなたがジェスチャを受け入れる場合あなたはそれを使っているということです。
あなたがそれを無視している場合あなたはそれに興味がありません。
ジェスチャを無視することは別のターゲットオブジェクトへの以来や、キャンセルされたことを意味するかもしれません。

各QGestureは全体を通して幾つかの状態を持ちます; 状態を変化させるよくある手段があり、一般的にユーザー入力は状態変化を引き起こすが(インスタンスのための開始と停止操作によって)しかしウィジェットも状態変化を引き起こします。

最初に特定のQGestureがウィジェットかグラフィックスアイテムに送られたら、それはQt::GestureStarted状態になります。
あなたがこの時点でジェスチャを処理する方法はあなたがそれを後に対話するかどうかに影響します。

ジェスチャのキャンセルのためのQGesture::CancelAllInContextの使用はそれらを明示的に受け入れない限りすべての状態ですべてのジェスチャで発生します。
これは子供のアクティブジェスチャがキャンセルされることを意味します。
それはウィジェットがそれらを無視した場合同じQGestureEventで送られたジェスチャはキャンセルされることも意味しています。
これはあなたの興味のあるものを除いてすべてのジェスチャのフィルターアウトに便利な方法です。

イベント処理のサンプル

便宜上、Image Gesture Exampleは一般的なevent()ハンドラを再実装し専用のgestureEvent()関数にジェスチャイベントを委任します:

 bool ImageWidget::event(QEvent *event)
 {
     if (event->type() == QEvent::Gesture)
         return gestureEvent(static_cast<QGestureEvent*>(event));
     return QWidget::event(event);
 }

ターゲットオブジェクトに送られたジェスチャイベントは個々に調査され適切に処理されます:

 bool ImageWidget::gestureEvent(QGestureEvent *event)
 {
     if (QGesture *swipe = event->gesture(Qt::SwipeGesture))
         swipeTriggered(static_cast<QSwipeGesture *>(swipe));
      elseif (QGesture *pan = event->gesture(Qt::PanGesture))
         panTriggered(static_cast<QPanGesture *>(pan));
     if (QGesture *pinch = event->gesture(Qt::PinchGesture))
         pinchTriggered(static_cast<QPinchGesture *>(pinch));
      returntrue;
 }

ジェスチャへ応答することはターゲットオブジェクトに送られたQGestureEventないで送られたQGestureを取得しそれに含まれる情報を調べることです。

 void ImageWidget::swipeTriggered(QSwipeGesture *gesture)
 {
     if (gesture->state() == Qt::GestureFinished) {
         if (gesture->horizontalDirection() == QSwipeGesture::Left
             || gesture->verticalDirection() == QSwipeGesture::Up)
             goPrevImage();
         else
             goNextImage();
         update();
     }
 }

ここで、我々はユーザーがスワイプしたウィジェットの方向を調べて適切に内容を変更します。

あなた独自のジェスチャレコグナイザの作成

新しいジェスチャのためのサポートを追加することは新しいジェスチャレコグナイザの作成と登録を含みます。
ジェスチャのための認識プロセスに依存することは、新しいジェスチャオブジェクトの作成も含むかもしれません。

新しいレコグナイザの作成には、あなたはカスタムレコグナイザクラスを作成するためにQGestureRecognizerのサブクラス化が必要です。
あなたが再実装のしなければならない一つの仮想関数と、他に必要ならば再実装可能な他の二つの関数があります。

入力イベントのフィルタリング

recognize()関数は再実装しなければなりません。
この関数はターゲットオブジェクトへ入来する入力イベントの処理とフィルタを行いそれらがレコグナイザが探すジェスチャに対応するかどうかを決定します。

しかしジェスチャ認識のためのロジックはこの関数に実装され、Qt::GestureStateをベースにしたステートマシンを使用することが可能で、QGestureオブジェクトが供給された認識プロセスの状態についての情報を永続的に保持可能です。

あなたのrecognize()関数は与えられたジェスチャとターゲットオブジェクトのための認識状態を示すQGestureRecognizer::Resultの値を返さなければなりません。
これはジェスチャイベントがターゲットオブジェクトに送られるかどうかを決定します。

カスタムジェスチャ

あなたがカスタムQGestureサブクラスによってジェスチャの指定を選択する場合、あなたは標準のQGestureインスタンスの代わりにあなたのジェスチャクラスのインスタンスを構築するためにcreate()関数を再実装する必要があります。

あるいは、あなたは標準のQGestureのインスタンスの使用を望むならば、あなたが処理したいジェスチャの特定の詳細を公開するためにそれらに付加的なダイナミックプロパティを追加します。

ジェスチャのリセット

あなたがカスタムジェスチャオブジェクトのリセットかジェスチャがキャンセルされた時に特別な処理が必要な場合は、これらの特別な処理を実行するためにreset()関数を再実装する必要があります。

QGestureオブジェクトは各ターゲットオブジェクトとジェスチャタイプのために一度だけ作成され、それらはユーザーがターゲットオブジェクト上で同じジェスチャタイプの実行を試みる度に再利用されるかもしれない事に注意してください。
結果的に、それは以前の各ジェスチャ認識の試みの後にクリーンアップするためにreset()関数の再実装することが役立ちます。

新しいジェスチャレコグナイザの使用

ジェスチャレコグナイザを使うために、あなたのQGestureRecognizerサブクラスのインスタンスを構築し、QGestureRecognizer()でアプリケーションに登録します。
ジェスチャのタイプに与えられたレコグナイザはQGestureRecognizer::unregisterRecognizer()で削除することができます。

Further Reading
さらなる情報

Image Gesture Exampleは簡単なイメージビューアアプリケーションでウィジェットでのジェスチャを有効にする方法を示します。


Qt5のGetting Started Guidesもちょっと。


Contents

入門ガイド

Qtを使ったアプリケーション開発は簡単です

あなたの手順を助けるために、我々は異なるアプローチを使って、二つの同様なアプリケーション作成する二つのチュートリアルを作成しました。

始める前に、Qt SDKの商用体験版かオープンソース版をダウンロードして開発ツールに慣れてください。
Qt SDKはQtアプリケーションの開発を簡単で効果的にするためのQt Creatorを提供します。

ユーザーインターフェイスオプション

スレッドやネットワーク通信からビデオ再生やカメラアクセスまで直感的でクロスプラットフォームの抽象化のために、Qtは異なるユースケースで好みに合わせて二つの別の方法でのグラフィカルユーザーインターフェイスの作成を提供します。

Qt Quickモジュールは可変でアニメーションするユーザーインターフェイスの作成のための宣言型言語を提供し、例えばとても特徴的な外観とアニメーションするユーザーインターフェイス要素が必要なアプリケーションに適し、小さなスクリーンで実行されるアプリケーションやタッチ入力デバイスをターゲットにしたアプリケーションを意図しています。

Qt Widgetsモジュールはより伝統的なデスクトップアプリケーションのためのサポートを提供し、Max OS X、Windows、KDE、Gnomeなどの、ターゲットプラットフォームをシームレスに統合します。
それは多くのユーザーインターフェイスウィジェットのための既存のコンポーネントと堅牢で柔軟なC++ベースのクラスライブラリで、必要なときに新しい機能の拡張に適しています。

あなたのアプリケーションのベースにするためにどちらのモジュールを選択するかはあなたが作成したいアプリケーションのタイプに依存します。
両方のアプローチは残りのQtライブラリに統合するので、独立したアプリケーションロジックの大部分を記述可能です。
あなたが決定する前に次のチュートリアルで二つの方法に慣れてください。

さらなる詳細のために、User Interface概要を参照してください。

チュートリアル

二つの似たアプリケーションを作成するために二つの異なるチュートリアルがあります。
一つはQt Quickを使ってユーザーインファーフェイスを実装し、もうひとつはQt Widgetを使います。

始めるためには下のリンクの一つをクリックしてください。

Getting Started Programming with Qt Quick

Getting Started Programming with Qt Widgets


Qt Quickのチュートリアルページも。

Qt Quickでのプログラミング紹介

ようこそ宣言型UI言語QMLの世界へ。
この初歩のガイドでは、私たちはQMLを使ってシンプルなテキストエディタを作成します。
このガイドを読んだあと、あなたはQMLとQt C++を使ったあなた独自のアプリケーションの開発準備ができているでしょう。

ユーザーインターフェイス構築のためのQML

私たちの作成するアプリケーションはロード、セーブ、いくつかのテキスト操作を行うシンプルなテキストエディタです。
このガイドは二つの部分に分かれています。
最初のパートはQMLの宣言型言語を使ってアプリケーションのレイアウトと動作を設計を行います。
二番目のパートでは、Qt C++を使ってファイルのロードとセーブを実装します。
Qtのメタオブジェクトシステムを使って、私たちはQMLエレメントで使えるプロパティとしてC++関数を公開出来ます。
QMLとQt C++を利用することで、私たちはアプリケーションロジックからインターフェイスロジックを効果的に切り離すことができます。

最終的なソースコードはexamples/tutorials/gettingStartedQmlディレクトリにあります。
最初にexamples/tutorials/gettingStartedQml/filedialogのC++プラグインをコンパイルする必要があるかもしれません。
QMLファイルが見つけることができるディレクトリにC++プラグインを配置します。

テキストエディタを起動するためには、単にQMLファイルを引数としてqmlsceneツールを提供するだけです。
このチュートリアルのC++部は、標準的なQtのコンパイル手順を理解していることを前提にしています。

チュートリアルの章:

  1. ボタンとメニューの定義
  2. メニューバーの実装
  3. テキストエディタの構築
  4. テキストエディタの装飾
  5. Qt C++を使ったQMLの拡張

ボタンとメニューの定義

標準コンポーネント - ボタン

ボタンを構築することによってテキストエディターの作成を開始します。
機能的には、ボタンはマウスに反応する領域とラベルを持っています。
ボタンはユーザーがボタンを押下した時にアクションを実行します。

QMLでは、標準的な可視アイテムはRectangleエレメントです。
Rectangleエレメントは、エレメントの表示と位置を制御するためのプロパティを持っています。

import QtQuick 2.0

Rectangle {
    id: simplebutton
    color: "grey"
    width: 150; height: 75

    Text{
        id: buttonLabel
        anchors.centerIn: parent
        text: "button label"
    }
}

最初のimport QtQuick2.0は後に使用するqmlsceneツールがQMLエレメントをインポートすることを可能にします。
この行はすべてのQMLファイルにある必要があります。
Qtモジュールのバージョンがimportステートメントに含まれていることに注意してください。

このシンプルな矩形はidプロパティに結び付けられたsimplebuttonというユニークIDを持っています。
Rectanleエレメントのプロパティはコロンのあとに値が続くプロパティのリストによって値が結び付けられています。
サンプルのコードではグレーの色がRectangleのcolorプロパティに結び付けられています。
同様にRectangleのwidthとheightも結び付けられています。

Textエレメントは編集不可のテキスト領域です。
このTextエレメントにbuttonLabelと名前をつけます。
Text領域の文字列内容を設定するために、textプロパティに値を割り当てます。
ラベルはRectangle内に含まれ中央に配置され、よりシンプルなレイアウトを可能にする他のアイテムのアンカーにバインドできるsimplebutton.Anchorsと呼ばれる、Textエレメントからその親へのアンカーをアサインします。

このコードをSimpleButton.qmlとして保存しましょう。
ファイルを引数としてqmlsceneを実行するとグレーの矩形とテキストラベルが表示されるでしょう。

ボタンクリック機能を実装するためにQMLイベント処理を使うことができます。
QMLのイベント処理はQtのシグナルとスロットメカニズムにとても似ています。
シグナルがエミットされると接続されたストットを呼び出されます。

Rectangle{
    id:simplebutton
    ...

    MouseArea{
        id: buttonMouseArea

        anchors.fill: parent 
                //anchor all sides of the mouse area to the rectangle's anchors//onClicked handles valid mouse button clicks
        onClicked: console.log(buttonLabel.text + " clicked" )
    }
}

我々のsimplebuttonにMouseAreaエレメントを含めます。
MouseAreaエレメントはマウスの移動が検知されたことを通知するエリアを記述します。
我々のボタンのために、親であるsimplebuttonにMouseArea全体をアンカーします。
anchors.fill構文はanchorsと呼ばれるプロパティーのグループ内部のfillを呼び出して特別なプロパティにアクセスする一つの手段です。
QMLは堅牢なレイアウトを作成する、アイテムが別のアイテムへアンカー可能なにするアンカー-ベースレイアウトを使用します。

MouseAreaは指定されたMouseArea境界内にマウスでマウスが移動する間に呼び出される多くのシグナルハンドラを持っています。
その一つが許可したマウスボタンがクリックされた時に常に呼び出されるopnClickで、デフォルトで左クリックで呼び出されます。
私達はonClickハンドラにアクションを割り当てることができます。
この例では、マウスエリアがクリックされるたびにconsole.log()でテキストを出力します。
console.log()関数はデバッグのためのテキスト出力の便利なツールです。

SimpleButton.qmlのコードはスクリーンにボタンを表示しマウスがクリックされるたびにテキストを出力するのに十分です。

Rectangle {
    id: button
    ...

    property color buttonColor: "lightblue"
    property color onHoverColor: "gold"
    property color borderColor: "white"

    signal buttonClick()
    onButtonClick: {
        console.log(buttonLabel.text + " clicked" )
    }

    MouseArea{
        onClicked: buttonClick()
        hoverEnabled: true
        onEntered: parent.border.color = onHoverColor
        onExited:  parent.border.color = borderColor
    }

    //determines the color of the button by using the conditional operator
    color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
}

ボタンの完全な機能はButton.qmlにあります。
この記事のコード片は一部のコードを省略してあり、前のセクションで紹介済みなものや現在無関係な表記は省略してあります。

カスタムプロパティはプロパティタイプ名構文を使って宣言されます。
コードでは、color型のbuttonColorプロパティが宣言され"lightblue"の値が割り当てられています。
buttonColorはボタンの塗りつぶし色を決定するための条件操作で後に使用されます。
プロパティ値の割り当ては=記号を使うことで可能で、更に:文字を使って値を割り当てることが可能です。
カスタムプロパティは内部のアイテムがRectangleのスコープの外部にアクセスすることを可能にします。
標準のQML型にはint、string、real、及びvariantと呼ばれる型があります。

colorsにonEnteredとonExitedシグナルハンドラを結びつけることによって、ボタンのボーダーはボタン上をマウスが移動するときに黄色くなりマウスエリアをマウスが外れた時にcolorが元に戻ります。

A buttonClick() signal is declared in Button.qml by placing the signal keyword in front of the signal name. All signals have their handlers automatically created, their names starting with on. As a result, the onButtonClick is buttonClick's handler. The onButtonClick is then assigned an action to perform. In our button example, the onClicked mouse handler will simply call onButtonClick, which displays a text. The onButtonClick enables outside objects to access the Button's mouse area easily. For example, items may have more than one MouseArea declarations and a buttonClick signal can make the distinction between the several MouseArea signal handlers better.

We now have the basic knowledge to implement items in QML that can handle basic mouse movements. We created aText label inside a Rectangle, customized its properties, and implemented behaviors that respond to mouse movements. This idea of creating elements within elements is repeated throughout the text editor application.

This button is not useful unless used as a component to perform an action. In the next section, we will soon create a menu containing several of these buttons.

Creating a Menu Page

Up to this stage, we covered how to create elements and assign behaviors inside a single QML file. In this section, we will cover how to import QML elements and how to reuse some of the created components to build other components.

Menus display the contents of a list, each item having the ability to perform an action. In QML, we can create a menu in several ways. First, we will create a menu containing buttons which will eventually perform different actions. The menu code is in FileMenu.qml.

import QtQuick 2.0                \\import the main Qt QML module
import "folderName"            \\import the contents of the folder
import "script.js" as Script        \\import a Javascript file and name it as Script

The syntax shown above shows how to use the import keyword. This is required to use JavaScript files, or QML files that are not within the same directory. Since Button.qml is in the same directory as FileMenu.qml, we do not need to import the Button.qml file to use it. We can directly create a Button element by declaring Button{}, similar to a Rectangle{}declaration.

In FileMenu.qml:

    Row{
        anchors.centerIn: parent
        spacing: parent.width/6

        Button{
            id: loadButton
            buttonColor: "lightgrey"
            label: "Load"
        }
        Button{
            buttonColor: "grey"
            id: saveButton
            label: "Save"
        }
        Button{
            id: exitButton
            label: "Exit"
            buttonColor: "darkgrey"

            onButtonClick: Qt.quit()
        }
    }

In FileMenu.qml, we declare three Button elements. They are declared inside a Row element, a positioner that will position its children along a vertical row. The Button declaration resides in Button.qml, which is the same as theButton.qml we used in the previous section. New property bindings can be declared within the newly created buttons, effectively overwriting the properties set in Button.qml. The button called exitButton will quit and close the window when it is clicked. Note that the signal handler onButtonClick in Button.qml will be called in addition to theonButtonClick handler in exitButton.

The Row declaration is declared in a Rectangle, creating a rectangle container for the row of buttons. This additional rectangle creates an indirect way of organizing the row of buttons inside a menu.

The declaration of the edit menu is very similar at this stage. The menu has buttons that have the labels: Copy, Paste, and Select All.

Armed with our knowledge of importing and customizing previously made components, we may now combine these menu pages to create a menu bar, consisting of buttons to select the menu, and look at how we may structure data using QML.

Implementing a Menu Bar

Our text editor application will need a way to display menus using a menu bar. The menu bar will switch the different menus and the user can choose which menu to display. Menu switching implies that the menus need more structure than merely displaying them in a row. QML uses models and views to structure data and display the structured data.

Using Data Models and Views

QML has different data views that display data models. Our menu bar will display the menus in a list, with a header that displays a row of menu names. The list of menus are declared inside a VisualItemModel. The VisualItemModel element contains items that already have views such as Rectangle elements and imported UI elements. Other model types such as the ListModel element need a delegate to display their data.

We declare two visual items in the menuListModel, the FileMenu and the EditMenu. We customize the two menus and display them using a ListView. The MenuBar.qml file contains the QML declarations and a simple edit menu is defined inEditMenu.qml.

    VisualItemModel{
        id: menuListModel
        FileMenu{
            width: menuListView.width
            height: menuBar.height
            color: fileColor
        }
        EditMenu{
            color: editColor
            width:  menuListView.width
            height: menuBar.height
        }
    }

The ListView element will display a model according to a delegate. The delegate may declare the model items to display in a Row element or display the items in a grid. Our menuListModel already has visible items, therefore, we do not need to declare a delegate.

    ListView{
        id: menuListView

        //Anchors are set to react to window anchors
        anchors.fill:parent
        anchors.bottom: parent.bottom
        width:parent.width
        height: parent.height

        //the model contains the data
        model: menuListModel

        //control the movement of the menu switching
        snapMode: ListView.SnapOneItem
        orientation: ListView.Horizontal
        boundsBehavior: Flickable.StopAtBounds
        flickDeceleration: 5000
        highlightFollowsCurrentItem: true
        highlightMoveDuration:240
        highlightRangeMode: ListView.StrictlyEnforceRange
    }

Additionally, ListView inherits from Flickable, making the list respond to mouse drags and other gestures. The last portion of the code above sets Flickable properties to create the desired flicking movement to our view. In particular,the property highlightMoveDuration changes the duration of the flick transition. A higherhighlightMoveDuration value results in slower menu switching.

The ListView maintains the model items through an index and each visual item in the model is accessible through theindex, in the order of the declaration. Changing the currentIndex effectively changes the highlighted item in theListView. The header of our menu bar exemplify this effect. There are two buttons in a row, both changing the current menu when clicked. The fileButton changes the current menu to the file menu when clicked, the index being 0 becauseFileMenu is declared first in the menuListModel. Similarly, the editButton will change the current menu to the EditMenuwhen clicked.

The labelList rectangle has z value of 1, denoting that it is displayed at the front of the menu bar. Items with higher zvalues are displayed in front of items with lower z values. The default z value is 0.

    Rectangle{
        id: labelList
        ...
        z: 1
        Row{
            anchors.centerIn: parent
            spacing:40
            Button{
                label: "File"
                id: fileButton
                ...
                onButtonClick: menuListView.currentIndex = 0
            }
            Button{
                id: editButton
                label: "Edit"
                ...
                onButtonClick:    menuListView.currentIndex = 1
            }
        }
    }

The menu bar we just created can be flicked to access the menus or by clicking on the menu names at the top. Switching menu screens feel intuitive and responsive.

Building a Text Editor

Declaring a TextArea

Our text editor is not a text editor if it didn't contain an editable text area. QML's TextEdit element allows the declaration of a multi-line editable text area. TextEdit is different from a Text element, which doesn't allow the user to directly edit the text.

    TextEdit{
        id: textEditor
        anchors.fill:parent
        width:parent.width; height:parent.height
        color:"midnightblue"
        focus: true

        wrapMode: TextEdit.Wrap

        onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)
    }

The editor has its font color property set and set to wrap the text. The TextEdit area is inside a flickable area that will scroll the text if the text cursor is outside the visible area. The function ensureVisible() will check if the cursor rectangle is outside the visible boundaries and move the text area accordingly. QML uses Javascript syntax for its scripts, and as previously mentioned, Javascript files can be imported and used within a QML file.

    function ensureVisible(r){
        if (contentX >= r.x)
            contentX = r.x;
         elseif (contentX+width <= r.x+r.width)
            contentX = r.x+r.width-width;
        if (contentY >= r.y)
            contentY = r.y;
         elseif (contentY+height <= r.y+r.height)
            contentY = r.y+r.height-height;
    }

Combining Components for the Text Editor

We are now ready to create the layout of our text editor using QML. The text editor has two components, the menu bar we created and the text area. QML allows us to reuse components, therefore making our code simpler, by importing components and customizing when necessary. Our text editor splits the window into two; one-third of the screen is dedicated to the menu bar and two-thirds of the screen displays the text area. The menu bar is displayed in front of any other elements.

    Rectangle{

        id: screen
        width: 1000; height: 1000

        //the screen is partitioned into the MenuBar and TextArea. 1/3 of the screen is assigned to the MenuBar
        property int partition: height/3

        MenuBar{
            id:menuBar
            height: partition
            width:parent.width
            z: 1
        }

        TextArea{
            id:textArea
            anchors.bottom:parent.bottom
            y: partition
            color: "white"
            height: partition*2
            width:parent.width
        }
    }

By importing reusable components, our TextEditor code looks much simpler. We can then customize the main application, without worrying about properties that already have defined behaviors. Using this approach, application layouts and UI components can be created easily.

Decorating the Text Editor

Implementing a Drawer Interface

Our text editor looks simple and we need to decorate it. Using QML, we can declare transitions and animate our text editor. Our menu bar is occupying one-third of the screen and it would be nice to have it only appear when we want it.

We can add a drawer interface, that will contract or expand the menu bar when clicked. In our implementation, we have a thin rectangle that responds to mouse clicks. The drawer, as well as the application, has two sates: the "drawer is open" state and the "drawer is closed" state. The drawer item is a strip of rectangle with a small height. There is a nestedImage element declaring that an arrow icon will be centered inside the drawer. The drawer assigns a state to the whole application, with the identifier screen, whenever a user clicks the mouse area.

    Rectangle{
        id:drawer
        height:15

        Image{
            id: arrowIcon
            source: "images/arrow.png"
            anchors.horizontalCenter: parent.horizontalCenter
        }

        MouseArea{
            id: drawerMouseArea
            anchors.fill:parent
            onClicked:{
                if (screen.state == "DRAWER_CLOSED"){
                    screen.state = "DRAWER_OPEN"
                }
                 elseif (screen.state == "DRAWER_OPEN"){
                    screen.state = "DRAWER_CLOSED"
                }
            }
            ...
        }
    }

A state is simply a collection of configurations and it is declared in a State element. A list of states can be listed and bound to the states property. In our application, the two states are called DRAWER_CLOSED and DRAWER_OPEN. Item configurations are declared in PropertyChanges elements. In the DRAWER_OPEN state, there are four items that will receive property changes. The first target, menuBar, will change its y property to 0. Similarly, the textArea will lower to a new position when the state is DRAWER_OPEN. The textArea, the drawer, and the drawer's icon will undergo property changes to meet the current state.

    states:[
        State {
            name: "DRAWER_OPEN"
            PropertyChanges { target: menuBar; y: 0}
            PropertyChanges { target: textArea; y: partition + drawer.height}
            PropertyChanges { target: drawer; y: partition}
            PropertyChanges { target: arrowIcon; rotation: 180}
        },
        State {
            name: "DRAWER_CLOSED"
            PropertyChanges { target: menuBar; y:-height; }
            PropertyChanges { target: textArea; y: drawer.height; height: screen.height - drawer.height }
            PropertyChanges { target: drawer; y: 0 }
            PropertyChanges { target: arrowIcon; rotation: 0 }
        }
    ]

State changes are abrupt and needs smoother transitions. Transitions between states are defined using the Transitionelement, which can then bind to the item's transitions property. Our text editor has a state transition whenever the state changes to either DRAWER_OPEN or DRAWER_CLOSED. Importantly, the transition needs a from and a to state but for our transitions, we can use the wild card * symbol to denote that the transition applies to all state changes.

During transitions, we can assign animations to the property changes. Our menuBar switches position from y:0 to y:-partition and we can animate this transition using the NumberAnimation element. We declare that the targets' properties will animate for a certain duration of time and using a certain easing curve. An easing curve controls the animation rates and interpolation behavior during state transitions. The easing curve we chose is Easing.OutQuint, which slows the movement near the end of the animation. Please read QML's Animation article.

    transitions: [
        Transition {
            to: "*"
            NumberAnimation { target: textArea; properties: "y, height"; duration: 100; easing.type:Easing.OutExpo }
            NumberAnimation { target: menuBar; properties: "y"; duration: 100; easing.type: Easing.OutExpo }
            NumberAnimation { target: drawer; properties: "y"; duration: 100; easing.type: Easing.OutExpo }
        }
    ]

Another way of animating property changes is by declaring a Behavior element. A transition only works during state changes and Behavior can set an animation for a general property change. In the text editor, the arrow has aNumberAnimation animating its rotation property whenever the property changes.

In TextEditor.qml:

    Behavior{
        NumberAnimation{property: "rotation";easing.type: Easing.OutExpo }
    }

Going back to our components with knowledge of states and animations, we can improve the appearances of the components. In Button.qml, we can add color and scale property changes when the button is clicked. Color types are animated using ColorAnimation and numbers are animated using NumberAnimation. The on propertyName syntax displayed below is helpful when targeting a single property.

In Button.qml:
    ...

    color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
    Behavior on color { ColorAnimation{ duration: 55} }

    scale: buttonMouseArea.pressed ? 1.1 : 1.00
    Behavior on scale { NumberAnimation{ duration: 55} }

Additionally, we can enhance the appearances of our QML components by adding color effects such as gradients and opacity effects. Declaring a Gradient element will override the color property of the element. You may declare a color in the gradient using the GradientStop element. The gradient is positioned using a scale, between 0.0 and 1.0.

In MenuBar.qml
    gradient: Gradient {
        GradientStop { position: 0.0; color: "#8C8F8C" }
        GradientStop { position: 0.17; color: "#6A6D6A" }
        GradientStop { position: 0.98;color: "#3F3F3F" }
        GradientStop { position: 1.0; color: "#0e1B20" }
    }

This gradient is used by the menu bar to display a gradient simulating depth. The first color starts at 0.0 and the last color is at 1.0.

Where to Go from Here

We are finished building the user interface of a very simple text editor. Going forward, the user interface is complete, and we can implement the application logic using regular Qt and C++. QML works nicely as a prototyping tool, separating the application logic away from the UI design.

Extending QML using Qt C++

Now that we have our text editor layout, we may now implement the text editor functionalities in C++. Using QML with C++ enables us to create our application logic using Qt. We can create a QML context in a C++ application using Qt's Quick classes and display the QML elements using a QQuickView. Alternatively, we can export our C++ code into a plugin that the qmlscene tool can read. For our application, we shall implement the load and save functions in C++ and export it as a plugin. This way, we only need to load the QML file directly instead of running an executable.

Exposing C++ Classes to QML

We will be implementing file loading and saving using Qt and C++. C++ classes and functions can be used in QML by registering them. The class also needs to be compiled as a Qt plugin and the QML file will need to know where the plugin is located.

For our application, we need to create the following items:

  1. Directory class that will handle directory related operations
  2. File class which is a QObject, simulating the list of files in a directory
  3. plugin class that will register the class to the QML context
  4. Qt project file that will compile the plugin
  5. A qmldir file telling the qmlscene tool where to find the plugin

Building a Qt Plugin

To build a plugin, we need to set the following in a Qt project file. First, the necessary sources, headers, and Qt modules need to be added into our project file. All the C++ code and project files are in the filedialog directory.

In filedialog.pro:

    TEMPLATE = lib
    CONFIG += qt plugin
    QT += qml

    DESTDIR   +=../plugins
    OBJECTS_DIR = tmp
    MOC_DIR = tmp

    TARGET = FileDialog

    HEADERS +=     directory.h \
            file.h \
            dialogPlugin.h

    SOURCES +=    directory.cpp \
            file.cpp \
            dialogPlugin.cpp

In particular, we link the project with the qml module and configure it as a plugin, needing a lib template. We shall put the compiled plugin into the parent's plugins directory.

Registering a Class into QML

In dialogPlugin.h:

    #include <QtQml/QQmlExtensionPlugin>

    class DialogPlugin : public QQmlExtensionPlugin
    {
        Q_OBJECT
        Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")

    public:
        // registerTypes is inherited from QQmlExtensionPlugin
        void registerTypes(const char *uri);
    };

We need to export the plugin using the Q_PLUGIN_METADATA macro. Note that in our dialogPlugin.h file, we have theQ_OBJECT macro at the top of our class. As well, we need to run qmake on the project file to generate the necessary meta-object code.

Our plugin class, DialogPlugin is a subclass of QQmlExtensionPlugin. We need to implement the inherited function,registerTypes(). The dialogPlugin.cpp file looks like this:

DialogPlugin.cpp:

    
    
    
    #include "dialogPlugin.h"#include "directory.h"#include "file.h"#include <QtQml/qqml.h>

    void DialogPlugin::registerTypes(const char *uri)
    {
        //register the class Directory into QML as a "Directory" element version 1.0
        qmlRegisterType<Directory>(uri, 1, 0, "Directory");
        qmlRegisterType<File>(uri,1,0,"File");
    }

The registerTypes() function registers our File and Directory classes into QML. This function needs the class name for its template, a major version number, a minor version number, and a name for our classes.

Creating QML Properties in a C++ class

We can create QML elements and properties using C++ and Qt's Meta-Object System. We can implement properties using slots and signals, making Qt aware of these properties. These properties can then be used in QML.

For the text editor, we need to be able to load and save files. Typically, these features are contained in a file dialog. Fortunately, we can use QDir, QFile, and QTextStream to implement directory reading and input/output streams.

    class Directory : public QObject{

        Q_OBJECT

        Q_PROPERTY(int filesCount READ filesCount CONSTANT)
        Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)
        Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)
        Q_PROPERTY(QQmlListProperty<File> files READ files CONSTANT )

        ...

The Directory class uses Qt's Meta-Object System to register properties it needs to accomplish file handling. TheDirectory class is exported as a plugin and is useable in QML as the Directory element. Each of the listed properties using the Q_PROPERTY macro is a QML property.

The Q_PROPERTY declares a property as well as its read and write functions into Qt's Meta-Object System. For example, the filename property, of type QString, is readable using the filename() function and writable using the functionsetFilename(). Additionally, there is a signal associated to the filename property called filenameChanged(), which is emitted whenever the property changes. The read and write functions are declared as public in the header file.

Similarly, we have the other properties declared according to their uses. The filesCount property indicates the number of files in a directory. The filename property is set to the currently selected file's name and the loaded/saved file content is stored in fileContent property.

    Q_PROPERTY(QQmlListProperty<File> files READ files CONSTANT )

The files list property is a list of all the filtered files in a directory. The Directory class is implemented to filter out invalid text files; only files with a .txt extension are valid. Further, QLists can be used in QML files by declaring them as aQQmlListProperty in C++. The templated object needs to inherit from a QObject, therefore, the File class must also inherit from QObject. In the Directory class, the list of File objects is stored in a QList called m_fileList.

    class File : public QObject{

        Q_OBJECT
        Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

        ...
    };

The properties can then be used in QML as part of the Directory element's properties. Note that we do not have to create an identifier id property in our C++ code.

    Directory{
        id: directory

        filesCount
        filename
        fileContent
        files

        files[0].name
    }

Because QML uses Javascript's syntax and structure, we can iterate through the list of files and retrieve its properties. To retrieve the first file's name property, we can call files[0].name.

Regular C++ functions are also accessible from QML. The file loading and saving functions are implemented in C++ and declared using the Q_INVOKABLE macro. Alternatively, we can declare the functions as a slot and the functions will be accessible from QML.

In Directory.h:

    Q_INVOKABLE void saveFile();
    Q_INVOKABLE void loadFile();

The Directory class also has to notify other objects whenever the directory contents change. This feature is performed using a signal. As previously mentioned, QML signals have a corresponding handler with their names prepended withon. The signal is called directoryChanged and it is emitted whenever there is a directory refresh. The refresh simply reloads the directory contents and updates the list of valid files in the directory. QML items can then be notified by attaching an action to the onDirectoryChanged signal handler.

The list properties need to be explored further. This is because list properties use callbacks to access and modify the list contents. The list property is of type QQmlListProperty<File>. Whenever the list is accessed, the accessor function needs to return a QQmlListProperty<File>. The template type, File, needs to be a QObject derivative. Further, to create the QQmlListProperty, the list's accessor and modifiers need to be passed to the constructor as function pointers. The list, a QList in our case, also needs to be a list of File pointers.

The constructor of QQmlListProperty constructor and the Directory implementation:

    QQmlListProperty  ( QObject * object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 )
    QQmlListProperty<File>( this ,&m_fileList ,&appendFiles ,&filesSize ,&fileAt  ,&clearFilesPtr );

The constructor passes pointers to functions that will append the list, count the list, retrieve the item using an index, and empty the list. Only the append function is mandatory. Note that the function pointers must match the definition of AppendFunction, CountFunction, AtFunction, or ClearFunction.

    void appendFiles(QQmlListProperty<File >* property, File * file)
    File* fileAt(QQmlListProperty<File >* property, int index)
    int filesSize(QQmlListProperty<File >* property)
    void clearFilesPtr(QQmlListProperty<File >*property)

To simplify our file dialog, the Directory class filters out invalid text files, which are files that do not have a .txt extension. If a file name doesn't have the .txt extension, then it won't be seen in our file dialog. Also, the implementation makes sure that saved files have a .txt extension in the file name. Directory uses QTextStream to read the file and to output the file contents to a file.

With our Directory element, we can retrieve the files as a list, know how many text files is in the application directory, get the file's name and content as a string, and be notified whenever there are changes in the directory contents.

To build the plugin, run qmake on the filedialog.pro project file, then run make to build and transfer the plugin to theplugins directory.

Importing a Plugin in QML

The qmlscene tool imports files that are in the same directory as the application. We can also create a qmldir file containing the locations of QML files we wish to import. The qmldir file can also store locations of plugins and other resources.

In qmldir:

    Button ./Button.qml
    FileDialog ./FileDialog.qml
    TextArea ./TextArea.qml
    TextEditor ./TextEditor.qml
    EditMenu ./EditMenu.qml

    plugin FileDialog plugins

The plugin we just created is called FileDialog, as indicated by the TARGET field in the project file. The compiled plugin is in the plugins directory.

Integrating a File Dialog into the File Menu

Our FileMenu needs to display the FileDialog element, containing a list of the text files in a directory thus allowing the user to select the file by clicking on the list. We also need to assign the save, load, and new buttons to their respective actions. The FileMenu contains an editable text input to allow the user to type a file name using the keyboard.

The Directory element is used in the FileMenu.qml file and it notifies the FileDialog element that the directory refreshed its contents. This notification is performed in the signal handler, onDirectoryChanged.

In FileMenu.qml:

    Directory{
        id:directory
        filename: textInput.text
        onDirectoryChanged: fileDialog.notifyRefresh()
    }

Keeping with the simplicity of our application, the file dialog will always be visible and will not display invalid text files, which do not have a .txt extension to their filenames.

In FileDialog.qml:

    signal notifyRefresh()
    onNotifyRefresh: dirView.model = directory.files

The FileDialog element will display the contents of a directory by reading its list property called files. The files are used as the model of a GridView element, which displays data items in a grid according to a delegate. The delegate handles the appearance of the model and our file dialog will simply create a grid with text centered in the middle. Clicking on the file name will result in the appearance of a rectangle to highlight the file name. The FileDialog is notified whenever thenotifyRefresh signal is emitted, reloading the files in the directory.

In FileMenu.qml:

    Button{
        id: newButton
        label: "New"
        onButtonClick:{
            textArea.textContent = ""
        }
    }
    Button{
        id: loadButton
        label: "Load"
        onButtonClick:{
            directory.filename = textInput.text
            directory.loadFile()
            textArea.textContent = directory.fileContent
        }
    }
    Button{
        id: saveButton
        label: "Save"
        onButtonClick:{
            directory.fileContent = textArea.textContent
            directory.filename = textInput.text
            directory.saveFile()
        }
    }
    Button{
        id: exitButton
        label: "Exit"
        onButtonClick:{
            Qt.quit()
        }
    }

Our FileMenu can now connect to their respective actions. The saveButton will transfer the text from the TextEdit onto the directory's fileContent property, then copy its file name from the editable text input. Finally, the button calls thesaveFile() function, saving the file. The loadButton has a similar execution. Also, the New action will empty the contents of the TextEdit.

Further, the EditMenu buttons are connected to the TextEdit functions to copy, paste, and select all the text in the text editor.

Text Editor Completion

The application can function as a simple text editor, able to accept text and save the text into a file. The text editor can also load from a file and perform text manipulation.

Running the Text Editor

We need to compile the file dialog C++ plugin before the text editor can run. To compile, enter the filedialog directory, then run qmake and compile using make or nmake, depending on your platform. To run, launch qmlscene and open the texteditor.qml file.

The source code is in the examples/tutorials/gettingStartedQml directory.

inserted by FC2 system