모델/뷰 프로그래밍
모델/뷰 프로그래밍 소개
Qt에는 모델/뷰 아키텍처를 사용하여 데이터와 사용자에게 표시되는 방식 사이의 관계를 관리하는 항목 뷰 클래스 세트가 포함되어 있습니다. 이 아키텍처에 의해 도입된 기능 분리는 개발자가 항목 표시를 사용자 정의할 수 있는 유연성을 제공하며, 기존 항목 보기와 함께 다양한 데이터 소스를 사용할 수 있는 표준 모델 인터페이스를 제공합니다. 이 문서에서는 모델/뷰 패러다임에 대한 간략한 소개와 관련된 개념을 설명하고 항목 보기 시스템의 아키텍처를 설명합니다. 아키텍처의 각 구성 요소에 대해 설명하고 제공된 클래스를 사용하는 방법을 보여주는 예제를 제공합니다.
모델/뷰 아키텍처
모델-뷰-컨트롤러(MVC)는 사용자 인터페이스를 구축할 때 자주 사용되는 Smalltalk에서 유래한 디자인 패턴입니다. 디자인 패턴에서 감마 등은 다음과 같이 쓰고 있습니다:
MVC는 세 가지 종류의 객체로 구성됩니다. 모델은 애플리케이션 객체, 뷰는 화면 표시, 컨트롤러는 사용자 인터페이스가 사용자 입력에 반응하는 방식을 정의합니다. MVC 이전에는 사용자 인터페이스 디자인에서 이러한 객체를 한데 묶는 경향이 있었습니다. MVC는 유연성과 재사용성을 높이기 위해 이들을 분리합니다.
뷰와 컨트롤러 객체를 결합하면 모델/뷰 아키텍처가 됩니다. 이는 여전히 데이터가 저장되는 방식과 사용자에게 표시되는 방식을 분리하지만, 동일한 원칙에 따라 더 간단한 프레임워크를 제공합니다. 이러한 분리를 통해 기본 데이터 구조를 변경하지 않고도 동일한 데이터를 여러 가지 보기에 표시하고 새로운 유형의 보기를 구현할 수 있습니다. 사용자 입력을 유연하게 처리할 수 있도록 델리게이트 개념을 도입했습니다. 이 프레임워크에서 델리게이트를 사용하면 데이터 항목을 렌더링하고 편집하는 방식을 사용자 지정할 수 있다는 이점이 있습니다.
모델/뷰 아키텍처 모델은 데이터 소스와 통신하여 아키텍처의 다른 구성 요소에 인터페이스를 제공합니다. 통신의 특성은 데이터 소스 유형과 모델이 구현되는 방식에 따라 달라집니다. 뷰는 모델에서 모델 인덱스를 가져옵니다. 이는 데이터 항목에 대한 참조입니다. 모델에 모델 인덱스를 제공함으로써 뷰는 데이터 소스에서 데이터 항목을 검색할 수 있습니다. 표준 뷰에서는 델리게이트가 데이터 항목을 렌더링합니다. 항목이 편집되면 델리게이트는 모델 인덱스를 사용하여 모델과 직접 통신합니다. |
일반적으로 모델/뷰 클래스는 위에서 설명한 세 가지 그룹, 즉 모델, 뷰 및 델리게이트로 구분할 수 있습니다. 이러한 각 구성 요소는 공통 인터페이스와 경우에 따라 기능의 기본 구현을 제공하는 추상 클래스에 의해 정의됩니다. 추상 클래스는 다른 컴포넌트가 기대하는 전체 기능 집합을 제공하기 위해 서브클래싱할 수 있으며, 이를 통해 특수한 컴포넌트를 작성할 수도 있습니다.
모델, 뷰 및 델리게이트는 신호와 슬롯을 사용하여 서로 통신합니다:
- 모델의 신호는 데이터 원본이 보유한 데이터에 대한 변경 사항을 뷰에 알려줍니다.
- 뷰의 신호는 표시되는 항목에 대한 사용자와의 상호 작용에 대한 정보를 제공합니다.
- 델리게이트의 신호는 편집 중에 모델과 뷰에 편집기의 상태를 알려주는 데 사용됩니다.
모델
모든 항목 모델은 QAbstractItemModel 클래스를 기반으로 합니다. 이 클래스는 뷰와 델리게이트가 데이터에 액세스하는 데 사용하는 인터페이스를 정의합니다. 데이터 자체는 모델에 저장할 필요 없이 별도의 클래스, 파일, 데이터베이스 또는 기타 애플리케이션 구성 요소에서 제공하는 데이터 구조나 저장소에 보관할 수 있습니다.
모델과 관련된 기본 개념은 모델 클래스 섹션에 나와 있습니다.
QAbstractItemModel 는 테이블, 목록 및 트리 형태로 데이터를 나타내는 보기를 처리할 수 있을 만큼 유연한 데이터에 대한 인터페이스를 제공합니다. 그러나 목록 및 테이블과 같은 데이터 구조에 대한 새 모델을 구현할 때는 QAbstractListModel 및 QAbstractTableModel 클래스가 공통 함수의 적절한 기본 구현을 제공하므로 더 좋은 출발점이 됩니다. 이러한 각 클래스를 서브클래싱하여 특수한 종류의 목록과 테이블을 지원하는 모델을 제공할 수 있습니다.
모델을 서브 클래싱하는 과정은 새 모델 생성하기 섹션에서 설명합니다.
Qt는 데이터 항목을 처리하는 데 사용할 수 있는 몇 가지 기성 모델을 제공합니다:
- QStringListModel 는 QString 항목의 간단한 목록을 저장하는 데 사용됩니다.
- QStandardItemModel 는 임의의 데이터를 포함할 수 있는 보다 복잡한 트리 구조의 항목을 관리합니다.
- QFileSystemModel 로컬 파일링 시스템의 파일 및 디렉터리에 대한 정보를 제공합니다.
- QSqlQueryModel, QSqlTableModel, QSqlRelationalTableModel 은 모델/보기 규칙을 사용하여 데이터베이스에 액세스하는 데 사용됩니다.
이러한 표준 모델이 요구 사항을 충족하지 않는 경우 QAbstractItemModel, QAbstractListModel, 또는 QAbstractTableModel 를 서브클래싱하여 사용자 정의 모델을 만들 수 있습니다.
뷰
QListView 은 항목 목록을 표시하고, QTableView 은 모델의 데이터를 테이블에 표시하며, QTreeView 은 데이터의 모델 항목을 계층 목록으로 표시하는 등 다양한 종류의 보기에 대한 완전한 구현이 제공됩니다. 이러한 각 클래스는 QAbstractItemView 추상 기본 클래스를 기반으로 합니다. 이러한 클래스는 바로 사용할 수 있는 구현이지만, 사용자 지정 보기를 제공하기 위해 하위 클래스로 만들 수도 있습니다.
사용 가능한 보기는 보기 클래스 섹션에서 살펴볼 수 있습니다.
델리게이트
QAbstractItemDelegate 는 모델/보기 프레임워크에서 델리게이트를 위한 추상 기본 클래스입니다. 기본 델리게이트 구현은 QStyledItemDelegate 에 의해 제공되며, 이는 Qt의 표준 뷰에서 기본 델리게이트로 사용됩니다. 그러나 QStyledItemDelegate 와 QItemDelegate 은 뷰의 항목에 대한 에디터 페인팅 및 제공에 대한 독립적인 대안입니다. 이 둘의 차이점은 QStyledItemDelegate 은 현재 스타일을 사용하여 항목을 칠한다는 것입니다. 따라서 사용자 정의 델리게이트를 구현하거나 Qt 스타일 시트로 작업할 때는 QStyledItemDelegate 을 기본 클래스로 사용하는 것이 좋습니다.
델리게이트는 델리게이트 클래스 섹션에 설명되어 있습니다.
정렬
모델/뷰 아키텍처에서 정렬에 접근하는 방법에는 두 가지가 있으며, 어떤 접근 방식을 선택할지는 기본 모델에 따라 다릅니다.
모델이 정렬 가능한 경우, 즉 QAbstractItemModel::sort() 함수를 재구현하는 경우 QTableView 및 QTreeView 모두 모델 데이터를 프로그래밍 방식으로 정렬할 수 있는 API를 제공합니다. 또한 QHeaderView::sortIndicatorChanged() 신호를 각각 QTableView::sortByColumn() 슬롯 또는 QTreeView::sortByColumn() 슬롯에 연결하여 대화형 정렬(즉, 사용자가 뷰의 헤더를 클릭하여 데이터를 정렬할 수 있도록 허용)을 활성화할 수 있습니다.
모델에 필요한 인터페이스가 없거나 목록 보기를 사용하여 데이터를 표시하려는 경우 다른 방법은 뷰에 데이터를 표시하기 전에 프록시 모델을 사용하여 모델의 구조를 변환하는 것입니다. 이에 대한 자세한 내용은 프록시 모델 섹션에서 다룹니다.
편의성 클래스
Qt의 항목 기반 항목 뷰와 테이블 클래스에 의존하는 애플리케이션의 편의를 위해 표준 뷰 클래스에서 여러 가지 편의 클래스가 파생되었습니다. 이러한 클래스는 서브 클래싱되지 않습니다.
이러한 클래스의 예로는 QListWidget, QTreeWidget, QTableWidget 등이 있습니다.
이러한 클래스는 뷰 클래스보다 유연성이 떨어지며 임의의 모델과 함께 사용할 수 없습니다. 항목 기반 클래스 집합이 꼭 필요한 경우가 아니라면 항목 보기에서 데이터를 처리할 때 모델/보기 접근 방식을 사용하는 것이 좋습니다.
항목 기반 인터페이스를 사용하면서 모델/뷰 접근 방식에서 제공하는 기능을 활용하려면 QListView, QTableView, QTreeView 와 같은 뷰 클래스를 QStandardItemModel 과 함께 사용하는 것이 좋습니다.
모델 및 보기 사용
다음 섹션에서는 Qt에서 모델/뷰 패턴을 사용하는 방법을 설명합니다. 각 섹션에는 예제가 포함되어 있으며, 그 뒤에 새 컴포넌트를 만드는 방법을 보여주는 섹션이 이어집니다.
Qt에 포함된 두 가지 모델
Qt에서 제공하는 표준 모델 중 두 가지가 QStandardItemModel 와 QFileSystemModel 입니다. QStandardItemModel 는 목록, 테이블, 트리 뷰에 필요한 다양한 데이터 구조를 표현하는 데 사용할 수 있는 다목적 모델입니다. 이 모델은 데이터의 항목도 보유합니다. QFileSystemModel 는 디렉토리의 내용에 대한 정보를 유지하는 모델입니다. 따라서 데이터 항목 자체를 보유하지 않고 단순히 로컬 파일링 시스템의 파일과 디렉터리를 나타냅니다.
QFileSystemModel 실험에 바로 사용할 수 있는 모델을 제공하며, 기존 데이터를 사용하도록 쉽게 구성할 수 있습니다. 이 모델을 사용하여 기성 뷰와 함께 사용할 모델을 설정하는 방법을 보여주고, 모델 인덱스를 사용하여 데이터를 조작하는 방법을 살펴볼 수 있습니다.
기존 모델과 함께 뷰 사용
QListView 및 QTreeView 클래스는 QFileSystemModel 과 함께 사용하기에 가장 적합한 뷰입니다. 아래 제시된 예는 목록 보기의 동일한 정보 옆에 트리 보기의 디렉터리 내용을 표시합니다. 이 보기는 사용자의 선택 내용을 공유하므로 선택한 항목이 두 보기 모두에서 강조 표시됩니다.
QFileSystemModel 을 설정하여 사용할 수 있도록 하고 디렉터리의 콘텐츠를 표시하는 몇 가지 보기를 만듭니다. 이것은 모델을 사용하는 가장 간단한 방법을 보여줍니다. 모델의 구성 및 사용은 단일 main()
함수 내에서 수행됩니다:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QSplitter *splitter = new QSplitter; QFileSystemModel *model = new QFileSystemModel; model->setRootPath(QDir::currentPath());
모델은 특정 파일 시스템의 데이터를 사용하도록 설정됩니다. setRootPath ()를 호출하면 모델에 파일 시스템의 어느 드라이브를 뷰에 노출할지 알려줍니다.
모델에 있는 항목을 두 가지 방식으로 검사할 수 있도록 두 개의 보기를 만듭니다:
QTreeView *tree = new QTreeView(splitter); tree->setModel(model); tree->setRootIndex(model->index(QDir::currentPath())); QListView *list = new QListView(splitter); list->setModel(model); list->setRootIndex(model->index(QDir::currentPath()));
뷰는 다른 위젯과 동일한 방식으로 구성됩니다. 모델의 항목을 표시하도록 보기를 설정하려면 디렉토리 모델을 인수로 사용하여 setModel() 함수를 호출하기만 하면 됩니다. 각 보기에서 setRootIndex() 함수를 호출하여 현재 디렉터리에 대한 파일 시스템 모델에서 적절한 모델 인덱스를 전달하여 모델에서 제공하는 데이터를 필터링합니다.
이 경우 사용된 index()
함수는 QFileSystemModel 에 고유하며, 디렉터리를 제공하면 모델 인덱스를 반환합니다. 모델 인덱스는 모델 클래스에서 설명합니다.
나머지 함수는 스플리터 위젯 내에서 뷰를 표시하고 애플리케이션의 이벤트 루프를 실행하기만 합니다:
splitter->setWindowTitle("Two views onto the same file system model"); splitter->show(); return app.exec(); }
위의 예제에서는 항목 선택을 처리하는 방법에 대해 언급하지 않았습니다. 이 주제는 항목 보기에서 선택 처리하기 섹션에서 더 자세히 다룹니다.
모델 클래스
선택이 처리되는 방법을 살펴보기 전에 모델/뷰 프레임워크에서 사용되는 개념을 살펴보는 것이 유용할 수 있습니다.
기본 개념
모델/뷰 아키텍처에서 모델은 뷰와 델리게이트가 데이터에 액세스하는 데 사용하는 표준 인터페이스를 제공합니다. Qt에서 표준 인터페이스는 QAbstractItemModel 클래스에 의해 정의됩니다. 데이터의 항목이 기본 데이터 구조에 저장되는 방식에 관계없이 QAbstractItemModel 의 모든 서브 클래스는 항목의 테이블을 포함하는 계층 구조로 데이터를 나타냅니다. 뷰는 이 규칙을 사용하여 모델의 데이터 항목에 액세스하지만 이 정보를 사용자에게 표시하는 방식에는 제한이 없습니다.
또한 모델은 신호 및 슬롯 메커니즘을 통해 연결된 모든 뷰에 데이터 변경 사항을 알립니다.
이 섹션에서는 모델 클래스를 통해 다른 컴포넌트가 데이터 항목에 액세스하는 방식의 핵심이 되는 몇 가지 기본 개념에 대해 설명합니다. 보다 고급 개념은 이후 섹션에서 설명합니다.
모델 인덱스
데이터의 표현이 데이터에 액세스하는 방식과 분리되도록 하기 위해 모델 인덱스의 개념을 도입했습니다. 모델을 통해 얻을 수 있는 각 정보는 모델 인덱스로 표현됩니다. 뷰 및 위임자는 이러한 인덱스를 사용하여 표시할 데이터 항목을 요청합니다.
따라서 모델만 데이터를 얻는 방법을 알면 되고, 모델이 관리하는 데이터의 유형은 상당히 일반적으로 정의할 수 있습니다. 모델 인덱스에는 인덱스를 생성한 모델에 대한 포인터가 포함되어 있으므로 둘 이상의 모델로 작업할 때 혼동을 방지할 수 있습니다.
QAbstractItemModel *model = index.model();
모델 인덱스는 정보에 대한 임시 참조를 제공하며, 모델을 통해 데이터를 검색하거나 수정하는 데 사용할 수 있습니다. 모델은 수시로 내부 구조를 재구성할 수 있으므로 모델 인덱스는 유효하지 않을 수 있으므로 저장해서는 안 됩니다. 정보에 대한 장기적인 참조가 필요한 경우 영구 모델 인덱스를 만들어야 합니다. 이렇게 하면 모델이 최신 상태로 유지되는 정보에 대한 참조를 제공합니다. 임시 모델 인덱스는 QModelIndex 클래스에서 제공하고 영구 모델 인덱스는 QPersistentModelIndex 클래스에서 제공합니다.
데이터 항목에 해당하는 모델 인덱스를 얻으려면 모델에 행 번호, 열 번호, 상위 항목의 모델 인덱스 등 세 가지 속성을 지정해야 합니다. 다음 섹션에서는 이러한 속성에 대해 자세히 설명하고 설명합니다.
행 및 열
가장 기본적인 형태인 모델은 행과 열 번호로 항목이 위치하는 간단한 테이블로 액세스할 수 있습니다. 그렇다고 해서 기본 데이터 조각이 배열 구조로 저장되는 것은 아니며, 행과 열 번호를 사용하는 것은 컴포넌트가 서로 통신할 수 있도록 하기 위한 규칙일 뿐입니다. 모델에 행과 열 번호를 지정하여 특정 항목에 대한 정보를 검색할 수 있으며, 해당 항목을 나타내는 인덱스를 받습니다:
QModelIndex index = model->index(row, column, ...);
목록이나 테이블과 같은 단순한 단일 레벨 데이터 구조에 대한 인터페이스를 제공하는 모델은 다른 정보를 제공할 필요가 없지만, 위의 코드에서 알 수 있듯이 모델 인덱스를 가져올 때는 더 많은 정보를 제공해야 합니다.
행과 열 이 다이어그램은 각 항목이 한 쌍의 행과 열 번호로 위치하는 기본 테이블 모델의 표현을 보여줍니다. 모델에 관련 행과 열 번호를 전달하여 데이터 항목을 참조하는 모델 인덱스를 얻습니다. QModelIndex indexA = model->index(0, 0, QModelIndex()); QModelIndex indexB = model->index(1, 1, QModelIndex()); QModelIndex indexC = model->index(2, 1, QModelIndex()); 모델의 최상위 항목은 항상 |
항목의 부모
모델에서 제공하는 항목 데이터에 대한 표와 같은 인터페이스는 테이블 또는 목록 보기에서 데이터를 사용할 때 이상적이며, 행 및 열 번호 시스템은 뷰가 항목을 표시하는 방식과 정확히 매핑됩니다. 그러나 트리 뷰와 같은 구조에서는 모델에서 그 안의 항목에 대해 보다 유연한 인터페이스를 노출해야 합니다. 따라서 트리 보기의 최상위 항목이 다른 항목 목록을 포함할 수 있는 것과 마찬가지로 각 항목은 다른 항목 테이블의 부모가 될 수도 있습니다.
모델 항목의 인덱스를 요청할 때는 항목의 부모에 대한 몇 가지 정보를 제공해야 합니다. 모델 외부에서 항목을 참조할 수 있는 유일한 방법은 모델 인덱스를 통해서이므로 상위 모델 인덱스도 제공해야 합니다:
QModelIndex index = model->index(row, column, parent);
부모, 행 및 열 이 다이어그램은 각 항목이 부모, 행 번호 및 열 번호로 참조되는 트리 모델의 표현을 보여줍니다. 항목 "A"와 "C"는 모델에서 최상위 형제 항목으로 표시됩니다: QModelIndex indexA = model->index(0, 0, QModelIndex()); QModelIndex indexC = model->index(2, 1, QModelIndex()); 항목 "A"에는 여러 개의 자식이 있습니다. 항목 "B"에 대한 모델 인덱스는 다음 코드를 사용하여 얻을 수 있습니다: QModelIndex indexB = model->index(1, 0, indexA); |
항목 역할
모델의 항목은 다른 구성 요소에 대해 다양한 역할을 수행할 수 있으므로 상황에 따라 다양한 종류의 데이터를 제공할 수 있습니다. 예를 들어 Qt::DisplayRole 은 뷰에서 텍스트로 표시할 수 있는 문자열에 액세스하는 데 사용됩니다. 일반적으로 항목에는 여러 가지 역할에 대한 데이터가 포함되며 표준 역할은 Qt::ItemDataRole 에 정의되어 있습니다.
모델에 항목에 해당하는 모델 인덱스를 전달하고 역할을 지정하여 원하는 데이터 유형을 가져와서 항목의 데이터를 요청할 수 있습니다:
QVariant value = model->data(index, role);
항목 역할 역할은 모델에 참조되는 데이터 유형을 나타냅니다. 뷰는 역할을 다양한 방식으로 표시할 수 있으므로 각 역할에 적합한 정보를 제공하는 것이 중요합니다. 새 모델 만들기 섹션에서 역할의 구체적인 용도에 대해 자세히 설명합니다. |
항목 데이터의 가장 일반적인 용도는 Qt::ItemDataRole 에 정의된 표준 역할에서 다룹니다. 각 역할에 적절한 항목 데이터를 제공함으로써 모델은 사용자에게 항목을 표시하는 방법에 대한 힌트를 뷰 및 위임자에게 제공할 수 있습니다. 다양한 종류의 보기는 필요에 따라 이 정보를 자유롭게 해석하거나 무시할 수 있습니다. 애플리케이션별 목적을 위해 추가 역할을 정의할 수도 있습니다.
요약
- 모델 인덱스는 뷰와 위임자에게 기본 데이터 구조와 독립적인 방식으로 모델에서 제공하는 항목의 위치에 대한 정보를 제공합니다.
- 항목은 행 및 열 번호와 상위 항목의 모델 인덱스로 참조됩니다.
- 모델 인덱스는 뷰 및 델리게이트와 같은 다른 구성 요소의 요청에 따라 모델에 의해 구성됩니다.
- index()를 사용하여 인덱스를 요청할 때 상위 항목에 대해 유효한 모델 인덱스가 지정되면 반환되는 인덱스는 모델에서 해당 상위 항목 아래에 있는 항목을 참조합니다. 얻은 인덱스는 해당 항목의 하위 항목을 참조합니다.
- index()를 사용하여 인덱스를 요청할 때 상위 항목에 대해 잘못된 모델 인덱스가 지정된 경우 반환되는 인덱스는 모델의 최상위 항목을 참조합니다.
- role 은 항목과 관련된 여러 종류의 데이터를 구분합니다.
모델 인덱스 사용
모델 인덱스를 사용하여 모델에서 데이터를 검색하는 방법을 보여 주기 위해 보기가 없는 QFileSystemModel 을 설정하고 위젯에 파일 및 디렉터리 이름을 표시합니다. 이것은 모델을 사용하는 일반적인 방법을 보여주지는 않지만 모델 인덱스를 다룰 때 모델에서 사용하는 규칙을 보여줍니다.
QFileSystemModel 로딩은 시스템 리소스 사용을 최소화하기 위해 비동기식으로 이루어집니다. 이 모델을 다룰 때는 이를 고려해야 합니다.
다음과 같은 방식으로 파일 시스템 모델을 구성합니다:
auto *model = new QFileSystemModel; auto onDirectoryLoaded = [model, layout, &window](const QString &directory) { QModelIndex parentIndex = model->index(directory); const int numRows = model->rowCount(parentIndex); for (int row = 0; row < numRows; ++row) { QModelIndex index = model->index(row, 0, parentIndex); QString text = model->data(index, Qt::DisplayRole).toString(); // Display the text in a widget. auto *label = new QLabel(text, &window); layout->addWidget(label); } }; QObject::connect(model, &QFileSystemModel::directoryLoaded, onDirectoryLoaded); model->setRootPath(QDir::currentPath());
이 경우 기본값 QFileSystemModel 을 설정하는 것으로 시작합니다. 해당 신호 directoryLoaded(QString)
를 람다에 연결하여 해당 모델에서 제공하는 index()의 특정 구현을 사용하여 디렉토리의 상위 인덱스를 얻습니다.
람다에서는 rowCount() 함수를 사용하여 모델의 행 수를 결정합니다.
간단하게 하기 위해 모델의 첫 번째 열에 있는 항목에만 관심이 있습니다. 각 행을 차례로 검사하여 각 행의 첫 번째 항목에 대한 모델 인덱스를 얻고 모델에서 해당 항목에 대해 저장된 데이터를 읽습니다.
for (int row = 0; row < numRows; ++row) { QModelIndex index = model->index(row, 0, parentIndex);
모델 인덱스를 얻으려면 행 번호, 열 번호(첫 번째 열의 경우 0), 원하는 모든 항목의 부모에 대한 적절한 모델 인덱스를 지정합니다. 각 항목에 저장된 텍스트는 모델의 data() 함수를 사용하여 검색합니다. 모델 인덱스와 DisplayRole 을 지정하여 문자열 형태로 항목에 대한 데이터를 가져옵니다.
마지막으로 QFileSystemModel 의 루트 경로를 설정하여 데이터 로드를 시작하고 람다를 트리거합니다.
위의 예는 모델에서 데이터를 검색하는 데 사용되는 기본 원칙을 보여줍니다:
- 모델의 차원은 rowCount() 및 columnCount()을 사용하여 찾을 수 있습니다. 이러한 함수를 사용하려면 일반적으로 상위 모델 인덱스를 지정해야 합니다.
- 모델 인덱스는 모델의 항목에 액세스하는 데 사용됩니다. 항목을 지정하려면 행, 열 및 상위 모델 인덱스가 필요합니다.
- 모델의 최상위 항목에 액세스하려면
QModelIndex()
을 사용하여 널 모델 인덱스를 부모 인덱스로 지정합니다. - 항목에는 다양한 역할에 대한 데이터가 포함되어 있습니다. 특정 역할에 대한 데이터를 가져오려면 모델 인덱스와 역할을 모두 모델에 제공해야 합니다.
추가 읽기
QAbstractItemModel 에서 제공하는 표준 인터페이스를 구현하여 새 모델을 만들 수 있습니다. 새 모델 만들기 섹션에서는 문자열 목록을 보관하기 위해 바로 사용할 수 있는 편리한 모델을 만들어서 이를 시연합니다.
클래스 보기
개념
모델/뷰 아키텍처에서 뷰는 모델에서 데이터 항목을 가져와 사용자에게 표시합니다. 데이터가 표시되는 방식은 모델에서 제공하는 데이터의 표현과 유사할 필요는 없으며 데이터 항목을 저장하는 데 사용되는 기본 데이터 구조와 완전히 다를 수 있습니다.
콘텐츠와 표현의 분리는 QAbstractItemModel 에서 제공하는 표준 모델 인터페이스, QAbstractItemView 에서 제공하는 표준 보기 인터페이스 및 데이터 항목을 일반적인 방식으로 표현하는 모델 인덱스의 사용을 통해 이루어집니다. 뷰는 일반적으로 모델에서 가져온 데이터의 전체 레이아웃을 관리합니다. 뷰는 개별 데이터 항목을 직접 렌더링하거나 델리게이트를 사용하여 렌더링 및 편집 기능을 모두 처리할 수 있습니다.
뷰는 데이터를 표시할 뿐만 아니라 항목 간 탐색 및 항목 선택의 일부 측면도 처리합니다. 뷰는 상황에 맞는 메뉴 및 끌어서 놓기와 같은 기본적인 사용자 인터페이스 기능도 구현합니다. 뷰는 항목에 대한 기본 편집 기능을 제공하거나 델리게이트와 함께 작동하여 사용자 지정 편집기를 제공할 수 있습니다.
뷰는 모델 없이도 만들 수 있지만 유용한 정보를 표시하려면 모델을 제공해야 합니다. 보기는 각 보기에 대해 개별적으로 유지 관리하거나 여러 보기 간에 공유할 수 있는 선택 항목을 사용하여 사용자가 선택한 항목을 추적합니다.
QTableView 및 QTreeView 과 같은 일부 보기는 항목뿐만 아니라 헤더도 표시합니다. 이러한 뷰는 뷰 클래스 QHeaderView 에 의해 구현되기도 합니다. 헤더는 일반적으로 헤더가 포함된 뷰와 동일한 모델에 액세스합니다. QAbstractItemModel::headerData () 함수를 사용하여 모델에서 데이터를 검색하고 일반적으로 헤더 정보를 레이블 형식으로 표시합니다. 새 헤더는 QHeaderView 클래스에서 하위 클래스로 지정하여 뷰에 보다 전문화된 레이블을 제공할 수 있습니다.
기존 뷰 사용
Qt는 대부분의 사용자에게 익숙한 방식으로 모델의 데이터를 표시하는 즉시 사용 가능한 세 가지 뷰 클래스를 제공합니다. QListView 는 모델의 항목을 단순한 목록 또는 클래식 아이콘 뷰 형태로 표시할 수 있습니다. QTreeView 는 모델의 항목을 목록의 계층 구조로 표시하여 깊게 중첩된 구조를 간결하게 표현할 수 있습니다. QTableView 는 스프레드시트 애플리케이션의 레이아웃처럼 모델의 항목을 표 형태로 표시합니다.
위에 표시된 표준 보기의 기본 동작은 대부분의 애플리케이션에 충분합니다. 기본적인 편집 기능을 제공하며 보다 전문적인 사용자 인터페이스의 요구에 맞게 사용자 지정할 수 있습니다.
모델 사용
예제 모델로 만든 문자열 목록 모델을 가져와서 몇 가지 데이터를 설정하고 모델의 내용을 표시하는 보기를 구성합니다. 이 모든 작업은 단일 함수 내에서 수행할 수 있습니다:
int main(int argc, char *argv[]) { QApplication app(argc, argv); // Unindented for quoting purposes: QStringList numbers; numbers << "One" << "Two" << "Three" << "Four" << "Five"; QAbstractItemModel *model = new StringListModel(numbers);
StringListModel
은 QAbstractItemModel 으로 선언되어 있으므로 모델에 대한 추상 인터페이스를 사용할 수 있으며 문자열 목록 모델을 다른 모델로 대체하더라도 코드가 계속 작동합니다.
QListView 에서 제공하는 목록 보기는 문자열 목록 모델의 항목을 표시하는 데 충분합니다. 다음 코드 줄을 사용하여 보기를 구성하고 모델을 설정합니다:
뷰는 일반적인 방식으로 표시됩니다:
view->show(); return app.exec(); }
뷰는 모델의 인터페이스를 통해 데이터에 액세스하여 모델의 콘텐츠를 렌더링합니다. 사용자가 항목을 편집하려고 하면 뷰는 기본 델리게이트를 사용하여 편집기 위젯을 제공합니다.
위 이미지는 QListView 이 문자열 목록 모델에서 데이터를 나타내는 방법을 보여줍니다. 이 모델은 편집 가능하므로 뷰는 기본 델리게이트를 사용하여 목록의 각 항목을 자동으로 편집할 수 있도록 합니다.
모델의 여러 보기 사용
동일한 모델에 여러 보기를 제공하는 것은 각 보기에 대해 동일한 모델을 설정하기만 하면 됩니다. 다음 코드에서는 이 예제에서 만든 것과 동일한 간단한 테이블 모델을 사용하여 각각 두 개의 테이블 보기를 만듭니다:
QTableView *firstTableView = new QTableView; QTableView *secondTableView = new QTableView; firstTableView->setModel(model); secondTableView->setModel(model);
모델/뷰 아키텍처에서 신호와 슬롯을 사용하면 모델에 대한 변경 사항이 연결된 모든 뷰에 전파될 수 있으므로 사용 중인 뷰에 관계없이 항상 동일한 데이터에 액세스할 수 있습니다.
위 이미지는 동일한 모델에 대해 각각 선택한 여러 항목을 포함하는 두 개의 서로 다른 뷰를 보여줍니다. 모델의 데이터는 뷰 간에 일관되게 표시되지만 각 뷰는 자체 내부 선택 모델을 유지합니다. 이는 특정 상황에서 유용할 수 있지만, 많은 애플리케이션에서는 공유 선택 모델을 사용하는 것이 바람직합니다.
항목 선택 처리
뷰 내에서 항목 선택을 처리하는 메커니즘은 QItemSelectionModel 클래스에 의해 제공됩니다. 모든 표준 뷰는 기본적으로 자체 선택 모델을 구성하고 일반적인 방식으로 상호 작용합니다. 뷰에서 사용 중인 선택 모델은 selectionModel() 함수를 통해 얻을 수 있으며, 대체 선택 모델은 setSelectionModel()로 지정할 수 있습니다. 뷰에서 사용하는 선택 모델을 제어하는 기능은 동일한 모델 데이터에 대해 여러 개의 일관된 뷰를 제공하려는 경우에 유용합니다.
일반적으로 모델이나 뷰를 서브클래싱하지 않는 한, 선택 내용을 직접 조작할 필요는 없습니다. 그러나 필요한 경우 선택 모델에 대한 인터페이스에 액세스할 수 있으며, 이에 대해서는 항목 뷰에서 선택 처리하기에서 살펴봅니다.
뷰 간에 선택 내용 공유
뷰 클래스가 기본적으로 자체 선택 모델을 제공하는 것이 편리하지만, 동일한 모델에 대해 둘 이상의 뷰를 사용하는 경우 모델의 데이터와 사용자의 선택이 모든 뷰에 일관되게 표시되는 것이 바람직할 때가 많습니다. 뷰 클래스는 내부 선택 모델을 교체할 수 있으므로 다음 줄을 사용하여 뷰 간에 통합된 선택을 수행할 수 있습니다:
secondTableView->setSelectionModel(firstTableView->selectionModel());
두 번째 뷰에는 첫 번째 뷰의 선택 모델이 제공됩니다. 이제 두 뷰가 동일한 선택 모델에서 작동하여 데이터와 선택한 항목이 모두 동기화됩니다.
위에 표시된 예에서는 동일한 유형의 두 뷰가 동일한 모델의 데이터를 표시하는 데 사용되었습니다. 그러나 두 가지 유형의 보기가 사용된 경우 선택한 항목이 각 보기에서 매우 다르게 표시될 수 있습니다. 예를 들어 테이블 보기에서 연속된 선택 항목은 트리 보기에서 강조 표시된 항목의 조각화된 집합으로 표시될 수 있습니다.
델리게이트 클래스
개념
모델-뷰-컨트롤러 패턴과 달리 모델/뷰 디자인에는 사용자와의 상호작용을 관리하기 위한 완전히 분리된 컴포넌트가 포함되어 있지 않습니다. 일반적으로 뷰는 사용자에게 모델 데이터를 표시하고 사용자 입력을 처리하는 역할을 담당합니다. 이 입력을 얻는 방식에 약간의 유연성을 부여하기 위해 상호 작용은 델리게이트에 의해 수행됩니다. 이러한 구성 요소는 입력 기능을 제공하며 일부 보기에서 개별 항목을 렌더링하는 역할도 담당합니다. 델리게이트를 제어하기 위한 표준 인터페이스는 QAbstractItemDelegate 클래스에 정의되어 있습니다.
델리게이트는 paint() 및 sizeHint() 함수를 구현하여 콘텐츠를 직접 렌더링할 수 있어야 합니다. 그러나 간단한 위젯 기반 델리게이트는 QAbstractItemDelegate 대신 QStyledItemDelegate 을 서브클래싱하고 이러한 함수의 기본 구현을 활용할 수 있습니다.
델리게이트용 편집기는 위젯을 사용하여 편집 프로세스를 관리하거나 이벤트를 직접 처리하여 구현할 수 있습니다. 첫 번째 접근 방식은 이 섹션의 뒷부분에서 다룹니다.
기존 델리게이트 사용
Qt와 함께 제공되는 표준 뷰는 QStyledItemDelegate 의 인스턴스를 사용하여 편집 기능을 제공합니다. 이 델리게이트 인터페이스의 기본 구현은 각 표준 뷰에 대해 일반적인 스타일로 항목을 렌더링합니다: QListView, QTableView, 그리고 QTreeView 입니다.
모든 표준 역할은 표준 뷰에서 사용하는 기본 델리게이트에 의해 처리됩니다. 이러한 역할이 해석되는 방식은 QStyledItemDelegate 문서에 설명되어 있습니다.
뷰에서 사용하는 델리게이트는 itemDelegate() 함수에 의해 반환됩니다. setItemDelegate () 함수를 사용하면 표준 보기에 대한 사용자 지정 델리게이트를 설치할 수 있으며, 사용자 지정 보기에 대한 델리게이트를 설정할 때 이 함수를 사용해야 합니다.
간단한 델리게이트
여기서 구현한 델리게이트는 QSpinBox 를 사용하여 편집 기능을 제공하며, 주로 정수를 표시하는 모델에 사용하기 위한 것입니다. 이 목적을 위해 사용자 지정 정수 기반 테이블 모델을 설정했지만, 사용자 지정 델리게이트가 데이터 입력을 제어하므로 QStandardItemModel 대신 쉽게 사용할 수 있었습니다. 모델의 내용을 표시하기 위해 테이블 뷰를 구성하고, 이 뷰는 편집에 사용자 지정 델리게이트를 사용합니다.
사용자 정의 표시 함수를 작성하고 싶지 않기 때문에 QStyledItemDelegate 에서 델리게이트를 서브클래싱합니다. 그러나 편집기 위젯을 관리하기 위한 함수는 여전히 제공해야 합니다:
class SpinBoxDelegate : public QStyledItemDelegate { Q_OBJECT public: SpinBoxDelegate(QObject *parent = nullptr); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; SpinBoxDelegate::SpinBoxDelegate(QObject *parent) : QStyledItemDelegate(parent) { }
델리게이트가 생성될 때 편집기 위젯은 설정되지 않습니다. 편집기 위젯은 필요할 때만 구성합니다.
편집기 제공
이 예에서는 테이블 보기에서 편집기를 제공해야 하는 경우 델리게이트에 수정 중인 항목에 적합한 편집기 위젯을 제공하도록 요청합니다. createEditor () 함수에는 델리게이트가 적절한 위젯을 설정하는 데 필요한 모든 것이 제공됩니다:
QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QSpinBox *editor = new QSpinBox(parent); editor->setFrame(false); editor->setMinimum(0); editor->setMaximum(100); return editor; }
편집기 위젯이 더 이상 필요하지 않으면 뷰에서 위젯을 파기하므로 편집기 위젯에 대한 포인터를 유지할 필요가 없습니다.
편집기에 델리게이트의 기본 이벤트 필터를 설치하여 사용자가 기대하는 표준 편집 바로 가기를 제공할 수 있도록 합니다. 편집기에 추가 바로 가기를 추가하여 보다 정교한 동작을 허용할 수 있으며, 이에 대한 자세한 내용은 편집 힌트 섹션에서 설명합니다.
뷰는 이러한 목적으로 나중에 정의하는 함수를 호출하여 편집기의 데이터와 지오메트리가 올바르게 설정되도록 합니다. 뷰에서 제공하는 모델 인덱스에 따라 다른 편집기를 만들 수 있습니다. 예를 들어 정수 열과 문자열 열이 있는 경우 편집 중인 열에 따라 QSpinBox
또는 QLineEdit
을 반환할 수 있습니다.
델리게이트는 모델 데이터를 편집기에 복사하는 함수를 제공해야 합니다. 이 예에서는 display role 에 저장된 데이터를 읽고 그에 따라 스핀 상자에 값을 설정합니다.
void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { int value = index.data(Qt::EditRole).toInt(); QSpinBox *spinBox = static_cast<QSpinBox*>(editor); spinBox->setValue(value); }
이 예제에서는 편집기 위젯이 스핀 박스라는 것을 알고 있지만 모델의 데이터 유형에 따라 다른 편집기를 제공할 수 있으며, 이 경우 멤버 함수에 액세스하기 전에 위젯을 적절한 유형으로 캐스팅해야 합니다.
모델에 데이터 제출하기
사용자가 회전 상자의 값 편집을 마치면 뷰는 setModelData() 함수를 호출하여 델리게이트에 편집된 값을 모델에 저장하도록 요청합니다.
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QSpinBox *spinBox = static_cast<QSpinBox*>(editor); spinBox->interpretText(); int value = spinBox->value(); model->setData(index, value, Qt::EditRole); }
뷰가 델리게이트의 편집기 위젯을 관리하므로 제공된 편집기의 콘텐츠로 모델을 업데이트하기만 하면 됩니다. 이 경우 스핀 박스가 최신 상태인지 확인하고 지정된 인덱스를 사용하여 스핀 박스에 포함된 값으로 모델을 업데이트합니다.
표준 QStyledItemDelegate 클래스는 편집이 완료되면 closeEditor() 신호를 전송하여 뷰에 알립니다. 뷰는 편집기 위젯이 닫히고 소멸되도록 합니다. 이 예제에서는 간단한 편집 기능만 제공하므로 이 신호를 보낼 필요가 없습니다.
데이터에 대한 모든 작업은 QAbstractItemModel 에서 제공하는 인터페이스를 통해 수행됩니다. 따라서 델리게이트는 조작하는 데이터 유형과 거의 독립적이지만 특정 유형의 편집기 위젯을 사용하려면 몇 가지 가정을 해야 합니다. 이 예에서는 모델에 항상 정수 값이 포함되어 있다고 가정했지만 QVariant 에서 예기치 않은 데이터에 대해 합리적인 기본값을 제공하므로 다른 종류의 모델에서도 이 델리게이트를 사용할 수 있습니다.
에디터의 지오메트리 업데이트하기
편집기의 지오메트리를 관리하는 것은 델리게이트의 책임입니다. 편집기를 만들 때와 뷰에서 항목의 크기 또는 위치가 변경될 때 지오메트리를 설정해야 합니다. 다행히도 뷰는 view option 개체 내에 필요한 모든 지오메트리 정보를 제공합니다.
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { editor->setGeometry(option.rect); }
이 경우 항목 사각형의 뷰 옵션에서 제공하는 지오메트리 정보를 사용하기만 하면 됩니다. 여러 요소가 있는 항목을 렌더링하는 델리게이트는 항목 직사각형을 직접 사용하지 않습니다. 이 델리게이트는 항목의 다른 요소와 관련하여 편집기를 배치합니다.
편집 힌트
편집 후 델리게이트는 다른 컴포넌트에 편집 과정의 결과에 대한 힌트를 제공하고 후속 편집 작업에 도움이 되는 힌트를 제공해야 합니다. 이는 적절한 힌트와 함께 closeEditor() 신호를 전송함으로써 이루어집니다. 이 작업은 스핀 박스를 만들 때 설치한 기본 QStyledItemDelegate 이벤트 필터가 처리합니다.
스핀 박스의 동작을 조정하여 보다 사용자 친화적으로 만들 수 있습니다. QStyledItemDelegate 에서 제공하는 기본 이벤트 필터에서는 사용자가 Return 을 눌러 스핀 상자에서 선택을 확인하면 델리게이트가 모델에 값을 커밋하고 스핀 상자를 닫습니다. 스핀 상자에 자체 이벤트 필터를 설치하여 이 동작을 변경하고 필요에 맞는 편집 힌트를 제공할 수 있습니다(예: EditNextItem 힌트와 함께 closeEditor()를 전송하여 뷰의 다음 항목 편집을 자동으로 시작할 수 있습니다.
이벤트 필터를 사용할 필요가 없는 또 다른 접근 방식은 편의를 위해 QSpinBox 을 하위 클래싱하여 자체 편집기 위젯을 제공하는 것입니다. 이 대안적인 접근 방식을 사용하면 추가 코드를 작성하는 대신 편집기 위젯의 작동 방식을 더 잘 제어할 수 있습니다. 표준 Qt 에디터 위젯의 동작을 사용자 정의해야 하는 경우 일반적으로 델리게이트에 이벤트 필터를 설치하는 것이 더 쉽습니다.
델리게이트는 이러한 힌트를 방출할 필요는 없지만, 그렇지 않은 델리게이트는 애플리케이션에 통합성이 떨어지고 일반적인 편집 작업을 지원하기 위해 힌트를 방출하는 델리게이트보다 사용성이 떨어집니다.
항목 보기에서 선택 항목 처리하기
개념
항목 보기 클래스에서 사용되는 선택 모델은 모델/보기 아키텍처의 기능을 기반으로 선택에 대한 일반적인 설명을 제공합니다. 선택 조작을 위한 표준 클래스는 제공된 항목 보기에 충분하지만 선택 모델을 사용하면 자체 항목 모델 및 보기의 요구 사항에 맞게 특수한 선택 모델을 만들 수 있습니다.
보기에서 선택한 항목에 대한 정보는 QItemSelectionModel 클래스의 인스턴스에 저장됩니다. 이렇게 하면 단일 모델의 항목에 대한 모델 인덱스가 유지되며 모든 보기와 독립적으로 유지됩니다. 하나의 모델에 많은 보기가 있을 수 있으므로 보기 간에 선택 내용을 공유할 수 있으므로 애플리케이션에서 일관된 방식으로 여러 보기를 표시할 수 있습니다.
선택은 선택 범위로 구성됩니다. 선택한 항목의 각 범위에 대한 시작 및 종료 모델 인덱스만 기록하여 대규모 항목 선택에 대한 정보를 효율적으로 유지합니다. 비연속적인 항목 선택은 둘 이상의 선택 범위를 사용하여 선택을 설명함으로써 구성됩니다.
선택은 선택 모델이 보유한 모델 인덱스 모음에 적용됩니다. 가장 최근에 적용된 항목 선택을 현재 선택이라고 합니다. 이 선택의 효과는 특정 유형의 선택 명령을 사용하여 적용 후에도 수정할 수 있습니다. 이에 대해서는 이 섹션의 뒷부분에서 설명합니다.
현재 항목 및 선택된 항목
뷰에는 항상 현재 항목과 선택된 항목, 즉 두 개의 독립적인 상태가 있습니다. 한 항목이 현재 항목이면서 동시에 선택된 항목일 수 있습니다. 예를 들어 키보드 탐색에는 현재 항목이 필요하므로 뷰는 항상 현재 항목이 있는지 확인할 책임이 있습니다.
아래 표는 현재 항목과 선택된 항목의 차이점을 강조 표시합니다.
현재 항목 | 선택된 항목 |
---|---|
현재 항목은 하나만 있을 수 있습니다. | 선택 항목은 여러 개가 있을 수 있습니다. |
현재 항목은 키 탐색 또는 마우스 버튼 클릭으로 변경됩니다. | 항목의 선택 상태는 사용자가 항목과 상호 작용할 때 미리 정의된 여러 모드(예: 단일 선택, 다중 선택 등)에 따라 설정되거나 설정 해제됩니다. - 사용자가 항목과 상호작용할 때 미리 정의된 여러 모드(예: 단일 선택, 다중 선택 등)에 따라 설정 또는 해제됩니다. |
수정 키( F2)를 누르거나 항목을 두 번 클릭하면 현재 항목이 수정됩니다(수정이 활성화되어 있는 경우). | 현재 항목은 앵커와 함께 사용하여 선택 또는 선택 취소할 범위(또는 이 두 가지를 조합하여)를 지정할 수 있습니다. |
현재 항목은 초점 사각형으로 표시됩니다. | 선택한 항목은 선택 사각형으로 표시됩니다. |
선택 항목을 조작할 때 QItemSelectionModel 을 항목 모델에 있는 모든 항목의 선택 상태 기록으로 생각하면 도움이 되는 경우가 많습니다. 선택 모델을 설정하면 어떤 항목이 이미 선택되어 있는지 알 필요 없이 항목 모음을 선택하거나 선택 취소하거나 선택 상태를 전환할 수 있습니다. 선택한 모든 항목의 인덱스는 언제든지 검색할 수 있으며, 신호 및 슬롯 메커니즘을 통해 다른 구성 요소에 선택 모델의 변경 사항을 알릴 수 있습니다.
선택 모델 사용
표준 보기 클래스는 대부분의 애플리케이션에서 사용할 수 있는 기본 선택 모델을 제공합니다. 뷰의 selectionModel() 함수를 사용하여 한 뷰에 속하는 선택 모델을 가져오고 setSelectionModel()를 사용하여 여러 뷰 간에 공유할 수 있으므로 일반적으로 새 선택 모델을 구성할 필요가 없습니다.
선택은 모델과 QItemSelection 에 대한 모델 인덱스 쌍을 지정하여 만들어집니다. 그러면 인덱스를 사용하여 지정된 모델의 항목을 참조하고 선택한 항목 블록에서 왼쪽 상단 및 오른쪽 하단 항목으로 해석합니다. 모델의 항목에 선택 내용을 적용하려면 선택 내용을 선택 모델에 제출해야 하며, 이는 선택 모델에 이미 있는 선택 내용에 각각 다른 영향을 미치는 여러 가지 방법으로 수행할 수 있습니다.
항목 선택
선택의 주요 기능 중 일부를 보여 주기 위해 총 32개의 항목이 있는 사용자 지정 테이블 모델의 인스턴스를 구성하고 해당 데이터에 대한 테이블 보기를 열어 보겠습니다:
TableModel *model = new TableModel(8, 4, &app); QTableView *table = new QTableView(0); table->setModel(model); QItemSelectionModel *selectionModel = table->selectionModel();
나중에 사용할 수 있도록 테이블 뷰의 기본 선택 모델이 검색됩니다. 모델의 항목을 수정하지 않고 대신 뷰가 테이블의 왼쪽 상단에 표시할 몇 가지 항목을 선택합니다. 이렇게 하려면 선택할 영역의 왼쪽 상단 및 오른쪽 하단 항목에 해당하는 모델 인덱스를 검색해야 합니다:
QModelIndex topLeft; QModelIndex bottomRight; topLeft = model->index(0, 0, QModelIndex()); bottomRight = model->index(5, 2, QModelIndex());
모델에서 이러한 항목을 선택하고 테이블 보기에서 해당 변경 사항을 확인하려면 선택 개체를 구성한 다음 선택 모델에 적용해야 합니다:
QItemSelection selection(topLeft, bottomRight); selectionModel->select(selection, QItemSelectionModel::Select);
선택은 selection flags 의 조합으로 정의된 명령을 사용하여 선택 모델에 적용됩니다. 이 경우 사용된 플래그는 선택 개체에 기록된 항목이 이전 상태에 관계없이 선택 모델에 포함되도록 합니다. 결과 선택이 뷰에 표시됩니다.
선택 플래그에 정의된 다양한 작업을 사용하여 항목 선택을 수정할 수 있습니다. 이러한 작업의 결과인 선택은 복잡한 구조를 가질 수 있지만 선택 모델에 의해 효율적으로 표현됩니다. 다양한 선택 플래그를 사용하여 선택된 항목을 조작하는 방법은 선택 내용을 업데이트하는 방법을 살펴볼 때 설명합니다.
선택 상태 읽기
선택 모델에 저장된 모델 인덱스는 selectedIndexes() 함수를 사용하여 읽을 수 있습니다. 이 함수는 정렬되지 않은 모델 인덱스 목록을 반환하며, 어떤 모델에 대한 인덱스인지 알고 있는 한 반복할 수 있습니다:
const QModelIndexList indexes = selectionModel->selectedIndexes(); for (const QModelIndex &index : indexes) { QString text = QString("(%1,%2)").arg(index.row()).arg(index.column()); model->setData(index, text); }
위 코드는 범위 기반 for-루프를 사용하여 선택 모델에서 반환한 인덱스에 해당하는 항목을 반복하고 수정합니다.
선택 모델은 선택 영역의 변경을 나타내는 신호를 방출합니다. 이러한 신호는 다른 컴포넌트에 선택 영역 전체와 항목 모델에서 현재 초점이 맞춰진 항목의 변경 사항을 알립니다. selectionChanged () 신호를 슬롯에 연결하여 선택이 변경될 때 선택되거나 선택 해제되는 모델의 항목을 검사할 수 있습니다. 슬롯은 두 개의 QItemSelection 객체로 호출됩니다. 하나는 새로 선택된 항목에 해당하는 인덱스 목록을 포함하고, 다른 하나는 새로 선택 해제된 항목에 해당하는 인덱스를 포함합니다.
다음 코드에서는 selectionChanged() 신호를 수신하여 선택한 항목을 문자열로 채우고 선택 해제된 항목의 내용을 지우는 슬롯을 제공합니다.
void MainWindow::updateSelection(const QItemSelection &selected, const QItemSelection &deselected) { QModelIndexList items = selected.indexes(); for (const QModelIndex &index : std::as_const(items)) { QString text = QString("(%1,%2)").arg(index.row()).arg(index.column()); model->setData(index, text); } items = deselected.indexes(); for (const QModelIndex &index : std::as_const(items)) { model->setData(index, QString()); }
currentChanged() 신호를 두 개의 모델 인덱스로 호출되는 슬롯에 연결하여 현재 초점이 맞춰진 항목을 추적할 수 있습니다. 이는 이전에 초점이 맞춰진 항목과 현재 초점이 맞춰진 항목에 해당합니다.
다음 코드에서는 currentChanged() 신호를 수신하는 슬롯을 제공하고, 제공된 정보를 사용하여 QMainWindow 의 상태 표시줄을 업데이트합니다:
void MainWindow::changeCurrent(const QModelIndex ¤t, const QModelIndex &previous) { statusBar()->showMessage( tr("Moved from (%1,%2) to (%3,%4)") .arg(previous.row()).arg(previous.column()) .arg(current.row()).arg(current.column())); }
이러한 신호를 통해 사용자가 선택한 항목을 모니터링하는 것은 간단하지만 선택 모델을 직접 업데이트할 수도 있습니다.
선택 항목 업데이트하기
선택 명령은 QItemSelectionModel::SelectionFlag 에 정의된 선택 플래그의 조합으로 제공됩니다. 각 선택 플래그는 select() 함수 중 하나가 호출될 때 선택 모델에 선택 항목의 내부 레코드를 업데이트하는 방법을 알려줍니다. 가장 일반적으로 사용되는 플래그는 Select 플래그로, 선택 모델에 지정된 항목을 선택된 것으로 기록하도록 지시합니다. Toggle 플래그는 선택 모델이 지정된 항목의 상태를 반전시켜 주어진 선택 해제된 항목을 선택하고 현재 선택된 항목을 모두 선택 해제하도록 합니다. Deselect 플래그는 지정된 모든 항목의 선택을 취소합니다.
선택 모델의 개별 항목은 항목 선택을 생성하고 선택 모델에 적용하여 업데이트됩니다. 다음 코드에서는 Toggle 명령을 사용하여 지정된 항목의 선택 상태를 반전시켜 위에 표시된 테이블 모델에 두 번째 항목 선택을 적용합니다.
QItemSelection toggleSelection; topLeft = model->index(2, 1, QModelIndex()); bottomRight = model->index(7, 3, QModelIndex()); toggleSelection.select(topLeft, bottomRight); selectionModel->select(toggleSelection, QItemSelectionModel::Toggle);
이 작업의 결과는 테이블 보기에 표시되어 달성한 결과를 시각화할 수 있는 편리한 방법을 제공합니다:
기본적으로 선택 명령은 모델 인덱스에 지정된 개별 항목에 대해서만 작동합니다. 그러나 선택 명령을 설명하는 데 사용되는 플래그를 추가 플래그와 결합하여 전체 행과 열을 변경할 수 있습니다. 예를 들어 인덱스가 하나만 있는 select()를 Select 과 Rows 의 조합인 명령으로 호출하는 경우 참조된 항목이 포함된 전체 행이 선택됩니다. 다음 코드는 Rows 및 Columns 플래그의 사용을 보여줍니다:
QItemSelection columnSelection; topLeft = model->index(0, 1, QModelIndex()); bottomRight = model->index(0, 2, QModelIndex()); columnSelection.select(topLeft, bottomRight); selectionModel->select(columnSelection, QItemSelectionModel::Select | QItemSelectionModel::Columns); QItemSelection rowSelection; topLeft = model->index(0, 0, QModelIndex()); bottomRight = model->index(1, 0, QModelIndex()); rowSelection.select(topLeft, bottomRight); selectionModel->select(rowSelection, QItemSelectionModel::Select | QItemSelectionModel::Rows);
선택 모델에 4개의 인덱스만 제공되지만 Columns 및 Rows 선택 플래그를 사용하면 2개의 열과 2개의 행이 선택됩니다. 다음 이미지는 이 두 가지 선택의 결과를 보여줍니다:
예제 모델에서 수행된 명령은 모두 모델에 선택 항목을 누적하는 것과 관련이 있습니다. 선택을 지우거나 현재 선택을 새 선택으로 바꿀 수도 있습니다.
현재 선택을 새 선택으로 바꾸려면 다른 선택 플래그를 Current 플래그와 결합합니다. 이 플래그를 사용하는 명령은 선택 모델에 현재 모델 인덱스 컬렉션을 select() 호출에 지정된 인덱스로 바꾸도록 지시합니다. 새 선택을 추가하기 전에 모든 선택을 지우려면 다른 선택 플래그를 Clear 플래그와 결합합니다. 이렇게 하면 선택 모델의 모델 인덱스 컬렉션이 재설정되는 효과가 있습니다.
모델의 모든 항목 선택하기
모델의 모든 항목을 선택하려면 모델의 각 레벨에 대해 해당 레벨의 모든 항목을 포함하는 선택 항목을 만들어야 합니다. 이 작업은 주어진 상위 인덱스를 사용하여 왼쪽 상단 및 오른쪽 하단 항목에 해당하는 인덱스를 검색하는 방식으로 수행합니다:
QModelIndex topLeft = model->index(0, 0, parent); QModelIndex bottomRight = model->index(model->rowCount(parent)-1, model->columnCount(parent)-1, parent);
이러한 인덱스와 모델을 사용하여 선택 항목이 구성됩니다. 그런 다음 선택 모델에서 해당 항목이 선택됩니다:
QItemSelection selection(topLeft, bottomRight); selectionModel->select(selection, QItemSelectionModel::Select);
이 작업은 모델의 모든 레벨에 대해 수행해야 합니다. 최상위 항목의 경우 일반적인 방법으로 상위 인덱스를 정의합니다:
QModelIndex parent = QModelIndex();
계층적 모델의 경우 hasChildren() 함수를 사용하여 특정 항목이 다른 수준의 항목의 상위 항목인지 여부를 확인합니다.
새 모델 만들기
모델/뷰 구성 요소 간의 기능 분리를 통해 기존 뷰를 활용할 수 있는 모델을 만들 수 있습니다. 이 접근 방식을 사용하면 QListView, QTableView, QTreeView 과 같은 표준 그래픽 사용자 인터페이스 구성 요소를 사용하여 다양한 소스의 데이터를 표시할 수 있습니다.
QAbstractItemModel 클래스는 계층 구조로 정보를 정렬하는 데이터 소스를 지원할 수 있을 만큼 유연한 인터페이스를 제공하여 어떤 방식으로든 데이터를 삽입, 제거, 수정 또는 정렬할 수 있도록 합니다. 또한 드래그 앤 드롭 작업도 지원합니다.
QAbstractListModel 및 QAbstractTableModel 클래스는 더 간단한 비계층적 데이터 구조에 대한 인터페이스를 지원하며, 간단한 목록 및 테이블 모델의 시작점으로 사용하기 쉽습니다.
이 섹션에서는 간단한 읽기 전용 모델을 만들어 모델/보기 아키텍처의 기본 원리를 살펴봅니다. 이 섹션의 후반부에서는 이 간단한 모델을 사용자가 항목을 수정할 수 있도록 조정합니다.
보다 복잡한 모델의 예는 간단한 트리 모델 예제를 참조하세요.
QAbstractItemModel 서브클래스의 요구 사항은 모델 서브클래싱 참조 문서에 자세히 설명되어 있습니다.
모델 디자인하기
기존 데이터 구조에 대한 새 모델을 만들 때는 데이터에 대한 인터페이스를 제공하기 위해 어떤 유형의 모델을 사용해야 하는지 고려하는 것이 중요합니다. 데이터 구조가 항목의 목록이나 테이블로 표현될 수 있는 경우 QAbstractListModel 또는 QAbstractTableModel 클래스를 서브클래싱할 수 있는데, 이 클래스는 많은 함수에 대해 적절한 기본 구현을 제공하기 때문입니다.
그러나 기본 데이터 구조가 계층적 트리 구조로만 표현될 수 있는 경우에는 QAbstractItemModel 을 서브클래싱해야 합니다. 이 접근 방식은 단순 트리 모델 예제에서 사용됩니다.
이 섹션에서는 문자열 목록을 기반으로 간단한 모델을 구현하므로 QAbstractListModel 은 빌드하기에 이상적인 베이스 클래스를 제공합니다.
기본 데이터 구조의 형태가 무엇이든, 일반적으로 특수 모델의 표준 QAbstractItemModel API를 기본 데이터 구조에 보다 자연스럽게 액세스할 수 있는 API로 보완하는 것이 좋습니다. 이렇게 하면 모델을 데이터로 더 쉽게 채우면서도 다른 일반 모델/보기 컴포넌트가 표준 API를 사용하여 모델과 상호 작용할 수 있습니다. 아래 설명된 모델은 이러한 용도로만 사용할 수 있는 사용자 정의 생성자를 제공합니다.
읽기 전용 예제 모델
여기에 구현된 모델은 표준 QStringListModel 클래스를 기반으로 하는 단순한 비계층적 읽기 전용 데이터 모델입니다. 내부 데이터 소스로 QStringList 을 가지고 있으며, 작동하는 모델을 만드는 데 필요한 것만 구현합니다. 구현을 더 쉽게 하기 위해 QAbstractListModel 클래스는 목록 모델에 대한 합리적인 기본 동작을 정의하고 QAbstractItemModel 클래스보다 더 간단한 인터페이스를 노출하기 때문에 하위 클래스입니다.
모델을 구현할 때 QAbstractItemModel 은 데이터 자체를 저장하지 않고 뷰가 데이터에 액세스하는 데 사용하는 인터페이스만 제공한다는 점을 기억하는 것이 중요합니다. 최소한의 읽기 전용 모델의 경우 대부분의 인터페이스에 대한 기본 구현이 있으므로 몇 가지 함수만 구현하면 됩니다. 클래스 선언은 다음과 같습니다:
class StringListModel : public QAbstractListModel { Q_OBJECT public: StringListModel(const QStringList &strings, QObject *parent = nullptr) : QAbstractListModel(parent), stringList(strings) {} int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private: QStringList stringList; };
모델의 생성자 외에 rowCount()는 모델의 행 수를 반환하고 data()는 지정된 모델 인덱스에 해당하는 데이터 항목을 반환하는 두 가지 함수만 구현하면 됩니다.
잘 작동하는 모델은 headerData()도 구현하여 트리 및 테이블 뷰에 헤더에 표시할 내용을 제공합니다.
이 모델은 비계층적 모델이므로 부모-자식 관계에 대해 걱정할 필요가 없습니다. 계층형 모델이라면 index() 및 parent() 함수도 구현해야 합니다.
문자열 목록은 stringList
비공개 멤버 변수에 내부적으로 저장됩니다.
모델의 차원
모델의 행 수가 문자열 목록의 문자열 수와 같기를 원합니다. 이를 염두에 두고 rowCount() 함수를 구현했습니다:
int StringListModel::rowCount(const QModelIndex &parent) const { return stringList.count(); }
모델이 계층적이지 않으므로 상위 항목에 해당하는 모델 인덱스를 무시해도 됩니다. 기본적으로 QAbstractListModel 에서 파생된 모델은 하나의 열만 포함하므로 columnCount() 함수를 다시 구현할 필요가 없습니다.
모델 헤더 및 데이터
뷰의 항목에 대해 문자열 목록의 문자열을 반환하려고 합니다. data () 함수는 인덱스 인수에 해당하는 데이터 항목을 반환합니다:
QVariant StringListModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() >= stringList.size()) return QVariant(); if (role == Qt::DisplayRole) return stringList.at(index.row()); else return QVariant(); }
제공된 모델 인덱스가 유효하고 행 번호가 문자열 목록의 항목 범위 내에 있으며 요청된 역할이 지원되는 역할인 경우에만 유효한 QVariant 을 반환합니다.
QTreeView 및 QTableView 과 같은 일부 보기는 항목 데이터와 함께 헤더를 표시할 수 있습니다. 모델이 머리글이 있는 뷰에 표시되는 경우 머리글에 행 및 열 번호가 표시되기를 원합니다. headerData () 함수를 서브클래싱하여 헤더에 대한 정보를 제공할 수 있습니다:
QVariant StringListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) return QStringLiteral("Column %1").arg(section); else return QStringLiteral("Row %1").arg(section); }
다시 말하지만, 지원되는 역할인 경우에만 유효한 QVariant 을 반환합니다. 반환할 정확한 데이터를 결정할 때 헤더의 방향도 고려됩니다.
모든 뷰에 항목 데이터가 포함된 헤더가 표시되는 것은 아니며, 표시되는 뷰는 헤더를 숨기도록 구성할 수 있습니다. 그럼에도 불구하고 모델에서 제공하는 데이터에 대한 관련 정보를 제공하기 위해 headerData() 함수를 구현하는 것이 좋습니다.
항목은 여러 역할을 가질 수 있으며, 지정된 역할에 따라 다른 데이터를 제공할 수 있습니다. 우리 모델의 항목에는 DisplayRole 이라는 하나의 역할만 있으므로 지정된 역할에 관계없이 항목에 대한 데이터를 반환합니다. 그러나 DisplayRole 에 제공한 데이터를 다른 역할(예: 뷰가 도구 설명에 항목에 대한 정보를 표시하는 데 사용할 수 있는 ToolTipRole )에서 재사용할 수 있습니다.
편집 가능한 모델
읽기 전용 모델은 사용자에게 간단한 선택 사항을 표시하는 방법을 보여 주지만, 많은 애플리케이션에서는 편집 가능한 목록 모델이 훨씬 더 유용합니다. 읽기 전용으로 구현한 data() 함수를 변경하고 flags() 및 setData() 두 개의 추가 함수를 구현하여 항목을 편집 가능하도록 읽기 전용 모델을 수정할 수 있습니다. 다음 함수 선언이 클래스 정의에 추가됩니다:
Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
모델을 편집 가능하게 만들기
델리게이트는 편집기를 만들기 전에 항목이 편집 가능한지 여부를 확인합니다. 모델은 델리게이트에게 해당 항목이 편집 가능함을 알려야 합니다. 모델의 각 항목에 대해 올바른 플래그를 반환하여 이를 수행하며, 이 경우 모든 항목을 활성화하고 선택 및 편집이 모두 가능하도록 설정합니다:
Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemIsEnabled; return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; }
델리게이트가 실제 편집 프로세스를 어떻게 수행하는지 알 필요는 없습니다. 델리게이트가 모델에서 데이터를 설정할 수 있는 방법만 제공하면 됩니다. 이는 setData() 함수를 통해 이루어집니다:
bool StringListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.isValid() && role == Qt::EditRole) { stringList.replace(index.row(), value.toString()); emit dataChanged(index, index, {role}); return true; } return false; }
이 모델에서는 모델 인덱스에 해당하는 문자열 목록의 항목이 제공된 값으로 대체됩니다. 그러나 문자열 목록을 수정하기 전에 인덱스가 유효한지, 항목의 유형이 올바른지, 역할이 지원되는지 확인해야 합니다. 표준 항목 델리게이트에서 사용하는 역할이 EditRole 이므로 관례에 따라 역할은 을 사용해야 합니다. 그러나 부울 값의 경우 Qt::CheckStateRole 을 사용하고 Qt::ItemIsUserCheckable 플래그를 설정하면 값을 편집하는 데 체크박스가 사용됩니다. 이 모델의 기본 데이터는 모든 역할에 대해 동일하므로 이 세부 사항은 모델을 표준 컴포넌트와 더 쉽게 통합할 수 있도록 해줍니다.
데이터가 설정되면 모델은 뷰에 일부 데이터가 변경되었음을 알려야 합니다. 이는 dataChanged() 신호를 방출하여 수행됩니다. 데이터의 한 항목만 변경되었으므로 신호에 지정된 항목의 범위는 하나의 모델 인덱스로만 제한됩니다.
또한 Qt::EditRole 테스트를 추가하려면 data() 함수를 변경해야 합니다:
QVariant StringListModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() >= stringList.size()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) return stringList.at(index.row()); else return QVariant(); }
행 삽입 및 제거
모델에서 행과 열의 수를 변경할 수 있습니다. 문자열 목록 모델에서는 행 수만 변경하는 것이 합리적이기 때문에 행을 삽입하고 제거하는 함수만 다시 구현합니다. 이러한 함수는 클래스 정의에 선언되어 있습니다:
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
이 모델의 행은 목록의 문자열에 해당하므로 insertRows()
함수는 지정된 위치 앞에 여러 개의 빈 문자열을 문자열 목록에 삽입합니다. 삽입되는 문자열의 수는 지정된 행 수와 동일합니다.
상위 인덱스는 일반적으로 모델에서 행을 추가할 위치를 결정하는 데 사용됩니다. 이 경우에는 문자열의 최상위 목록이 하나뿐이므로 해당 목록에 빈 문자열을 삽입하기만 하면 됩니다.
bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent) { beginInsertRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { stringList.insert(position, ""); } endInsertRows(); return true; }
모델은 먼저 beginInsertRows() 함수를 호출하여 행 수가 곧 변경될 것임을 다른 컴포넌트에 알립니다. 이 함수는 삽입할 첫 번째와 마지막 새 행의 행 번호와 상위 항목의 모델 인덱스를 지정합니다. 문자열 목록을 변경한 후 endInsertRows()를 호출하여 작업을 완료하고 다른 컴포넌트에 모델의 차원이 변경되었음을 알리고 참을 반환하여 성공을 나타냅니다.
모델에서 행을 제거하는 함수도 간단하게 작성할 수 있습니다. 모델에서 제거할 행은 주어진 위치와 행 수로 지정됩니다. 구현을 단순화하기 위해 상위 인덱스를 무시하고 문자열 목록에서 해당 항목만 제거합니다.
bool StringListModel::removeRows(int position, int rows, const QModelIndex &parent) { beginRemoveRows(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { stringList.removeAt(position); } endRemoveRows(); return true; }
beginRemoveRows() 함수는 항상 기초 데이터가 제거되기 전에 호출되며, 제거할 첫 번째와 마지막 행을 지정합니다. 이렇게 하면 다른 컴포넌트가 데이터를 사용할 수 없게 되기 전에 데이터에 액세스할 수 있습니다. 행이 제거된 후 모델은 endRemoveRows()를 실행하여 작업을 완료하고 다른 컴포넌트에 모델의 차원이 변경되었음을 알립니다.
다음 단계
QListView 클래스를 사용하여 이 모델 또는 다른 모델에서 제공하는 데이터를 표시하여 모델의 항목을 세로 목록 형태로 표시할 수 있습니다. 문자열 목록 모델의 경우 이 보기는 항목을 조작할 수 있도록 기본 편집기도 제공합니다. 뷰 클래스에서 표준 뷰 클래스가 제공하는 가능성에 대해 살펴봅니다.
모델 하위 클래스 참조 문서( QAbstractItemModel )에서는 하위 클래스의 요구 사항에 대해 자세히 설명하며, 다양한 유형의 모델에서 다양한 기능을 사용하려면 구현해야 하는 가상 함수에 대한 가이드를 제공합니다.
아이템 뷰 편의성 클래스
항목 기반 위젯에는 용도를 반영하는 이름이 있습니다. QListWidget
은 항목 목록을, QTreeWidget
은 다단계 트리 구조를, QTableWidget
은 셀 항목의 표를 제공합니다. 각 클래스는 항목 선택 및 헤더 관리를 위한 공통 동작을 구현하는 QAbstractItemView
클래스의 동작을 상속합니다.
목록 위젯
단일 레벨 항목 목록은 일반적으로 QListWidget
및 여러 개의 QListWidgetItem
를 사용하여 표시되며, 목록 위젯은 다른 위젯과 동일한 방식으로 구성됩니다:
QListWidget *listWidget = new QListWidget(this);
목록 위젯을 만들 때 목록 항목을 목록 위젯에 바로 추가할 수 있습니다:
new QListWidgetItem(tr("Sycamore"), listWidget); new QListWidgetItem(tr("Chestnut"), listWidget); new QListWidgetItem(tr("Mahogany"), listWidget);
상위 목록 위젯 없이 목록 위젯을 만든 후 나중에 목록에 추가할 수도 있습니다:
QListWidgetItem *newItem = new QListWidgetItem; newItem->setText(itemText); listWidget->insertItem(row, newItem);
목록의 각 항목은 텍스트 레이블과 아이콘을 표시할 수 있습니다. 텍스트를 렌더링하는 데 사용되는 색상과 글꼴을 변경하여 항목의 모양을 사용자 지정할 수 있습니다. 도구 설명, 상태 팁 및 "이게 뭐예요?" 도움말을 모두 쉽게 구성하여 목록이 애플리케이션에 제대로 통합되도록 할 수 있습니다.
newItem->setToolTip(toolTipText); newItem->setStatusTip(toolTipText); newItem->setWhatsThis(whatsThisText);
기본적으로 목록의 항목은 만든 순서대로 표시됩니다. Qt::SortOrder 에 제공된 기준에 따라 항목 목록을 정렬하여 정방향 또는 역방향 알파벳 순서로 정렬된 항목 목록을 만들 수 있습니다:
트리 위젯
트리 또는 계층형 항목 목록은 QTreeWidget
및 QTreeWidgetItem
클래스에서 제공합니다. 트리 위젯의 각 항목은 자체적으로 하위 항목을 가질 수 있으며 여러 개의 정보 열을 표시할 수 있습니다. 트리 위젯은 다른 위젯과 마찬가지로 생성됩니다:
QTreeWidget *treeWidget = new QTreeWidget(this);
트리 위젯에 항목을 추가하려면 먼저 열의 개수를 설정해야 합니다. 예를 들어 두 개의 열을 정의하고 헤더를 만들어 각 열의 상단에 레이블을 제공할 수 있습니다:
treeWidget->setColumnCount(2); QStringList headers; headers << tr("Subject") << tr("Default"); treeWidget->setHeaderLabels(headers);
각 섹션의 레이블을 설정하는 가장 쉬운 방법은 문자열 목록을 제공하는 것입니다. 보다 정교한 헤더를 만들려면 트리 항목을 만들고 원하는 대로 꾸민 다음 이를 트리 위젯의 헤더로 사용할 수 있습니다.
트리 위젯의 최상위 항목은 트리 위젯을 부모 위젯으로 하여 구성됩니다. 임의의 순서로 삽입할 수도 있고, 각 항목을 만들 때 이전 항목을 지정하여 특정 순서로 나열되도록 할 수도 있습니다:
QTreeWidgetItem *cities = new QTreeWidgetItem(treeWidget); cities->setText(0, tr("Cities")); QTreeWidgetItem *osloItem = new QTreeWidgetItem(cities); osloItem->setText(0, tr("Oslo")); osloItem->setText(1, tr("Yes")); QTreeWidgetItem *planets = new QTreeWidgetItem(treeWidget, cities);
트리 위젯은 최상위 항목을 트리 내 더 깊은 곳에 있는 다른 항목과 약간 다르게 처리합니다. 트리 위젯의 takeTopLevelItem() 함수를 호출하여 트리 최상위 수준에서 항목을 제거할 수 있지만, 하위 수준의 항목은 상위 항목의 takeChild() 함수를 호출하여 제거합니다. 트리의 최상위 레벨에는 insertTopLevelItem() 함수를 사용하여 항목이 삽입됩니다. 트리의 하위 수준에서는 상위 항목의 insertChild() 함수가 사용됩니다.
트리의 최상위 수준과 하위 수준 간에 항목을 쉽게 이동할 수 있습니다. 항목이 최상위 항목인지 아닌지만 확인하면 되며, 이 정보는 각 항목의 parent()
함수에 의해 제공됩니다. 예를 들어 트리 위젯에서 현재 항목의 위치에 관계없이 해당 항목을 제거할 수 있습니다:
QTreeWidgetItem *parent = currentItem->parent(); int index; if (parent) { index = parent->indexOfChild(treeWidget->currentItem()); delete parent->takeChild(index); } else { index = treeWidget->indexOfTopLevelItem(treeWidget->currentItem()); delete treeWidget->takeTopLevelItem(index); }
트리 위젯의 다른 곳에 항목을 삽입하는 것도 동일한 패턴을 따릅니다:
QTreeWidgetItem *parent = currentItem->parent(); QTreeWidgetItem *newItem; if (parent) newItem = new QTreeWidgetItem(parent, treeWidget->currentItem()); else newItem = new QTreeWidgetItem(treeWidget, treeWidget->currentItem());
표 위젯
스프레드시트 애플리케이션에서 볼 수 있는 것과 유사한 항목으로 구성된 표는 QTableWidget
및 QTableWidgetItem
을 사용하여 구성됩니다. 이러한 위젯은 헤더와 그 안에 사용할 항목이 있는 스크롤링 테이블 위젯을 제공합니다.
표는 정해진 수의 행과 열로 만들거나 필요에 따라 크지 않은 표에 추가할 수 있습니다.
QTableWidget *tableWidget; tableWidget = new QTableWidget(12, 3, this);
항목은 테이블 외부에 구성한 후 필요한 위치에 테이블에 추가합니다:
QTableWidgetItem *newItem = new QTableWidgetItem(tr("%1").arg( pow(row, column+1))); tableWidget->setItem(row, column, newItem);
가로 및 세로 머리글은 표 외부에 항목을 구성하고 이를 머리글로 사용하여 표에 추가할 수 있습니다:
QTableWidgetItem *valuesHeaderItem = new QTableWidgetItem(tr("Values")); tableWidget->setHorizontalHeaderItem(0, valuesHeaderItem);
표의 행과 열은 0에서 시작한다는 점에 유의하세요.
공통 기능
각 편의성 클래스에는 각 클래스에서 동일한 인터페이스를 통해 사용할 수 있는 여러 가지 항목 기반 기능이 공통적으로 있습니다. 다음 섹션에서는 다양한 위젯에 대한 몇 가지 예와 함께 이러한 기능을 소개합니다. 각 위젯의 모델/보기 클래스 목록에서 사용된 각 기능의 사용법에 대한 자세한 내용을 확인하세요.
숨겨진 항목
때로는 항목 보기 위젯에서 항목을 제거하는 대신 숨길 수 있는 기능이 유용할 때가 있습니다. 위의 모든 위젯의 항목을 숨겼다가 나중에 다시 표시할 수 있습니다. 아이템이 숨겨져 있는지 여부는 isItemHidden() 함수를 호출하여 확인할 수 있으며, setItemHidden()
로 아이템을 숨길 수 있습니다.
이 작업은 항목 기반이므로 세 가지 편의 클래스 모두에 동일한 함수를 사용할 수 있습니다.
선택
항목이 선택되는 방식은 위젯의 선택 모드(QAbstractItemView::SelectionMode)에 의해 제어됩니다. 이 속성은 사용자가 항목을 하나 또는 여러 개 선택할 수 있는지 여부와 여러 항목 선택 시 선택 범위가 연속된 항목이어야 하는지 여부를 제어합니다. 선택 모드는 위의 모든 위젯에 대해 동일한 방식으로 작동합니다.
단일 항목 선택: 사용자가 위젯에서 단일 항목을 선택해야 하는 경우 기본 SingleSelection 모드가 가장 적합합니다. 이 모드에서는 현재 항목과 선택한 항목이 동일합니다. | |
다중 항목 선택: 이 모드에서는 비독점 체크박스를 독립적으로 토글할 수 있는 방식과 마찬가지로 사용자가 기존 선택 내용을 변경하지 않고 위젯의 모든 항목의 선택 상태를 토글할 수 있습니다. | |
확장 선택: 스프레드시트와 같이 인접한 여러 항목을 선택해야 하는 위젯에는 ExtendedSelection 모드가 필요합니다. 이 모드에서는 위젯의 연속된 범위의 항목을 마우스와 키보드로 모두 선택할 수 있습니다. 수정자 키를 사용하면 위젯에서 선택한 다른 항목과 인접하지 않은 여러 항목이 포함된 복잡한 선택도 만들 수 있습니다.사용자가 수정자 키를 사용하지 않고 항목을 선택하면 기존 선택 항목이 지워집니다. |
위젯에서 선택한 항목은 selectedItems()
함수를 사용하여 읽어들여 반복할 수 있는 관련 항목의 목록을 제공합니다. 예를 들어 다음 코드를 사용하여 선택한 항목 목록 내의 모든 숫자 값의 합계를 찾을 수 있습니다:
const QList<QTableWidgetItem *> selected = tableWidget->selectedItems(); int number = 0; double total = 0; for (QTableWidgetItem *item : selected) { bool ok; double value = item->text().toDouble(&ok); if (ok && !item->text().isEmpty()) { total += value; number++; } }
단일 선택 모드의 경우 현재 항목이 선택 영역에 포함됩니다. 다중 선택 및 확장 선택 모드에서는 사용자가 선택 항목을 구성한 방식에 따라 현재 항목이 선택 영역 내에 있지 않을 수 있습니다.
검색
개발자나 사용자에게 제공할 서비스에서 항목 보기 위젯 내에서 항목을 찾을 수 있으면 유용할 때가 많습니다. 세 가지 항목 보기 편의성 클래스 모두 공통된 findItems()
함수를 제공하여 가능한 한 일관되고 간단하게 검색할 수 있도록 합니다.
항목은 Qt::MatchFlags 에서 선택한 값으로 지정된 기준에 따라 포함된 텍스트로 검색됩니다. findItems()
함수를 사용하여 일치하는 항목 목록을 얻을 수 있습니다:
const QList<QTreeWidgetItem *> found = treeWidget->findItems( itemText, Qt::MatchWildcard); for (QTreeWidgetItem *item : found) { item->setSelected(true); // Show the item->text(0) for each item. }
위의 코드는 트리 위젯의 항목이 검색 문자열에 지정된 텍스트를 포함하는 경우 선택되도록 합니다. 이 패턴은 목록 및 표 위젯에서도 사용할 수 있습니다.
항목 보기에서 드래그 앤 드롭 사용하기
Qt의 드래그 앤 드롭 인프라는 모델/뷰 프레임워크에서 완벽하게 지원됩니다. 목록, 테이블, 트리의 항목을 뷰 내에서 드래그할 수 있으며, 데이터를 MIME 인코딩 데이터로 가져오고 내보낼 수 있습니다.
표준 뷰는 항목을 이동하여 표시되는 순서를 변경하는 내부 드래그 앤 드롭을 자동으로 지원합니다. 이러한 보기는 가장 단순하고 일반적인 용도로 구성되었기 때문에 기본적으로 드래그 앤 드롭이 활성화되어 있지 않습니다. 항목을 끌어서 이동할 수 있도록 하려면 뷰의 특정 속성을 활성화해야 하며 항목 자체에서도 끌어서 이동을 허용해야 합니다.
뷰에서 항목 내보내기만 허용하고 데이터를 끌어 놓을 수 없는 모델의 요구 사항은 완전히 사용하도록 설정된 끌어서 놓기 모델에 대한 요구 사항보다 적습니다.
새 모델에서 끌어서 놓기 지원을 사용하도록 설정하는 방법에 대한 자세한 내용은 모델 하위 클래스 참조를 참조하십시오.
편의 보기 사용
QListWidget, QTableWidget, QTreeWidget 에 사용되는 각 항목 유형은 기본적으로 서로 다른 플래그 집합을 사용하도록 구성되어 있습니다. 예를 들어 QListWidgetItem 또는 QTreeWidgetItem 각각은 처음에 활성화되어 있고, 확인 및 선택이 가능하며 끌어서 놓기 작업의 소스로 사용할 수 있고, QTableWidgetItem 각각은 편집하여 끌어서 놓기 작업의 대상으로 사용할 수도 있습니다.
모든 표준 항목에는 끌어서 놓기용 플래그가 하나 또는 둘 다 설정되어 있지만 일반적으로 뷰 자체에서 다양한 속성을 설정해야 끌어서 놓기 기본 지원을 활용할 수 있습니다:
- 항목 끌기를 사용하도록 설정하려면 뷰의 dragEnabled 속성을
true
으로 설정합니다. - 사용자가 뷰 내에서 내부 또는 외부 항목을 끌어 놓을 수 있도록 하려면 뷰의 viewport()의 acceptDrops 속성을
true
로 설정합니다. - 현재 끌기 중인 항목을 놓을 경우 사용자에게 해당 항목이 배치되는 위치를 표시하려면 뷰의 showDropIndicator 속성을 설정합니다. 이렇게 하면 사용자에게 뷰 내의 항목 배치에 대한 정보를 지속적으로 업데이트할 수 있습니다.
예를 들어 다음 코드 줄을 사용하여 목록 위젯에서 드래그 앤 드롭을 활성화할 수 있습니다:
QListWidget *listWidget = new QListWidget(this); listWidget->setSelectionMode(QAbstractItemView::SingleSelection); listWidget->setDragEnabled(true); listWidget->viewport()->setAcceptDrops(true); listWidget->setDropIndicatorShown(true);
그 결과 목록 위젯을 통해 뷰 내에서 항목을 복사할 수 있으며, 사용자가 동일한 유형의 데이터를 포함하는 뷰 간에 항목을 끌어서 놓을 수도 있습니다. 두 경우 모두 항목은 이동되지 않고 복사됩니다.
사용자가 뷰 내에서 항목을 이동할 수 있도록 하려면 목록 위젯의 dragDropMode 을 설정해야 합니다:
listWidget->setDragDropMode(QAbstractItemView::InternalMove);
모델/뷰 클래스 사용
드래그 앤 드롭을 위한 뷰 설정은 편의 뷰에 사용된 것과 동일한 패턴을 따릅니다. 예를 들어 QListView 은 QListWidget 과 같은 방식으로 설정할 수 있습니다:
QListView *listView = new QListView(this); listView->setSelectionMode(QAbstractItemView::ExtendedSelection); listView->setDragEnabled(true); listView->setAcceptDrops(true); listView->setDropIndicatorShown(true);
뷰에 표시되는 데이터에 대한 액세스는 모델에 의해 제어되므로 사용되는 모델도 드래그 앤 드롭 작업을 지원해야 합니다. 모델에서 지원하는 작업은 QAbstractItemModel::supportedDropActions() 함수를 다시 구현하여 지정할 수 있습니다. 예를 들어 다음 코드를 사용하여 복사 및 이동 작업을 활성화할 수 있습니다:
Qt::DropActions DragDropListModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; }
Qt::DropActions 의 모든 값 조합을 지정할 수 있지만, 이를 지원하도록 모델을 작성해야 합니다. 예를 들어 목록 모델에서 Qt::MoveAction 을 올바르게 사용하려면 모델에서 직접 또는 기본 클래스에서 구현을 상속하여 QAbstractItemModel::removeRows() 의 구현을 제공해야 합니다.
항목에 드래그 앤 드롭 사용
모델은 QAbstractItemModel::flags() 함수를 다시 구현하여 적절한 플래그를 제공함으로써 끌어서 놓을 수 있는 항목과 끌어서 놓기를 허용하는 항목을 뷰에 표시합니다.
예를 들어 QAbstractListModel 기반의 간단한 목록을 제공하는 모델은 반환되는 플래그에 Qt::ItemIsDragEnabled 및 Qt::ItemIsDropEnabled 값이 포함되어 있는지 확인하여 각 항목에 대해 드래그 앤 드롭을 활성화할 수 있습니다:
Qt::ItemFlags DragDropListModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QStringListModel::flags(index); if (index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; else return Qt::ItemIsDropEnabled | defaultFlags; }
항목을 모델의 최상위 레벨로 끌어 놓을 수 있지만 드래그는 유효한 항목에 대해서만 활성화된다는 점에 유의하세요.
위 코드에서는 모델이 QStringListModel 에서 파생되었으므로 해당 모델의 구현을 flags() 함수로 호출하여 기본 플래그 집합을 얻습니다.
내보낸 데이터 인코딩
드래그 앤 드롭 작업으로 모델에서 데이터 항목을 내보내면 하나 이상의 MIME 유형에 해당하는 적절한 형식으로 인코딩됩니다. 모델은 QAbstractItemModel::mimeTypes() 함수를 다시 구현하여 표준 MIME 유형 목록을 반환함으로써 항목을 제공하는 데 사용할 수 있는 MIME 유형을 선언합니다.
예를 들어 일반 텍스트만 제공하는 모델은 다음과 같은 구현을 제공합니다:
QStringList DragDropListModel::mimeTypes() const { QStringList types; types << "application/vnd.text.list"; return types; }
또한 모델은 광고된 형식으로 데이터를 인코딩하는 코드도 제공해야 합니다. 이는 다른 드래그 앤 드롭 작업과 마찬가지로 QAbstractItemModel::mimeData() 함수를 다시 구현하여 QMimeData 객체를 제공함으로써 달성할 수 있습니다.
다음 코드는 주어진 인덱스 목록에 해당하는 각 데이터 항목이 일반 텍스트로 인코딩되어 QMimeData 객체에 저장되는 방법을 보여줍니다.
QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &index : indexes) { if (index.isValid()) { QString text = data(index, Qt::DisplayRole).toString(); stream << text; } } mimeData->setData("application/vnd.text.list", encodedData); return mimeData; }
모델 인덱스 목록이 함수에 제공되므로 이 접근 방식은 계층적 모델과 비계층적 모델 모두에서 사용할 수 있을 만큼 일반적입니다.
사용자 정의 데이터 유형은 meta objects 으로 선언해야 하며 스트림 연산자를 구현해야 합니다. 자세한 내용은 QMetaObject 클래스 설명을 참조하세요.
모델에 삭제된 데이터 삽입하기
특정 모델이 삭제된 데이터를 처리하는 방식은 그 유형(목록, 표 또는 트리)과 그 내용이 사용자에게 표시되는 방식에 따라 달라집니다. 일반적으로, 삭제된 데이터를 수용하기 위해 취하는 접근 방식은 모델의 기본 데이터 저장소에 가장 적합한 방식이어야 합니다.
모델 유형에 따라 삭제된 데이터를 처리하는 방식이 다릅니다. 목록 및 테이블 모델은 데이터 항목이 저장되는 플랫 구조만 제공합니다. 따라서 뷰의 기존 항목에 데이터를 끌어 놓을 때 새 행(및 열)을 삽입하거나 제공된 데이터 중 일부를 사용하여 모델의 항목 내용을 덮어쓸 수 있습니다. 트리 모델은 종종 기본 데이터 저장소에 새 데이터를 포함하는 하위 항목을 추가할 수 있으므로 사용자 입장에서 보다 예측 가능한 방식으로 작동합니다.
삭제된 데이터는 모델의 QAbstractItemModel::dropMimeData() 재구현을 통해 처리됩니다. 예를 들어, 단순한 문자열 목록을 처리하는 모델은 기존 항목에 드롭된 데이터를 모델의 최상위 수준(즉, 유효하지 않은 항목)에 드롭된 데이터와 별도로 처리하는 구현을 제공할 수 있습니다.
모델은 QAbstractItemModel::canDropMimeData()를 다시 구현하여 특정 항목에 대한 드롭을 금지하거나 드롭된 데이터에 따라 드롭을 금지할 수 있습니다.
모델은 먼저 작업을 수행해야 하는지, 제공된 데이터가 사용할 수 있는 형식인지, 모델 내의 대상이 유효한지 확인해야 합니다:
bool DragDropListModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { Q_UNUSED(action); Q_UNUSED(row); Q_UNUSED(parent); if (!data->hasFormat("application/vnd.text.list")) return false; if (column > 0) return false; return true; } bool DragDropListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!canDropMimeData(data, action, row, column, parent)) return false; if (action == Qt::IgnoreAction) return true;
단순한 한 열 문자열 목록 모델의 경우 제공된 데이터가 일반 텍스트가 아니거나 드롭에 지정된 열 번호가 유효하지 않은 경우 실패를 나타낼 수 있습니다.
모델에 삽입할 데이터는 기존 항목에 끌어다 놓는지 여부에 따라 다르게 처리됩니다. 이 간단한 예제에서는 기존 항목 사이, 목록의 첫 번째 항목 앞과 마지막 항목 뒤에 드롭을 허용하려고 합니다.
드롭이 발생하면 상위 항목에 해당하는 모델 인덱스가 유효하여 항목에서 드롭이 발생했음을 나타내거나 유효하지 않으므로 모델의 최상위 수준에 해당하는 뷰의 어딘가에서 드롭이 발생했음을 나타냅니다.
int beginRow; if (row != -1) beginRow = row;
처음에는 제공된 행 번호를 검사하여 상위 인덱스가 유효한지 여부에 관계없이 이 번호를 사용하여 모델에 항목을 삽입할 수 있는지 확인합니다.
else if (parent.isValid()) beginRow = parent.row();
상위 모델 인덱스가 유효하면 항목에 대한 드롭이 발생합니다. 이 간단한 목록 모델에서는 항목의 행 번호를 알아내고 이 값을 사용하여 삭제된 항목을 모델의 최상위 레벨에 삽입합니다.
else beginRow = rowCount(QModelIndex());
뷰의 다른 곳에서 드롭이 발생하여 해당 행 번호를 사용할 수 없는 경우 모델의 최상위 수준에 항목을 추가합니다.
계층적 모델에서는 항목에 드롭이 발생하면 새 항목을 해당 항목의 하위 항목으로 모델에 삽입하는 것이 좋습니다. 여기에 표시된 간단한 예에서는 모델에 레벨이 하나만 있으므로 이 접근 방식은 적절하지 않습니다.
가져온 데이터 디코딩하기
dropMimeData()의 각 구현은 또한 데이터를 디코딩하여 모델의 기본 데이터 구조에 삽입해야 합니다.
간단한 문자열 목록 모델의 경우 인코딩된 항목을 디코딩하여 QStringList 로 스트리밍할 수 있습니다:
QByteArray encodedData = data->data("application/vnd.text.list"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QStringList newItems; int rows = 0; while (!stream.atEnd()) { QString text; stream >> text; newItems << text; ++rows; }
그런 다음 문자열을 기본 데이터 저장소에 삽입할 수 있습니다. 일관성을 위해 모델의 자체 인터페이스를 통해 이 작업을 수행할 수 있습니다:
insertRows(beginRow, rows, QModelIndex()); for (const QString &text : std::as_const(newItems)) { QModelIndex idx = index(beginRow, 0, QModelIndex()); setData(idx, text); beginRow++; } return true; }
일반적으로 모델은 QAbstractItemModel::insertRows() 및 QAbstractItemModel::setData() 함수의 구현을 제공해야 합니다.
프록시 모델
모델/보기 프레임워크에서 단일 모델에서 제공하는 데이터 항목은 여러 개의 보기에서 공유할 수 있으며, 각 보기는 동일한 정보를 완전히 다른 방식으로 나타낼 수 있습니다. 사용자 지정 보기 및 위임은 동일한 데이터에 대해 완전히 다른 표현을 제공하는 효과적인 방법입니다. 그러나 애플리케이션은 종종 동일한 데이터의 처리된 버전에 대해 기존 보기를 제공해야 하는 경우가 있습니다(예: 항목 목록에 대해 서로 다르게 정렬된 보기).
뷰의 내부 기능으로 정렬 및 필터링 작업을 수행하는 것이 적절해 보이지만, 이 접근 방식은 잠재적으로 비용이 많이 드는 이러한 작업의 결과를 여러 뷰가 공유하는 것을 허용하지 않습니다. 모델 자체 내에서 정렬을 포함하는 다른 접근 방식은 각 뷰가 가장 최근 처리 작업에 따라 정리된 데이터 항목을 표시해야 하는 유사한 문제를 야기합니다.
이 문제를 해결하기 위해 모델/뷰 프레임워크는 프록시 모델을 사용하여 개별 모델과 뷰 간에 제공되는 정보를 관리합니다. 프록시 모델은 뷰의 관점에서 일반 모델처럼 작동하고 해당 뷰를 대신하여 소스 모델의 데이터에 액세스하는 구성 요소입니다. 모델/뷰 프레임워크에서 사용하는 신호 및 슬롯은 자체 모델과 소스 모델 사이에 배치된 프록시 모델의 수에 관계없이 각 뷰가 적절하게 업데이트되도록 보장합니다.
프록시 모델 사용
프록시 모델은 기존 모델과 뷰 사이에 원하는 수만큼 삽입할 수 있습니다. Qt에는 표준 프록시 모델인 QSortFilterProxyModel 이 제공되며, 일반적으로 직접 인스턴스화되어 사용되지만 사용자 정의 필터링 및 정렬 동작을 제공하기 위해 서브클래스를 만들 수도 있습니다. QSortFilterProxyModel 클래스는 다음과 같은 방식으로 사용할 수 있습니다:
QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(parent); filterModel->setSourceModel(stringListModel); QListView *filteredView = new QListView; filteredView->setModel(filterModel);
프록시 모델은 QAbstractItemModel 에서 상속되므로 모든 종류의 보기에 연결할 수 있으며 보기 간에 공유할 수 있습니다. 또한 파이프라인 배열에서 다른 프록시 모델에서 얻은 정보를 처리하는 데 사용할 수도 있습니다.
QSortFilterProxyModel 클래스는 애플리케이션에서 직접 인스턴스화하여 사용할 수 있도록 설계되었습니다. 이 클래스를 서브클래싱하고 필요한 비교 연산을 구현하여 보다 전문화된 프록시 모델을 만들 수 있습니다.
프록시 모델 사용자 정의하기
일반적으로 프록시 모델에서 사용되는 처리 유형에는 각 데이터 항목을 소스 모델의 원래 위치에서 프록시 모델의 다른 위치로 매핑하는 작업이 포함됩니다. 일부 모델에서는 프록시 모델에 해당 위치가 없는 항목이 있을 수 있으며, 이러한 모델은 프록시 모델을 필터링합니다. 뷰는 프록시 모델에서 제공하는 모델 인덱스를 사용하여 항목에 액세스하며, 여기에는 소스 모델이나 해당 모델의 원래 항목 위치에 대한 정보가 포함되어 있지 않습니다.
QSortFilterProxyModel 를 사용하면 소스 모델의 데이터를 뷰에 제공하기 전에 필터링할 수 있으며, 소스 모델의 콘텐츠를 미리 정렬된 데이터로 뷰에 제공할 수도 있습니다.
사용자 지정 필터링 모델
QSortFilterProxyModel 클래스는 상당히 다재다능하고 다양한 일반적인 상황에서 사용할 수 있는 필터링 모델을 제공합니다. 고급 사용자를 위해 QSortFilterProxyModel 클래스를 하위 클래스화하여 사용자 지정 필터를 구현할 수 있는 메커니즘을 제공할 수 있습니다.
QSortFilterProxyModel 의 서브클래스는 프록시 모델의 모델 인덱스가 요청되거나 사용될 때마다 호출되는 두 개의 가상 함수를 다시 구현할 수 있습니다:
- filterAcceptsColumn()는 소스 모델의 일부에서 특정 열을 필터링하는 데 사용됩니다.
- filterAcceptsRow()는 소스 모델의 일부에서 특정 행을 필터링하는 데 사용됩니다.
QSortFilterProxyModel 에서 위 함수의 기본 구현은 모든 항목이 뷰에 전달되도록 참을 반환하며, 이러한 함수를 다시 구현하면 개별 행과 열을 필터링하기 위해 거짓을 반환해야 합니다.
사용자 정의 정렬 모델
QSortFilterProxyModel 인스턴스는 std::stable_sort() 함수를 사용하여 소스 모델의 항목과 프록시 모델의 항목 간에 매핑을 설정하여 소스 모델의 구조를 수정하지 않고도 정렬된 항목 계층을 뷰에 노출할 수 있습니다. 사용자 지정 정렬 동작을 제공하려면 lessThan() 함수를 다시 구현하여 사용자 지정 비교를 수행합니다.
모델 서브클래스 참조
모델 서브클래스는 QAbstractItemModel 베이스 클래스에 정의된 많은 가상 함수의 구현을 제공해야 합니다. 구현해야 하는 이러한 함수의 수는 뷰에 단순한 목록, 테이블 또는 복잡한 항목 계층 구조를 제공하는지 여부에 따라 모델 유형에 따라 다릅니다. QAbstractListModel 및 QAbstractTableModel 에서 상속하는 모델은 해당 클래스에서 제공하는 함수의 기본 구현을 활용할 수 있습니다. 트리와 같은 구조로 데이터 항목을 노출하는 모델은 QAbstractItemModel 에 있는 많은 가상 함수에 대한 구현을 제공해야 합니다.
모델 서브클래스에서 구현해야 하는 함수는 세 가지 그룹으로 나눌 수 있습니다:
- 항목 데이터 처리: 모든 모델은 뷰 및 위임자가 모델의 차원을 쿼리하고, 항목을 검사하고, 데이터를 검색할 수 있도록 하는 함수를 구현해야 합니다.
- 탐색 및 인덱스 생성: 계층적 모델은 뷰가 노출되는 트리형 구조를 탐색하고 항목에 대한 모델 인덱스를 얻기 위해 호출할 수 있는 함수를 제공해야 합니다.
- 드래그 앤 드롭 지원 및 MIME 유형 처리: 모델은 내부 및 외부 드래그 앤 드롭 작업이 수행되는 방식을 제어하는 함수를 상속합니다. 이러한 함수를 사용하면 데이터 항목을 다른 컴포넌트와 애플리케이션이 이해할 수 있는 MIME 유형으로 설명할 수 있습니다.
항목 데이터 처리
모델은 제공하는 데이터에 대한 다양한 수준의 액세스를 제공할 수 있습니다: 단순한 읽기 전용 컴포넌트일 수도 있고, 일부 모델은 크기 조정 작업을 지원할 수도 있으며, 항목을 편집할 수 있는 모델도 있습니다.
읽기 전용 액세스
모델에서 제공하는 데이터에 읽기 전용 액세스를 제공하려면 모델의 서브클래스에 다음 함수를 구현해야 합니다:
flags() | 모델에서 제공하는 각 항목에 대한 정보를 얻기 위해 다른 컴포넌트에서 사용합니다. 많은 모델에서 플래그 조합에는 Qt::ItemIsEnabled 및 Qt::ItemIsSelectable 이 포함되어야 합니다. |
data() | 뷰 및 델리게이트에 항목 데이터를 제공하는 데 사용됩니다. 일반적으로 모델은 Qt::DisplayRole 및 애플리케이션별 사용자 역할에 대한 데이터만 제공하면 되지만 Qt::ToolTipRole, Qt::AccessibleTextRole, Qt::AccessibleDescriptionRole 에 대한 데이터도 제공하는 것이 좋습니다. 각 역할과 관련된 유형에 대한 자세한 내용은 Qt::ItemDataRole 열거형 문서를 참조하세요. |
headerData() | 뷰에 헤더에 표시할 정보를 제공합니다. 이 정보는 헤더 정보를 표시할 수 있는 뷰에서만 검색됩니다. |
rowCount() | 모델에 의해 노출되는 데이터 행 수를 제공합니다. |
이 네 가지 함수는 목록 모델(QAbstractListModel 하위 클래스) 및 테이블 모델(QAbstractTableModel 하위 클래스)을 포함한 모든 유형의 모델에서 구현되어야 합니다.
또한 다음 함수는 QAbstractTableModel 및 QAbstractItemModel 의 직접 하위 클래스에서 구현해야 합니다:
columnCount() | 모델에서 노출되는 데이터의 열 수를 제공합니다. 목록 모델은 QAbstractListModel 에서 이미 구현되어 있으므로 이 함수를 제공하지 않습니다. |
편집 가능한 항목
편집 가능한 모델은 데이터 항목을 수정할 수 있으며 행과 열을 삽입 및 제거할 수 있는 함수를 제공할 수도 있습니다. 편집을 사용하려면 다음 함수를 올바르게 구현해야 합니다:
flags() | 각 항목에 대해 적절한 플래그 조합을 반환해야 합니다. 특히 이 함수가 반환하는 값에는 읽기 전용 모델의 항목에 적용되는 값 외에 Qt::ItemIsEditable 이 포함되어야 합니다. |
setData() | 지정된 모델 인덱스와 연결된 데이터의 항목을 수정하는 데 사용됩니다. 사용자 인터페이스 요소에서 제공하는 사용자 입력을 수락하려면 이 함수는 Qt::EditRole 와 연결된 데이터를 처리해야 합니다. 구현은 Qt::ItemDataRole 에 지정된 다양한 종류의 역할과 관련된 데이터도 받아들일 수 있습니다. 데이터 항목을 변경한 후 모델은 dataChanged() 신호를 전송하여 다른 컴포넌트에 변경 사항을 알려야 합니다. |
setHeaderData() | 가로 및 세로 헤더 정보를 수정하는 데 사용됩니다. 데이터 항목을 변경한 후 모델은 headerDataChanged() 신호를 전송하여 다른 컴포넌트에 변경 사항을 알려야 합니다. |
크기 조정 가능한 모델
모든 유형의 모델은 행의 삽입 및 제거를 지원할 수 있습니다. 테이블 모델과 계층형 모델도 열의 삽입 및 제거를 지원할 수 있습니다. 모델의 차원이 변경되기 전후에 다른 컴포넌트에 변경 사항을 알리는 것이 중요합니다. 따라서 모델의 크기를 조정할 수 있도록 다음 함수를 구현할 수 있지만 구현 시 적절한 함수를 호출하여 연결된 뷰 및 델리게이트에 알릴 수 있도록 해야 합니다:
insertRows() | 모든 유형의 모델에 새 행과 데이터 항목을 추가하는 데 사용됩니다. 구현은 새 행을 기본 데이터 구조에 삽입하기 전에 beginInsertRows()를 호출하고 그 직후에 endInsertRows()를 호출해야 합니다. |
removeRows() | 모든 유형의 모델에서 행과 행에 포함된 데이터 항목을 제거하는 데 사용됩니다. 구현은 기본 데이터 구조에서 행을 제거하기 전에 beginRemoveRows()를 호출하고 그 직후에 endRemoveRows()를 호출해야 합니다. |
insertColumns() | 테이블 모델과 계층형 모델에 새 열과 데이터 항목을 추가하는 데 사용됩니다. 구현은 기본 데이터 구조에 새 열을 삽입하기 전에 beginInsertColumns()를 호출하고 그 직후에 endInsertColumns()를 호출해야 합니다. |
removeColumns() | 테이블 모델 및 계층형 모델에서 열과 열에 포함된 데이터 항목을 제거하는 데 사용됩니다. 구현은 열을 기본 데이터 구조에서 제거하기 전에 beginRemoveColumns()를 호출하고 그 직후에 endRemoveColumns()를 호출해야 합니다. |
일반적으로 이러한 함수는 작업이 성공하면 참을 반환해야 합니다. 그러나 연산이 부분적으로만 성공한 경우도 있을 수 있습니다(예: 지정된 행 수보다 적은 수의 행이 삽입될 수 있는 경우). 이러한 경우 모델은 연결된 컴포넌트가 해당 상황을 처리할 수 없음을 나타내기 위해 거짓을 반환해야 합니다.
크기 조정 API 구현에서 호출되는 함수에서 발생하는 신호는 데이터를 사용할 수 없게 되기 전에 연결된 컴포넌트가 조치를 취할 수 있는 기회를 제공합니다. 삽입 및 제거 작업을 시작 및 종료 함수로 캡슐화하면 모델에서 persistent model indexes 을 올바르게 관리할 수 있습니다.
일반적으로 시작 및 종료 함수는 모델의 기본 구조에 대한 변경 사항을 다른 컴포넌트에 알릴 수 있습니다. 내부 재구성, 데이터 정렬 또는 기타 구조 변경과 같이 모델 구조가 더 복잡하게 변경되는 경우에는 다음 순서를 수행해야 합니다:
- layoutAboutToBeChanged() 신호를 내보냅니다.
- 모델의 구조를 나타내는 내부 데이터를 업데이트합니다.
- changePersistentIndexList()를 사용하여 영구 인덱스를 업데이트합니다.
- layoutChanged() 신호를 내보냅니다.
이 시퀀스는 더 높은 수준의 편리한 보호 방법 대신 모든 구조적 업데이트에 사용할 수 있습니다. 예를 들어, 200만 개의 행으로 구성된 모델에서 홀수 행을 모두 제거해야 하는 경우, 이는 각각 1개의 요소로 구성된 100만 개의 할인 범위입니다. 시작RemoveRows와 끝RemoveRows를 백만 번 사용할 수도 있지만 이는 분명히 비효율적입니다. 대신, 필요한 모든 영구 인덱스를 한 번에 업데이트하는 단일 레이아웃 변경으로 신호를 보낼 수 있습니다.
모델 데이터의 지연 모집단
모델 데이터의 지연 모집단을 사용하면 뷰에서 실제로 필요할 때까지 모델에 대한 정보 요청을 효과적으로 연기할 수 있습니다.
일부 모델은 원격 소스에서 데이터를 가져와야 하거나 데이터 구성 방식에 대한 정보를 얻기 위해 시간이 많이 걸리는 작업을 수행해야 합니다. 일반적으로 뷰는 모델 데이터를 정확하게 표시하기 위해 가능한 한 많은 정보를 요청하므로 불필요한 데이터 후속 요청을 줄이기 위해 뷰에 반환되는 정보의 양을 제한하는 것이 유용할 수 있습니다.
특정 항목의 하위 항목 수를 찾는 데 많은 비용이 드는 계층적 모델에서는 모델의 rowCount() 구현이 필요할 때만 호출되도록 하는 것이 유용합니다. 이러한 경우 hasChildren() 함수를 다시 구현하여 뷰가 자식의 존재 여부를 확인하고 QTreeView 의 경우 부모 항목에 적절한 장식을 그리는 저렴한 방법을 제공할 수 있습니다.
hasChildren()를 다시 구현하면 true
또는 false
를 반환하므로 뷰에서 rowCount()를 호출하여 자식 수를 확인할 필요가 없을 수 있습니다. 예를 들어 부모 항목이 확장되어 표시되지 않은 경우 QTreeView 는 자식 수를 알 필요가 없습니다.
많은 항목에 자식이 있다는 것을 알고 있는 경우 hasChildren()를 다시 구현하여 무조건 true
을 반환하는 것이 유용한 접근 방식이 될 수 있습니다. 이렇게 하면 모델 데이터의 초기 모집단을 최대한 빠르게 만들면서 나중에 각 항목에 대해 하위 항목이 있는지 검사할 수 있습니다. 유일한 단점은 사용자가 존재하지 않는 하위 항목을 보려고 시도할 때까지 일부 보기에서 하위 항목이 없는 항목이 잘못 표시될 수 있다는 것입니다.
탐색 및 모델 인덱스 생성
계층적 모델은 뷰가 노출하는 트리와 같은 구조를 탐색하고 항목의 모델 인덱스를 얻기 위해 호출할 수 있는 함수를 제공해야 합니다.
부모 및 자식
뷰에 노출되는 구조는 기본 데이터 구조에 의해 결정되므로 각 모델 서브클래스가 다음 함수의 구현을 제공하여 자체 모델 인덱스를 만드는 것은 각 모델 서브클래스의 몫입니다:
index() | 부모 항목에 대한 모델 인덱스가 주어지면 이 함수를 사용하면 뷰와 위임자가 해당 항목의 하위 항목에 액세스할 수 있습니다. 지정된 행, 열 및 상위 모델 인덱스에 해당하는 유효한 하위 항목을 찾을 수 없는 경우 이 함수는 잘못된 모델 인덱스인 QModelIndex()를 반환해야 합니다. |
parent() | 지정된 하위 항목의 부모에 해당하는 모델 인덱스를 제공합니다. 지정된 모델 인덱스가 모델의 최상위 항목에 해당하거나 모델에 유효한 상위 항목이 없는 경우 이 함수는 빈 QModelIndex() 생성자로 생성된 잘못된 모델 인덱스를 반환해야 합니다. |
위의 두 함수는 createIndex() 팩토리 함수를 사용하여 다른 컴포넌트가 사용할 인덱스를 생성합니다. 모델 인덱스가 나중에 해당 항목에 다시 연결될 수 있도록 이 함수에 고유 식별자를 제공하는 것이 일반적입니다.
드래그 앤 드롭 지원 및 MIME 유형 처리
모델/뷰 클래스는 드래그 앤 드롭 작업을 지원하여 많은 애플리케이션에 충분한 기본 동작을 제공합니다. 그러나 드래그 앤 드롭 작업 중에 항목이 인코딩되는 방식, 기본적으로 복사 또는 이동되는지 여부, 기존 모델에 삽입되는 방식 등을 사용자 지정할 수도 있습니다.
또한 편의 보기 클래스는 기존 개발자가 기대하는 것과 유사한 특수 동작을 구현합니다. 편의 뷰 섹션에서는 이러한 동작에 대한 개요를 제공합니다.
MIME 데이터
기본적으로 기본 제공 모델 및 뷰는 내부 MIME 유형(application/x-qabstractitemmodeldatalist
)을 사용하여 모델 인덱스에 대한 정보를 전달합니다. 이는 각 항목의 행 및 열 번호와 각 항목이 지원하는 역할에 대한 정보를 포함하는 항목 목록에 대한 데이터를 지정합니다.
이 MIME 유형을 사용하여 인코딩된 데이터는 직렬화할 항목이 포함된 QModelIndexList 과 함께 QAbstractItemModel::mimeData()를 호출하여 얻을 수 있습니다.
사용자 정의 모델에서 드래그 앤 드롭 지원을 구현하는 경우 다음 함수를 다시 구현하여 데이터의 항목을 특수 형식으로 내보낼 수 있습니다:
mimeData() | 이 함수를 재구현하여 기본값 application/x-qabstractitemmodeldatalist 내부 MIME 형식이 아닌 다른 형식으로 데이터를 반환할 수 있습니다.서브클래스는 기본 QMimeData 객체를 기본 클래스로부터 가져와서 추가 형식으로 데이터를 추가할 수 있습니다. |
많은 모델의 경우 text/plain
및 image/png
과 같은 MIME 유형으로 표현되는 일반적인 형식으로 항목의 내용을 제공하는 것이 유용합니다. QMimeData::setImageData (), QMimeData::setColorData(), QMimeData::setHtml() 함수를 사용하여 이미지, 색상 및 HTML 문서를 QMimeData 객체에 쉽게 추가할 수 있습니다.
드래그 앤 드롭한 데이터 허용
뷰에 끌어서 놓기 작업을 수행하면 기본 모델이 쿼리되어 지원되는 작업 유형과 허용할 수 있는 MIME 유형을 결정합니다. 이 정보는 QAbstractItemModel::supportedDropActions() 및 QAbstractItemModel::mimeTypes() 함수에 의해 제공됩니다. QAbstractItemModel 에서 제공하는 구현을 재정의하지 않는 모델은 복사 연산과 항목에 대한 기본 내부 MIME 유형을 지원합니다.
직렬화된 항목 데이터를 뷰에 끌어 놓으면 QAbstractItemModel::dropMimeData() 구현을 사용하여 데이터가 현재 모델에 삽입됩니다. 이 함수의 기본 구현은 모델의 데이터를 덮어쓰지 않고 대신 데이터의 항목을 항목의 형제 또는 해당 항목의 자식으로 삽입하려고 시도합니다.
QAbstractItemModel 의 기본 구현을 빌트인 MIME 유형에 활용하려면 새 모델에서 다음 함수의 재구현을 제공해야 합니다:
insertRows() | 이러한 함수를 사용하면 QAbstractItemModel::dropMimeData()에서 제공하는 기존 구현을 사용하여 모델이 새 데이터를 자동으로 삽입할 수 있습니다. |
insertColumns() | |
setData() | 새 행과 열을 항목으로 채울 수 있습니다. |
setItemData() | 이 함수는 새 항목 채우기를 보다 효율적으로 지원합니다. |
다른 형태의 데이터를 받으려면 이 함수를 다시 구현해야 합니다:
supportedDropActions() | 모델에서 허용하는 드래그 앤 드롭 작업의 유형을 나타내는 drop actions 의 조합을 반환하는 데 사용됩니다. |
mimeTypes() | 모델에서 디코딩하고 처리할 수 있는 MIME 유형 목록을 반환하는 데 사용됩니다. 일반적으로 모델에 입력할 때 지원되는 MIME 유형은 외부 컴포넌트에서 사용하기 위해 데이터를 인코딩할 때 사용할 수 있는 유형과 동일합니다. |
dropMimeData() | 드래그 앤 드롭 작업으로 전송된 데이터의 실제 디코딩을 수행하고, 모델에서 설정할 위치를 결정하고, 필요한 경우 새 행과 열을 삽입합니다. 이 함수가 하위 클래스에서 구현되는 방식은 각 모델에서 노출되는 데이터의 요구 사항에 따라 다릅니다. |
dropMimeData() 함수를 구현하여 행이나 열을 삽입 또는 제거하여 모델의 차원을 변경하거나 데이터 항목을 수정하는 경우 모든 관련 신호가 방출되도록 주의를 기울여야 합니다. setData (), insertRows(), insertColumns()와 같은 하위 클래스에서 다른 함수의 재구현을 간단히 호출하여 모델이 일관되게 작동하도록 하는 것이 유용할 수 있습니다.
드래그 작업이 제대로 작동하도록 하려면 모델에서 데이터를 제거하는 다음 함수를 다시 구현하는 것이 중요합니다:
- removeRows()
- removeRow()
- removeColumns()
- removeColumn()
항목 보기에서 끌어서 놓기에 대한 자세한 내용은 항목 보기에서 끌어서 놓기 사용을 참조하세요.
편의 보기
편의 보기(QListWidget, QTableWidget, QTreeWidget)는 기본 드래그 앤 드롭 기능을 재정의하여 유연성은 떨어지지만 많은 애플리케이션에 적합한 보다 자연스러운 동작을 제공합니다. 예를 들어, QTableWidget 의 셀에 데이터를 끌어다 놓아 기존 콘텐츠를 전송 중인 데이터로 대체하는 것이 더 일반적이므로 기본 모델은 모델에 새 행과 열을 삽입하기보다는 대상 항목의 데이터를 설정합니다. 편의 보기에서 끌어서 놓기에 대한 자세한 내용은 항목 보기에서 끌어서 놓기 사용을 참조하세요.
대용량 데이터에 대한 성능 최적화
canFetchMore() 함수는 부모에 사용 가능한 데이터가 더 있는지 확인하고 그에 따라 true
또는 false를 반환합니다. fetchMore () 함수는 지정된 부모를 기준으로 데이터를 가져옵니다. 예를 들어 증분 데이터를 포함하는 데이터베이스 쿼리에서 QAbstractItemModel 을 채우기 위해 이 두 함수를 결합할 수 있습니다. canFetchMore()을 다시 구현하여 가져올 데이터가 더 있는지 표시하고 fetchMore()을 구현하여 필요에 따라 모델을 채웁니다.
또 다른 예로는 동적으로 채워지는 트리 모델을 들 수 있는데, 트리 모델에서 분기가 확장되면 fetchMore()를 다시 구현합니다.
fetchMore()를 다시 구현하여 모델에 행을 추가하는 경우 beginInsertRows() 및 endInsertRows()를 호출해야 합니다. 또한 canFetchMore()와 fetchMore()는 기본 구현이 거짓을 반환하고 아무 작업도 수행하지 않으므로 다시 구현해야 합니다.
모델/뷰 클래스
이러한 클래스는 모델/뷰 디자인 패턴을 사용하여 기초 데이터(모델)를 사용자가 데이터를 표시하고 조작하는 방식(뷰)과 분리하여 유지합니다.
모델에서 데이터 항목을 표시하고 편집하는 데 사용됩니다. | |
항목 모델 클래스의 추상 인터페이스 | |
항목 뷰 클래스의 기본 기능 | |
1차원 목록 모델을 만들기 위해 서브클래싱할 수 있는 추상 모델 | |
정렬, 필터링 또는 기타 데이터 처리 작업을 수행할 수 있는 프록시 항목 모델용 기본 클래스 | |
테이블 모델을 생성하기 위해 서브클래스화할 수 있는 추상 모델 | |
열 보기의 모델/보기 구현 | |
여러 소스 모델을 프록시하여 행을 연결합니다. | |
데이터 모델의 섹션과 위젯 간 매핑 | |
로컬 파일 시스템용 데이터 모델 | |
항목 보기의 머리글 행 또는 머리글 열 | |
수정되지 않은 소스 모델 프록시 | |
모델의 데이터 항목에 대한 표시 및 편집 기능 | |
QItemEditorCreatorBase를 서브클래싱하지 않고도 항목 편집기 작성자 베이스를 만들 수 있습니다. | |
새 항목 편집기 생성자를 구현할 때 서브클래싱해야 하는 추상 베이스 클래스 | |
뷰 및 델리게이트에서 항목 데이터를 편집하기 위한 위젯 | |
모델에서 선택한 항목에 대한 정보를 관리합니다. | |
뷰에서 선택한 항목을 추적합니다. | |
모델에서 선택한 다양한 항목에 대한 정보 관리 | |
모델에 목록 또는 아이콘 보기 | |
항목 기반 목록 위젯 | |
QListWidget 항목 뷰 클래스와 함께 사용하기 위한 항목입니다. | |
데이터 모델에서 데이터를 찾는 데 사용 | |
역할과 해당 역할에 연결된 데이터를 보유합니다. | |
QModelRoleData 객체에 걸쳐 있음 | |
데이터 모델에서 데이터를 찾는 데 사용 | |
다른 모델과 뷰 간에 전달된 데이터 정렬 및 필터링 지원 | |
QStandardItemModel 클래스와 함께 사용하기 위한 항목 | |
QItemEditorCreatorBase를 서브클래스하지 않고도 위젯을 등록할 수 있는 가능성 | |
사용자 지정 데이터를 저장하기 위한 일반 모델 | |
뷰에 문자열을 제공하는 모델 | |
모델의 데이터 항목에 대한 표시 및 편집 기능 | |
테이블 뷰의 기본 모델/뷰 구현 | |
기본 모델을 사용하는 항목 기반 테이블 뷰 | |
QTableWidget 클래스와 함께 사용하기 위한 항목 | |
모델 인덱스 및 선택 모델을 사용하지 않고 모델에서 선택과 상호 작용하는 방법 | |
트리 뷰의 기본 모델/뷰 구현 | |
미리 정의된 트리 모델을 사용하는 트리 뷰 | |
QTreeWidget 편의 클래스와 함께 사용하기 위한 항목 | |
QTreeWidget 인스턴스에서 항목을 반복하는 방법 |
관련 예제
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.