0

私は主に .Net 開発者で、しばらく Qt を調査しています。私は現在、モデル/ビュー フレームワークを Qt に実装しようとしている段階です。私は基本的な原則を把握していると思いますが、ウィジェットが相互に通信する必要があるより複雑な UI で物事をまとめる方法が不明です。以下を考えると:

// 'domain' model classes
class NestedDomainModel1
{
public:
  NestedDomainModel1();

  QString name() const;
  void setName(const QString& newName);

  // other properties

private:
  QString m_name;
};

class NestedDomainModel2
{
public:
  NestedDomainModel2();

  QString name() const;
  void setName(const QString& newName);

  // other properties
};

class MyDomainModel
{
public:
  MyDomainModel();

  void addNestedModel1(const NestedDomainModel1& modelToAdd);
  NestedDomainModel& nestedObjectModel1At(int index);
  int nestedObjectModel1Count() const;

  // repeat for model 2


private:

  QList<NestedDomainModel1> m_nestedModels1;
  QList<NestedDomainModel2> m_nestedModels2;
};


// 'GUI' classes
class MainWindow : public QMainWindow
{

private:
  MyDomainModel* m_model;
  MyTreeViewWidget* m_treeWidget;  // -> this sits in a left dock window
  MyInfoDisplayWidget* m_infoWidget; // -> this sits in a right dock window and display details about the item selected in the tree
};

class MyDomainModelTreeModel : public QAbstractItemModel
{
public:
  explicit MyDomainModelTreeModel(MyDomainModel* model);

  // required overrides for QAbstractItemModel
private:
  MyDomainModel* m_model;
};

class MyTreeViewWidget : public QWidget
{
public:
  // Take a pointer to the domain model and create a model for the 'view'.
  // Will create a tree like:
  // Nested Objects 1
  //   |- object 001
  //   |- object 002
  //   |- you get the idea
  // Nested Objects 2
  //   |- other object 001
  //   |- more of the same
  explicit MyTreeViewWidget(MyDomainModel* model);

public slots:  
  // Used to notify widget when an item is added to the underlying model.
  void nestedModel1Added(); 
  void nestedModel2Added(); 

signals:
  void nestedModel1Selected(NestedDomainModel1& selectedModel);  
  void nestedModel2Selected(NestedDomainModel2& selectedModel);

private slots:
  // connect to tree view event when an item is selected and if all ok, emit one of the selected events
  void onTreeItemSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);  

private:
  QTreeView* m_treeView;
  MyDomainModelTreeModel* m_treeModel;
};

class MyNestedClass1ViewModel : QAbstractItemModel
{
public:
  explicit MyNestedClass1ViewModel(NestedDomainModel1* model);

  setModel(NestedDomainModel1* model);

  // required overrides for QAbstractItemModel

private:
  NestedDomainModel1* m_model
};

class MyInfoDisplayWidget : public QWidget
{
public:
  explicit MyInfoDisplayWidget(QWidget* parent = 0);

public slots:
  // this is connected to the 'tree' widget signal in MainWindow
  void setModel(NestedDomainModel1& selectedModel);
};

UI の基本的な前提は、Visual Studio に似た感覚です。ツリーはソリューション エクスプローラーに似ており、「情報表示」はプロパティ ウィンドウに似ています。

  1. これがモデル/ビュー フレームワークの使い方ですか? WPF/Silverlight 開発に精通している人にとって、モデル/ビュー フレームワークは、「ビューのモデル」であり、ドメイン モデルをラップ/含むという点で (高レベルで) MVVM に似ていますか?

  2. これは、モデル/ビュー フレームワークを使用してウィジェットを接続する方法ですか (つまり、あるウィジェットがモデルのポインターまたは参照を別のウィジェットに渡します)。または、SelectionModel を使用する必要がありますか? ツリー モデルにはさまざまな種類のオブジェクトが含まれているため、それは機能しますか?

  3. ルートノードをどのように識別しますか? たとえば、MyNestedObject1 が作成され、ツリーに追加する必要がある場合、ルート ノードがモデル インデックス QModelIndex(0, 0) (つまり、無効な親インデックスを持つ行 0) にあるという知識に依存しますか?

4

1 に答える 1

1

あなたが使用している用語が少しぎこちないことがわかりました。たとえば、 MyNestedClass1ViewModel は単なるモデルです。ViewModel がどうなるかわかりません。

この例で欠けているのは、実際のビューです。MyTreeViewWidget は、実際には Qt 用語のビューではないただのダム ウィジェットです。本質的には、データを表示したいだけのダム 'キャンバス' です。したがって、これを行う方法は次のとおりです。

  1. NestedDomainModel2 などの通常のオブジェクトに基になるデータがあります。ただし、これらは Qt の意味でのモデルではなく、そのような名前は付けません。それらは単なる通常のオブジェクトであり、MVC インターフェイスを実装していません。

  2. Qt モデル クラスである MyNestedClass1ViewModel。その data() および setData() メソッドの実装で、上記 (1) の基礎となるデータ オブジェクトにアクセスします。

  3. QAbstractItemView からサブクラス化されたビュー クラス。これはあなたが見逃しているものです。上記 (2) のモデル クラスの API にプラグインするためのマジック フックがすべて含まれています。変更があったことを伝えるモデルからシグナルを取得し、dataChanged()、rowsInserted() などのメソッドを呼び出します。これらのメソッドを実装して、以下のポイント (4) で表示ウィジェットに適切な変更を加えます。

  4. 表示ウィジェット。モデル/ビュー API 自体は実装されておらず、ビューによって更新されます。インタラクティブで、モデル データの変更に使用できる場合は、モデルで setData()、insertRows()、removeRows() などを呼び出してそれを行います。表示の変更は、ビューを介してウィジェットに自動的に反映されます。widget->model->view->widget->model->view などから伝播する変更の無限ループを生成しないように注意してください。

QGraphicsScene/QGraphicsView を使用してモデル内のアイテムを表示するために同様のことを行いました。その名前にもかかわらず、QGraphicsView はモデル/ビュー フレームワークの一部ではないため、QGraphicsScene でモデル データを描画するカスタム ビュー クラスを実装しました。

これがPythonでの私のコードです。SF ウォーゲームのマップ上に世界を描画します。

class WorldItemView(QtGui.QAbstractItemView):
""" Hidden view which interfaces between the model and the scene.
"""
def __init__(self, model, parent=None):
    QtGui.QAbstractItemView.__init__(self, parent)
    self.hide()
    self.setModel(model)
    self.my_model = model
    self.scene = MapScene(self.my_model)
    self.resetWorlds()

def dataChanged(self, topLeft, bottomRight):
    top_row = topLeft.row()
    bottom_row = bottomRight.row()
    #debug_log("Top row " + str(top_row) + " Bottom row " + str(bottom_row))
    for row in range(top_row, (bottom_row + 1)):
        self.scene.worldChanged(row)

def rowsInserted(self, parent, start, end):
    for row in range(start, (end + 1) ):
        pmi = self.my_model.getPMI(row)
        self.scene.insertWorld(pmi)

def rowsAboutToBeRemoved(self, parent, start, end):
    for row in range(start, (end + 1)):
        self.scene.removeWorld(row)

def resetWorlds(self):
    self.scene.clearWorlds()
    # Add worlds to scene
    last_row = self.my_model.rowCount() - 1
    self.rowsInserted(None, 0, last_row)

お役に立てば幸いです。

于 2012-06-28T08:41:42.450 に答える