2012-06-12 42 views
0

我主要是一個.Net開發人員,並且一直在調查Qt一段時間。我現在正試圖在Qt中實現模型/視圖框架。我認爲我掌握了基本原理,但不清楚如何在更復雜的用戶界面中將小工具掛在一起,小工具需要相互溝通。鑑於以下內容:從嵌套對象集合開始Qt模型/視圖

// '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具有無效父索引)的知識?

回答

1

我發現你使用的術語有點尷尬,例如MyNestedClass1ViewModel只是一個模型。我不確定ViewModel會是什麼。

本示例中缺少的是實際視圖。 MyTreeViewWidget僅僅是一個愚蠢的小工具,其實並不是在所有的Qt方面的視圖,它是essdentialy只是一個愚蠢的「畫布」要在顯示的資料,因此要做到這一點的方法是:

  1. 您在普通對象(如NestedDomainModel2)中具有基礎數據。這些不是Qt意義上的模型,但我不會將它們命名爲這樣。它們只是普通的對象,並沒有實現任何MVC接口。

  2. MyNestedClass1ViewModel,它是一個Qt模型類。它在數據()和setData()方法的實現中訪問上面(1)中的底層數據對象。

  3. 從QAbstractItemView中分類的視圖類。這就是你想要的。它具有從上面(2)插入到模型類的API中的所有魔術掛鉤。它從模型中獲取信號,告訴它什麼時候發生了變化,它調用諸如dataChanged(),rowsInserted()之類的方法。您可以實現這些方法,以在第(4)點中的顯示小部件中進行適當的更改。

  4. 您的顯示小部件。它沒有實現任何模型/視圖API本身,並且由您的視圖進行更新。如果它是交互式的並且可以用來更改模型數據,則可以通過在模型上調用setData(),insertRows(),removeRows()等來實現。顯示更改將通過視圖自動傳播回小部件。要小心,不要產生變化來自小窗口>模型 - >查看 - >小窗口>模型 - >視圖傳播的無限循環等

我做了使用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) 

我希望有所幫助。