Qt リテラシー - グラフィックス ビュー フレームワーク理論の概要

I. 概要

グラフィックス ビューは、多数のカスタム 2D グラフィックス アイテムを管理および操作するためのサーフェスと、これらのアイテムを視覚化し、ズームや回転をサポートするためのビュー ウィジェットを提供します。

このフレームワークには、シーン内のアイテム間の正確な倍精度対話を可能にするイベント伝播アーキテクチャが含まれています。要素は、キー イベント、マウス ダウン、移動、リリース、ダブルクリック イベントを処理でき、マウスの動きを追跡できます。

Graphics View は BSP (Binary Space Partitioning) ツリーを使用して非常に高速なアイテム検出を提供するため、数百万のアイテムであっても大規模なシーンをリアルタイムで視覚化できます。

Graphics View は、以前の QCanvas に代わって Qt 4.2 で導入されました。

2. グラフィックスビューのアーキテクチャ

Graphics View は、InterView のコンビニエンス クラス QTableView、QTreeView、QListView と同様に、モデル ビュー プログラミングに対する項目ベースのアプローチを提供します。複数のビューで、異なる幾何学的形状のアイテムを含む同じシーンを観察できます。

1. シーン

QGraphicsScene はグラフィックスビューのシーンを提供します。シーンの責任は次のとおりです。

  • 多数のアイテムを管理するための迅速なインターフェイスを提供します
  • イベントを各項目に伝播する
  • 選択やフォーカスの処理など、アイテムの状態を管理する
  • 変換されていないレンダリング機能を提供します。主に印刷機能に使用されます。

Scene は QGraphicsItem オブジェクトのコンテナとして機能します。アイテムは、QGraphicsScene::addItem() を呼び出すことによってシーンに追加され、その後、多くのアイテム検出関数の 1 つを呼び出すことによって取得されます。QGraphicsScene::items() とそのオーバーロードされた関数は、点、四角形、多角形、または一般的なベクトル パスに含まれる、または交差するすべての要素を返します。QGraphicsScene::itemAt() 指定された位置にある最上位の要素を返します。すべての項目検出関数は、降順に積み重ねられた項目を返します (つまり、最初に返された項目が一番上にあり、最後に返された項目が一番下にあります)。

  QGraphicsScene scene;
  QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100));

  QGraphicsItem *item = scene.itemAt(50, 50);
  // item == rect

QGraphicsScene のイベント伝播アーキテクチャは、アイテムに配信するシーン イベントをスケジュールし、アイテム間の伝播を管理します。シーンが特定の位置でマウス ダウン イベントを受信すると、シーンはその位置にある要素にイベントを渡します。

QGraphicsScene は、アイテムの選択やフォーカスなど、特定のアイテムの状態も管理します。QGraphicsScene::setSelectionArea() を呼び出し、任意の形状の引数を渡すことで、シーン内の要素を選択できます。この機能は、QGraphicsView でのラバーバンド選択の基礎としても使用されます。現在選択されているすべての項目のリストを取得するには、QGraphicsScene::selectedItems() を呼び出します。QGraphicsScene が処理するもう 1 つの状態は、項目にキーボード入力フォーカスがあるかどうかです。QGraphicsScene::setFocusItem() または QGraphicsItem::setFocus() を呼び出して項目にフォーカスを設定したり、QGraphicsScene::focusItem() を呼び出して現在フォーカスされている項目を取得したりできます。

最後に、QGraphicsScene では、QGraphicsScene::render() 関数を通じてシーンの一部を描画デバイスにレンダリングできます。

これにより、画像を出力するための基盤が提供されます。

2. ビュー

QGraphicsView は、シーンの内容を視覚化できるビュー コントロールを提供します。同じシーンに複数のビューを追加できるため、同じデータセットに複数のビューポートが提供されます。ビュー ウィジェットは、大きなシーン内を移動するためのスクロール バーを提供するスクロール領域です。OpenGL サポートを有効にするには、QGraphicsView::setViewport() を呼び出して QGLWidget をビューとして設定できます。

  QGraphicsScene scene;
  myPopulateScene(&scene);

  QGraphicsView view(&scene);
  view.show();

ビューはキーボードとマウスから入力イベントを受け取り、これらのイベントをシーン イベントに変換し (使用された座標をシーン座標に変換し)、イベントを視覚的なシーンに送信します。

変換行列 QGraphicsView::transform() を使用して、ビューはシーンの座標系を変換できます。ビューでは、ズームや回転などの高度なナビゲーション機能も利用できます。便宜上、QGraphicsView はビュー座標とシーン座標の間で変換するための関数、QGraphicsView::mapToScene() および QGraphicsView::mapFromScene() も提供します。
ここに画像の説明を挿入

3. グラフィック要素項目

QGraphicsItem は、Scene のグラフィック要素の基本クラスです。グラフィックスビューは、長方形 (QGraphicsItem)、楕円形 (QGraphicsEllipseItem)、テキスト項目 (QGraphicsTextItem) など、よく使用される形状の標準項目をいくつか提供しますが、カスタム項目を記述する場合は、最も強力な QGraphicsItem 機能を使用できます。この関数を継承するだけです。

QGraphicsItem は次の機能をサポートしています。

  • マウス ダウン、移動、リリース、ダブルクリック イベント、およびマウス オーバー、ホイール、コンテキスト メニュー イベント。
  • キーボード入力フォーカスとキーイベント
  • ドラッグ アンド ドロップ ドロップ アンド ドラッグ
  • グループ管理、親子関係、QGraphicsItemGroup のグループ化メソッド
  • 衝撃チェック

アイテムはローカル座標系に存在し、QGraphicsView と同様に、アイテムとシーン間の座標、およびアイテムとアイテム間の座標をマッピングするための多くの関数も提供します。また、QGraphicsView と同様に、行列 QGraphicsItem::transform() を使用して座標系を変換できます。これは、個々の要素を回転したり拡大縮小したりする場合に便利です。

アイテムには他のアイテム (サブアイテム) を含めることができます。親の変換はそのすべての子に継承されます。ただし、そのすべての関数 (QGraphicsItem::contains()、QGraphicsItem::boundingRect()、QGraphicsItem::collidesWith() など) は、要素の累積変換に関係なく、引き続きローカル座標で動作します。

QGraphicsItem は、QGraphicsItem::shape() 関数と QGraphicsItem::collidesWith() 関数による衝突検出をサポートします。どちらも仮想関数です。QGraphicsItem::shape() からローカル座標 QPainterPath としてアイテムの形状を返すことにより、QGraphicsItem はすべての衝突検出を処理します。ただし、独自の衝突検出を提供したい場合は、QGraphicsItem::colridesWith() を再実装できます。

ここに画像の説明を挿入

3. グラフィックビューの座標系

グラフィックス ビューはデカルト座標系に基づいています。シーン内のアイテムの位置とジオメトリは、x 座標と y 座標の 2 つの数値セットで表されます。変換されていないビューでシーンを表示すると、シーン内のユニットは画面上のピクセルで表されます。

注: グラフ ビューは Qt の座標系を使用するため、逆 y 軸座標系 (y が上に伸びる) はサポートされていません。

グラフィックス ビューでは、アイテム座標、シーン座標、ビュー座標の 3 つの座標系が使用できます。実装を簡素化するために、グラフィックス ビューには 3 つの座標系間でマッピングできる便利な関数が用意されています。

レンダリング時、グラフィックスビューのシーン座標は QPainter の論理座標に対応し、ビュー座標はデバイス座標と同じになります。座標系のドキュメントでは、論理座標とデバイス座標の関係について説明しています。

ここに画像の説明を挿入

1. プリミティブアイテムの座標

要素は独自のローカル座標系に存在します。それらの座標は通常、すべての変換の中心でもある中心点 (0,0) を中心としています。アイテム座標系の幾何学的プリミティブは、アイテム ポイント、アイテム ライン、またはアイテム長方形と呼ばれることがよくあります。

カスタム項目を作成する場合、考慮する必要があるのは項目の座標だけです。QGraphicsScene と QGraphicsView がすべての変換を実行します。これにより、カスタマイズの実装が非常に簡単になります。たとえば、マウスダウンまたはドラッグイン イベントを受信した場合、イベントの位置は要素の座標で指定されます。

QGraphicsItem::contains() 仮想関数は、ポイントがアイテム内にある場合は true を返し、それ以外の場合は false を返し、アイテム座標のポイント パラメーターを受け入れます。同様に、アイテムの外接する四角形と形状はアイテムの座標内にあります。

At アイテムの位置は、親の座標系におけるアイテムの中心点の座標であり、親の座標とも呼ばれます。この意味で、シーンは親のないすべてのアイテムの「親」とみなされます。最上位項目の位置はシーン座標にあります。

子要素の座標は親要素の座標を基準とします。子要素を変換しない場合、子要素の座標と親の座標の差は、親の座標におけるアイテム間の距離と同じになります。例: 変換されていない子が親の中心点に正確に配置されている場合、両方の子の座標系は同じになります。子ノードの位置が (10,0) の場合、子ノードの (0,10) 点は親ノードの (10,10) 点に対応します。

要素の位置と変換は親要素に対して相対的なものであるため、親の変換は暗黙的に子を変換しますが、子要素の座標は親の変換の影響を受けません。上の例では、親要素が回転および拡大縮小されても、子要素の (0,10) 点は依然として親要素の (10,10) 点に対応しています。ただし、Scene に関しては、子要素は親要素の変換と位置に従います。親要素がスケール (2x、2x) されている場合、子要素の位置はシーン座標 (20,0) にあり、その (10,0) 点はシーン内の (40,0) 点に対応します。 。

QGraphicsItem::pos() は数少ない例外の 1 つであり、QGraphicsItem の関数は、アイテムが何であるか、その親変換が何であるかに関係なく、アイテム座標で動作します。たとえば、アイテムの境界四角形 (つまり、QGraphicsItem::boundingRect()) は常にアイテムの座標で指定されます。

2. シーン シーン座標

シーンは、すべてのアイテムの基本座標系を表します。シーン座標系は、各トップレベル アイテムの位置を記述し、ビューからシーンに送信されるすべてのシーン イベントの基礎も形成します。シーン内の各アイテムには、ローカルのアイテムの位置と境界四角形に加えて、シーンの位置と境界四角形 (QGraphicsItem::scenePos()、QGraphicsItem::sceneBoundingRect()) があります。シーンの位置はシーン座標でのアイテムの位置を表し、そのシーンの境界四角形は、QGraphicsScene がシーンのどの領域が変更されたかを決定する方法の基礎を形成します。シーンの変更は QGraphicsScene::changed() シグナルを通じて伝達され、パラメーターはシーンの四角形のリストです。

3. ビュービュー座標

ビュー座標はコントロールの座標です。ビュー座標の各単位はピクセルに対応します。この座標系の特別な点は、それがウィジェット (またはビューポート) に対して相対的であり、観察されるシーンの影響を受けないことです。QGraphicsView のビューポートの左上隅は常に (0,0) であり、右下隅は常に (ビューポートの幅、ビューポートの高さ) です。すべてのマウス イベントとドラッグ アンド ドロップ イベントは、最初はビュー座標の形式で受信されるため、要素を操作するにはこれらの座標をシーンにマップする必要があります。

4. 座標マッピング

多くの場合、シーン内のアイテムを操作する場合、シーンからアイテムへ、あるアイテムから別のアイテムへ、またはビューからシーンへ、座標と任意の形状をマップすると便利です。たとえば、QGraphicsView のビューポートでマウスをクリックすると、QGraphicsView::mapToScene() を呼び出してから QGraphicsScene::itemAt() を呼び出すことで、カーソルの下にあるアイテムをシーンに問い合わせることができます。ビューポート内の項目がどこにあるかを知りたい場合は、項目に対して QGraphicsItem::mapToScene() を呼び出してから、ビューに対して QGraphicsView::mapFromScene() を呼び出します。最後に、ビュー楕円内の項目を検索したい場合は、QPainterPath を mapToScene() に渡してから、マップされたパスを QGraphicsScene::items() に渡すことができます。

QGraphicsItem::mapToScene() および QGraphicsItem::mapFromScene() を呼び出すことで、座標と形状をアイテムのシーンにマッピングできます。QGraphicsItem::mapToParent() および QGraphicsItem::mapFromParent() を呼び出して項目の親項目にマップしたり、QGraphicsItem::mapToItem() および QGraphicsItem::mapFromItem() を呼び出して項目間をマップしたりすることもできます。すべてのマッピング関数は、点、四角形、多角形、およびパスを同時にマッピングできます。

Sceneとのマッピング用のビューにも同様のマッピング機能があります。QGraphicsView::mapFromScene() と QGraphicsView::mapToScene() 。ビューからアイテムにマッピングするには、まずシーンにマッピングし、次にシーンからアイテムにマッピングします。

4. 主な特徴

1.拡大縮小と回転

QGraphicsView は、QGraphicsView::setMatrix() を通じて QPainter と同じアフィン変換をサポートします。ビューに変換を適用することで、ズームや回転などの一般的なナビゲーション機能のサポートを簡単に追加できます。
以下は、QGraphicsView のサブクラスでスケーリングと回転スロットを実装する方法の例です。

  class View : public QGraphicsView
  {
    
    
  Q_OBJECT
      ...
  public slots:
      void zoomIn() {
    
     scale(1.2, 1.2); }
      void zoomOut() {
    
     scale(1 / 1.2, 1 / 1.2); }
      void rotateLeft() {
    
     rotate(-10); }
      void rotateRight() {
    
     rotate(10); }
      ...
  };

スロットは、autoRepeat を有効にして QToolButton に接続できます。

QGraphicsView は、ビューを切り替えたときにビューの中心を揃えたままにします。

2. 印刷する

Graphics View は、レンダリング関数 QGraphicsScene::render() および QGraphicsView::render() を通じて単一行印刷を提供します。これらの関数は同じ API を提供します。QPainter をいずれかのレンダリング関数に渡すことで、Scene または View にコンテンツのすべてまたは一部を任意の描画デバイスにレンダリングさせることができます。この例では、QPrinter を使用してシーン全体を完全なページに印刷する方法を示します。

  QGraphicsScene scene;
  scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

  QPrinter printer;
  if (QPrintDialog(&printer).exec() == QDialog::Accepted) {
    
    
      QPainter painter(&printer);
      painter.setRenderHint(QPainter::Antialiasing);
      scene.render(&painter);
  }

シーン レンダー関数とビュー レンダー関数の違いは、一方はシーン座標で動作し、もう一方はビュー座標で動作することです。QGraphicsScene::render() は通常、幾何学データの描画やテキストドキュメントの印刷など、シーンのフラグメント全体を変換せずに印刷するために使用されます。一方、QGraphicsView::render() はスクリーンショットを撮るのに適しており、そのデフォルトの動作は、提供されたペインタを使用してコントロールのウィンドウの外側にコンテンツをレンダリングすることです。

  QGraphicsScene scene;
  scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

  QPixmap pixmap;
  QPainter painter(&pixmap);
  painter.setRenderHint(QPainter::Antialiasing);
  scene.render(&painter);
  painter.end();

  pixmap.save("scene.png");

ソース領域とターゲット領域のサイズが一致しない場合、ソース領域のコンテンツはターゲット領域に合わせて引き伸ばされます。使用しているレンダリング関数に Qt::AspectRatioMode を渡すことで、コンテンツが引き伸ばされている間、シーンのアスペクト比を維持するか無視するかを選択できます。

3. ドラッグアンドドロップ

QGraphicsView は QWidget を間接的に継承するため、QWidget が提供するのと同じドラッグ アンド ドロップ機能をすでに提供しています。さらに、便宜上、グラフィック ビュー フレームワークはシーンと各アイテムのドラッグ アンド ドロップ サポートを提供します。ビューがドラッグを受け取ると、ドラッグ ドロップ イベントを QGraphicsSceneDragDropEvent に変換し、それがシーンに転送されます。シーンはこのイベントのスケジュールを引き継ぎ、ドロップを受け入れるマウス カーソルの下にある最初のアイテムにイベントを送信します。

要素からドラッグを開始するには、QDrag オブジェクトを作成し、ドラッグを開始するウィジェットにポインタを渡します。複数のビューが同時にアイテムを観察できますが、ドラッグを開始できるのは 1 つのビューだけです。ほとんどの場合、ドラッグはマウスを押すか動かすことによって開始されるため、mousePressEvent() または MouseMoveEvent() でイベントからウィジェットの初期ポインターを取得できます。例えば:

  void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
  {
    
    
      QMimeData *data = new QMimeData;
      data->setColor(Qt::green);

      QDrag *drag = new QDrag(event->widget());
      drag->setMimeData(data);
      drag->start();
  }

シーンのドラッグ アンド ドロップ イベントをインターセプトするには、QGraphicsScene::dragEnterEvent() と、QGraphicsItem サブクラスの特定のシーンに必要なイベント ハンドラーを再実装する必要があります。グラフィックス ビューのドラッグ アンド ドロップの詳細については、各 QGraphicsScene のイベント ハンドラーのドキュメントを参照してください。

QGraphicsItem::setAcceptDrops() を呼び出すことで、項目をドラッグ アンド ドロップできます。受信ドラッグを処理するには、QGraphicsItem::dragEnterEvent()、QGraphicsItem::dragMoveEvent()、QGraphicsItem::dragLeaveEvent()、および QGraphicsItem::dropEvent() を再実装します。

4. マウスポインタとヒント

QWidget と同様に、QGraphicsItem はマウス (QGraphicsItem::setCursor()) とツールチップ (QGraphicsItem::setToolTip()) もサポートします。マウス カーソルが項目領域に入ると (QGraphicsItem::contains() の呼び出しによって検出されます)、QGraphicsView はマウス ポインターとツールチップをアクティブにします。
QGraphicsView::setCursor() を呼び出して、ビュー上にデフォルトのマウス ポインターを直接設定することもできます。

5.アニメーション

グラフ ビューは複数レベルのアニメーションをサポートします。アニメーション フレームワークを使用すると、アニメーションを簡単に組み立てることができます。これを行うには、アイテムが QGraphicsObject を継承し、QPropertyAnimation をアイテムに関連付ける必要があります。QPropertyAnimation を使用すると、任意の QObject プロパティをアニメーション化できます。
別のオプションは、QObject と QGraphicsItem を継承するカスタム項目を作成することです。アイテムは独自のタイマーを設定し、QObject::timerEvent() で増分ステップでアニメーションを制御できます。

3 番目のオプションは、QGraphicsScene::advance() を呼び出してシーンを進めることです。これは、主に Qt 3 の QCanvas との互換性のために、QGraphicsItem::advance() を呼び出します。

6. OpenGL レンダリング

OpenGL レンダリングを有効にするには、QGraphicsView::setViewport() を呼び出して、新しい QGLWidget を QGraphicsView のビューポートとして設定するだけです。OpenGL アンチエイリアスが必要な場合は、OpenGL サンプル バッファーのサポートが必要です (QGLFormat::sampleBuffers() を参照)。

  QGraphicsView view(&scene);
  view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));

7. アイテム群

項目を別の項目の子にすることで、項目グループ化の最も基本的なプロパティを実現できます。つまり、すべての項目が一緒に移動し、すべての変換を親から子に伝播できます。

さらに、QGraphicsItemGroup は、グループに項目を追加したり、グループから項目を削除したりするための便利なインターフェイスと子イベント処理を組み合わせた特別な項目です。QGraphicsItemGroup に要素を追加すると、要素の元の位置と変換が維持されますが、要素を再配置すると、通常、新しい親を基準にして子要素の位置が変更されます。便宜上、シーンから QGraphicsScene::createItemGroup() を呼び出して QGraphicsItemGroups を作成できます。

8. 部品とレイアウト

Qt 4.4 では、qgraphicwidget を介してジオメトリとレイアウトを認識するアイテムのサポートが導入されました。この特別なベース項目は QWidget に似ていますが、QWidget とは異なり、QPaintDevice から継承せず、QGraphicsItem から派生しません。これにより、イベント、シグナルとスロット、サイズのヒントとポリシーを備えた完全なウィジェットを作成できるようになり、QGraphicsLinearLayout と QGraphicsGridLayout を介してレイアウト内のウィジェットのジオメトリを管理することもできます。

1.Qグラフィックスウィジェット

QGraphicsItem の機能と必要最低限​​の機能を基にして、QGraphicsWidget は両方の長所を提供します。スタイル、フォント、カラー パレット、レイアウト方向とそのジオメトリなどの QWidget の追加機能、および QGraphicsItem からの解像度の独立性と移行サポートです。グラフィックスビューは整数ではなく実際の座標を使用するため、QGraphicsWidget のジオメトリ関数は QRectF および QPointF 上でも動作できます。これはフレームの四角形、マージン、間隔にも当てはまります。たとえば、QGraphicsWidget を使用する場合、コンテンツのマージンを (0.5,0.5,0.5,0.5) として指定するのが非常に一般的です。子ウィンドウ コンポーネントと「トップレベル」ウィンドウの両方を作成でき、場合によっては、高度な MDI アプリケーションにグラフィカル ビューを使用できるようになりました。

ウィンドウ フラグや属性など、QWidget の一部のプロパティはサポートされていますが、すべてではありません。サポートされるものとサポートされないものの完全な概要については、QGraphicWidget クラスのドキュメントを参照してください。たとえば、Qt::Window ウィンドウ フラグを qgraphicwidget コンストラクターに渡すことで装飾ウィンドウを作成できますが、グラフィック ビューは現在、macOS 上の共通の Qt::Sheet フラグと Qt::Drawer フラグをサポートしていません。

2.QGraphicsLayout

QGraphicsLayout は、qgraphicwidget 用に特別に設計された第 2 世代のレイアウト フレームワークの一部です。その API は QLayout に非常に似ています。QGraphicsLinearLayout と QGraphicsGridLayout でウィジェットとサブレイアウトを管理できます。また、QGraphicsLaytItem を自分で継承して独自のレイアウトを簡単に記述したり、QGraphicsLayoutItem のアダプター サブクラスを記述して独自の QGraphicsItem アイテムをレイアウトに追加したりすることもできます。

9. ウィジェットの埋め込みのサポート

グラフィックス ビューは、シーンにコントロールを埋め込むためのシームレスなサポートを提供します。QLineEdit や QPushButton などの単純なコントロール、QTabWidget などの複雑なコントロール、さらには完全なメイン ウィンドウを埋め込むこともできます。ウィジェットをシーンに埋め込むには、QGraphicsScene::addWidget() を呼び出すか、QGraphicsProxyWidget のインスタンスを作成してウィジェットを手動で埋め込みます。

QGraphicsProxyWidget を通じて、Graphics View は、カーソル、ツールチップ、マウス、タブレット、キーボードのイベント、子ウィジェット、アニメーション、ポップアップ ウィンドウ (QComboBox や QCompleter など)、ウィジェットの入力フォーカスとアクティブ化を含むクライアント ウィジェットの機能を深く統合できます。QGraphicsProxyWidget は埋め込みウィジェットのタブ オーダーも統合しているので、埋め込みウィジェットにタブで出入りできるようになります。新しい QGraphicsView をシーンに埋め込んで、複雑なネストされたシーンを提供することもできます。

埋め込みコントロールを変換する場合、グラフィックス ビューはコントロールが独立して解像度を変換することを保証し、ズームインしてもフォントとスタイルが読みやすい状態を維持できるようにします。(解像度非依存の効果はスタイルによって異なりますのでご注意ください。)

5. パフォーマンス

要素に変換と特殊効果を正確かつ迅速に適用するために、グラフィックス ビューは、ユーザーのハードウェアが浮動小数点命令に対して適切なパフォーマンスを提供できることを前提として構築されています。
多くのワークステーションやデスクトップ コンピュータには、このような計算を高速化するための適切なハードウェアが装備されていますが、一部の組み込みデバイスは、ソフトウェアで数学演算を処理したり、浮動小数点命令をエミュレートするためのライブラリのみを提供する場合があります。

したがって、一部のデバイスでは、一部の種類のエフェクトが予想よりも遅くなる可能性があります。このパフォーマンスの低下は、OpenGL を使用してシーンをレンダリングするなど、他の方法で最適化することで補うことができます。ただし、この最適化自体も浮動小数点ハードウェアに依存している場合、パフォーマンスが低下します。

おすすめ

転載: blog.csdn.net/qq_43680827/article/details/132252105
おすすめ