2012-06-12 3 views
0

주로 .Net 개발자이며 잠시 동안 Qt를 조사하고 있습니다. 이제는 Qt에서 모델/뷰 프레임 워크를 구현하려는 단계에 있습니다. 기본 원칙을 이해하고 있지만 위젯이 서로 통신해야하는보다 복잡한 UI에서 함께 작업하는 방법이 불분명하다고 생각합니다. 다음을 감안할 때 :중첩 객체 컬렉션이있는 시작 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); 
}; 

Visual Studio의 기본 기능은 Visual Studio와 비슷합니다. 트리는 솔루션 탐색기와 유사하며 '정보 표시'는 속성 창과 유사합니다.

  1. 모델/뷰 프레임 워크를 어떻게 사용합니까? WPF/Silverlight 개발에 익숙한 사람들은 '보기의 모델'이며 도메인 모델을 포함한다는 점에서 모델/뷰 프레임 워크가 MVVM과 유사합니다 (상위 수준).

  2. 모델/뷰 프레임 워크를 사용하여 위젯을 연결하는 방법입니다 (즉, 하나의 위젯이 모델의 포인터 또는 참조를 다른 모델로 전달합니다)? 아니면 SelectionModel을 사용해야합니까? 트리 모델에 다른 유형의 객체가 포함되어 있기 때문에이 작업이 가능합니까?

  3. 어떻게 루트 노드를 식별합니까? 예를 들어, MyNestedObject1이 생성되어 트리에 추가되어야 할 때, 루트 노드가 모델 인덱스 QModelIndex (0, 0) (즉, 부모 인덱스가 유효하지 않은 행 0)에 있다는 지식에 의존합니까?

답변

1

MyNestedClass1ViewModel이 약간 모델을 사용하는 것과 같이 약간 어색한 용어를 찾고 있습니다. ViewModel이 무엇인지 확신 할 수 없습니다.

이 예에서 누락 된 부분은 실제보기입니다. MyTreeViewWidget는 그냥 essdentialy 당신이 데이터를 표시 할 바보 '캔버스'의 모든에서 Qt는 측면에서보기가 실제로없는 단지 바보 위젯입니다이 작업을 수행 할 수있는 방법입니다 그래서 :.

  1. NestedDomainModel2와 같은 일반 객체에 기본 데이터가 있습니다. 이것들은 Qt의 의미에서의 모델이 아니기 때문에 저는 그것들을 그렇게 부르지 않을 것입니다. 그것들은 단지 일반적인 객체이며 MVC 인터페이스를 구현하지 않습니다.

  2. Qt 모델 클래스 인 MyNestedClass1ViewModel. 그것은 data() 및 setData() 메소드의 구현에서 위의 기본 데이터 객체 (1)에 액세스합니다.

  3. QAbstractItemView에서 서브 클래 싱 된 뷰 클래스. 이것이 당신이 놓친 것입니다. 위의 (2)에서 모델 클래스의 API에 연결하는 모든 마법의 고리가 있습니다. modelChanged(), rowsInserted()와 같은 메소드를 호출하는 변경된 신호를 모델에서 가져옵니다. 이러한 메소드를 구현하여 포인트 (4) 아래에서 디스플레이 위젯을 적절하게 변경합니다.

  4. 표시 위젯. 모델 /보기 API 자체를 구현하지 않으며보기에 의해 업데이트됩니다. 대화 형이고 모델 데이터를 변경하는 데 사용할 수있는 경우 모델에서 setData(), insertRows(), removeRows() 등을 호출하여 데이터를 변경할 수 있습니다. 디스플레이 변경 사항은 뷰를 통해 위젯에 자동으로 전파됩니다. widget-> 모델 ->보기 -> widget-> 모델 ->보기에서 전파 변화의 무한 루프를 생성하기 위해 조심하지 수 등

나는 표시하는 QGraphicsScene /를 QGraphicsView를 사용하는 비슷한 일을했다 모델의 항목.그것의 이름에도 불구하고 QGraphicsView는 모델/뷰 프레임 워크의 일부가 아니므로 QGraphicsScene에서 모델 데이터를 그린 사용자 정의 뷰 클래스를 구현했습니다.

여기 내 코드는 Python에서 제공됩니다. SF wargame에 대한지도에서 세계를 그립니다.

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) 

나는 희망을 품습니다.