Tłumaczenie i adaptacja materiałów: dr Tomasz Xięski. Na podstawie prezentacji udostępnionych przez Digia Plc. na licencji CC.

Podobne dokumenty
Tłumaczenie i adaptacja materiałów: dr Tomasz Xięski. Na podstawie prezentacji udostępnionych przez Digia Plc. na licencji CC.

Tłumaczenie i adaptacja materiałów: dr Tomasz Xięski. Na podstawie prezentacji udostępnionych przez Digia Plc. na licencji CC.

Tłumaczenie i adaptacja materiałów: dr Tomasz Xięski. Na podstawie prezentacji udostępnionych przez Digia Plc. na licencji CC.

Podstawy programowania w Qt4

Qt sygnały i designer

Qt - podstawowe cechy

Oprogramowanie i wykorzystanie stacji roboczych. Wykład 4

Programowanie w środowiskach RAD QtCreator, Qt i C++

Tabela wewnętrzna - definicja

Qt in Education. Sieć I drukowanie

Laboratorium 7 Blog: dodawanie i edycja wpisów

Backend Administratora

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Zaawansowane programowanie w C++ (PCP)

ASP.NET MVC. Podstawy. Zaawansowane programowanie internetowe Instrukcja nr 3

Szablony funkcji i szablony klas

Qt - dialogi. Bogdan Kreczmer. ZPCiR ICT PWR pokój 307 budynek C3

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Qt sygnały i sloty. Bogdan Kreczmer. Katedra Cybernetyki i Robotyki Wydział Elektroniki Politechnika Wrocławska

The Graphics View Framework. Oprogramowanie i wykorzystanie stacji roboczych. Wykład 5. he Graphics View Framework - architektura

Builder (budowniczy) Cel: Przykład:

Architektura interfejsu użytkownika

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Algorytmy i Struktury Danych. Anna Paszyńska

Programowanie w środowiskach RAD Qt i C++

Projekt Hurtownia, realizacja skojarzeń dostawców i produktów

Dynamiczne i wydajne tworzenie interfejsu. Piotr Michałkiewicz

Wzorce Strukturalne. Adapter: opis. Tomasz Borzyszkowski

Systemy Rozproszone. Spis treści. Temat projektu: Regułowy system analizujacy logi. autorzy: Rafał Sadłowski, Sebastian Falkus, Michał Różycki

Szablony klas, zastosowanie szablonów w programach

Aplikacje w środowisku Java

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

Zaawansowane programowanie w języku C++ Klasy w C++

PHP 5 język obiektowy

Wykład 3 Składnia języka C# (cz. 2)

Technologie i usługi internetowe cz. 2

Implementacja aplikacji sieciowych z wykorzystaniem środowiska Qt

Programowanie obiektowe

Enkapsulacja, dziedziczenie, polimorfizm

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Programowanie obiektowe

Wypożyczalnia VIDEO. Technologie obiektowe

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Programowanie obiektowe

ECDL/ICDL Użytkowanie baz danych Moduł S1 Sylabus - wersja 6.0

TP1 - TABELE PRZESTAWNE od A do Z

Projektowanie klas c.d. Projektowanie klas przykład

Mechanizm dziedziczenia

Lokalizacja jest to położenie geograficzne zajmowane przez aparat. Miejsce, w którym zainstalowane jest to urządzenie.

Kompleksowe tworzenie aplikacji klasy Desktop z wykorzystaniem SWT i

Systemy operacyjne na platformach mobilnych

Wykład 8: klasy cz. 4

Widoki zagnieżdżone, layout. 1. Wprowadzenie Repozytoria danych

Podstawowe funkcjonalności interfejsu. - co warto wiedzieć o interfejsie Mozart-a

Komputerowe systemy na rynkach finansowych. wykład 5. MQL4 - funkcje operujące na obiektach wykresu

Sieciowe Technologie Mobilne. Laboratorium 2

UNIWERSYTET RZESZOWSKI KATEDRA INFORMATYKI

Polimorfizm. dr Jarosław Skaruz

Projektowanie Graficznych Interfejsów Użytkownika Robert Szmurło

I. Program II. Opis głównych funkcji programu... 19

Temat: Ułatwienia wynikające z zastosowania Frameworku CakePHP podczas budowania stron internetowych

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

TabControl kontrolka odpowiedzialna za wyświetlenie zestawu zakładek. PageControl podobnie jak TabControl ale posiada wbudowane strony.

Wykład 4: Klasy i Metody

Systemy wirtualnej rzeczywistości. Komponenty i serwisy

Programowanie obiektowe

5. Bazy danych Base Okno bazy danych

Programowanie obiektowe

UONET+ moduł Dziennik. Praca z rozkładami materiału nauczania

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

Szkolenie. IBM Lotus - Podstawy projektowania aplikacji w Domino Designer 8.5. Strona szkolenia Terminy szkolenia Rejestracja na szkolenie Promocje

Instrukcja obsługi Zaplecza epk w zakresie zarządzania tłumaczeniami opisów procedur, publikacji oraz poradników przedsiębiorcy

Leszek Stasiak Zastosowanie technologii LINQ w

Kurs programowania aplikacji bazodanowych

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Programowanie w Javie 1 Wykład i Ćwiczenia 3 Programowanie obiektowe w Javie cd. Płock, 16 października 2013 r.

Temat 3. Projektowanie interfejsu użytkonwnika Kalkulator pierwszy program dla IOS

Klasy i obiekty cz II

Programowanie obiektowe

Dokumentacja do API Javy.

Instrukcja użytkownika

Programowanie obiektowe

Ćwiczenie 7 z Podstaw programowania. Język C++, programy pisane w nieobiektowym stylu programowania. Zofia Kruczkiewicz

ECDL/ICDL Użytkowanie baz danych Moduł S1 Sylabus - wersja 5.0

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Wstęp do Programowania 2

Języki programowania imperatywnego

Aktywności są związane z ekranem i definiują jego wygląd. Dzieje się to poprzez podpięcie do aktywności odpowiedniego widoku.

5.3. Tabele. Tworzenie tabeli. Tworzenie tabeli z widoku projektu. Rozdział III Tworzenie i modyfikacja tabel

Podstawy JavaScript ćwiczenia

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Wstęp do wiadomości teoretycznych (nie, nie jest to masło maślane ani wstęp, wstępów proszę cierpliwie czytać)

Informatyka Edytor tekstów Word 2010 dla WINDOWS cz.3

W tej sekcji znajdują się foldery gromadzące wszystkie pliki znajdujące się w Twoim projekcie. Nazwa folderu odpowiada rodzaj owi załącznika.

Listy powiązane zorientowane obiektowo

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

Transkrypt:

Model MVC w Qt

Tłumaczenie i adaptacja materiałów: dr Tomasz Xięski. Na podstawie prezentacji udostępnionych przez Digia Plc. na licencji CC. 2012 Digia Plc. The enclosed Qt Materials are provided under the Creative Commons Attribution-Share Alike 2.5 License Agreement. The full license text is available here: http://creativecommons.org/licenses/by-sa/2.5/legalcode. Digia, Qt and the Digia and Qt logos are the registered trademarks of Digia Plc. in Finland and other countries worldwide.

Celem wzorca MVC jest separacja danych (model) wizualizacji (widok) modyfikacji (kontroler) Widok Model Kontroler Każdy z elementów wchodzących w jego skład ma ściśle określony zestaw obowiązków i nie powinno się ich mieszać dokonując jak najczęściej tzw. refactoring kodu.

Oddziela dane od ich graficznej reprezentacji Unika się powielania danych Może pokazywać te same dane w wielu widokach Może wykorzystywać ten sam widok dla wielu różnych danych Oddziela warstwę wizualną od modyfikacji Jeden widok może być wykorzystywany przez wiele kontrolerów (wyświetlanie, edycja, usuwanie)

Implementacja modelu MVC w Qt jest nieco inna niż klasyczna, ale działa w ten sam sposób: Warstwa modelu i widoku jest obecna, ale kontroler nazywany jest delegatem i ma większe możliwości Delegat odpowiada również za zmianę widoku podczas edytowania wartości elementu Występuje dodatkowo tzw. model zaznaczenia (ang. selection model), który synchronizuje zaznaczone elementy danych dla wielu widoków. Model Delegat Model zaznaczenia Widok

Wszystkie modele oparte są o klasę QAbstractItemModel, która wspiera: Listy dane w jednej kolumnie, wiele wierszy Tabele dane w wielu wierszach i kolumnach Drzewa zagnieżdżone tabele Dane nie muszą być trzymane w modelu mogą pochodzić np. z pliku lub bazy danych.

Model ten przechowuje wiele elementów w formie jednej kolumny. Każdy element jest adresowany przez QModelIndex uzyskuje się dzięki temu separację między reprezentacją danych a sposobem dostępu do nich.

Model oparty o tabele rozmieszcza elementy w siatce składającej się z wierszy i kolumn Dostęp do danych: QModelIndex indexa = model->index(0, 0, QModelIndex()); QModelIndex indexb = model->index(1, 1, QModelIndex()); QModelIndex indexc = model->index(2, 1, QModelIndex());

Analizowany model może być postrzegany jako agregacja zagnieżdżonych tabel. Dostęp do każdego elementu odbywa się przez podanie rodzica, numeru wiersza i kolumny. Dostęp do elementów: QModelIndex indexa = model->index(0, 0, QModelIndex()); QModelIndex indexc = model->index(2, 1, QModelIndex()); QModelIndex indexb = model->index(1, 0, indexa);

Każdy model ma metodę data() używaną do odczytu danych QVariant QAbstractItemModel::data( const QModelIndex &index, int role) const Drugi argument określa używaną aktualnie rolę (domyślnie Qt::DisplayRole): DecorationRole dla ikon, kolorów itp EditRole dane przystosowane do edycji FontRole używana domyślnie czcionka CheckStateRole rola do przechowywania zaznaczenia elementów

Używany jest do adresacji indywidualnych elementów w danym modelu QModelIndex zapewnia następujące metody: data(role) dostęp do danych child(row, column) dostęp do potomka parent() dostęp do rodzica Do tworzenia nowych modeli zaleca się wykorzystanie klasy QAbstractItemModel, która zapewnia m.in. metody: index(row, column, parent=qmodelindex()) zwraca indeks elementu dla zadanego modelu rowcount(parent=qmodelindex()) liczba wierszy (dla zadanego rodzica) columncount(parent=qmodelindex()) liczba kolumn (dla zadanego rodzica) parent(index) rodzic zadanego elementu

Qt dostarcza szeregu gotowych modeli, wśród których najpopularniejsze to: QStringListModel model do przechowywania listy ciągów znaków QFileSystemModel model umożliwiający dostęp do systemu plików QStandardItemModel model przechowujący obiekty typu QStandardItem. Wykorzystywany do tworzenia list, tablic, drzew. QSqlQueryModel, QSqlTableModel, QSqlRelationalTableModel modele wykorzystywane do interakcji z bazą danych

Wszystkie widoki dziedziczą po klasie QAbstractItemView Najważniejsze dostępne widoki to: QListView wyświetla tekst i/lub ikony QTableView QTreeView QColumnView

Wyświetla pojedynczą kolumnę zestawu danych Właściwość modelcolumn określa którą kolumnę należy wyświetlić Dwa tryby: IconMode oraz ListMode

Pokazuje siatkę elementów: Metod hiderow oraz hidecolumn służą do ukrywania elementów Za ponowne wyświetlanie elementów odpowiadają metody showrow oraz showcolumn

Umożliwia dostosowanie się do zawartości wykorzystując metody resizecolumnstocontents oraz resizerowstocontents Dostęp do nagłówków gwarantują metody verticalheader lub horizontalheader Właściwość stretchlastsection powoduje rozciągnięcie zawartości do rozmiarów komponentu inaczej może zostać puste miejsce Nagłówki mogą być widoczne lub nie Dostęp do pasków przewijania umożliwiają właściwości horizontalscrollbarpolicy oraz verticalscrollbarpolicy

Struktura drzewiasta Użyj setrowhidden oraz setcolumnhidden by pokazać bądź ukryć zawartość Użyj expandall, expandtodepth oraz collapseall by kontrolować jak wiele danych pokazać naraz

Hierarchia w poziomie Na ostatnim poziomie może agregować komponent do graficznego pokazania danych np. miniatury obrazka

Wykorzystując QDataWidgetMapper możliwe jest powiązanie danych z poszczególnymi widgetami QDataWidgetMapper *mapper = new QDataWidgetMapper; mapper->setmodel(model); mapper->addmapping(cityedit, 0); mapper->addmapping(populationedit, 1); mapper->tofirst(); connect(nextbutton, SIGNAL(clicked()), mapper, SLOT(toNext())); connect(prevbutton, SIGNAL(clicked()), mapper, SLOT(toPrevious()));

W niektórych zastosowaniach separacja modelu od widoku nie jest wskazana: Gdy dane nie są powielane Gdy model musi indywidualnie przetworzyć dane (implikując wykonanie ich kopii) Dla takich zastosowań wykorzystuje się klasy QListWidget, QTableWidget oraz QTreeWidget Przechowują one elementy kolejno typu QListWidgetItem, QTableWidgetItem oraz QTreeWidgetItem.

Delegat odpowiada za edytowanie i wizualną reprezentację elementu (w tym stanie) Widok wykorzystuje i wchodzi w interakcję z delegatem Wszystkie delegaty implementują interfejs QAbstractItemDelegate Standardowo wykorzystywany jest delegat QStyledItemDelegate Model Delegat Model zaznaczenia Widok

Delegat QStyledItemDelegate akceptuje następujące typy Role Types danych CheckStateRole Qt::CheckState DecorationStyle DisplayRole EditRole QIcon, QPixmap, QImage and QColor QString (QVariant::toString()) Klasa QItemEditorFactor określa jaki widget zostanie wykorzystany dla danego typu Type bool double int / unsigned int QDate QDateTime QPixmap QString QTime Widget QComboBox QDoubleSpinBox QSpinBox QDateEdit QDateTimeEdit QLabel QLineEdit QTimeEdit

Możliwa jest implementacja własnych delegatów do obsługi rysowania i/lub edycji Jeżeli interesuje nas tylko edycja, najprościej dokonać redefinicji klasy QItemEditorCreatorBase Delegaty przyporządkowywane są do całego widoku, bądź poszczególnych kolumn i wierszy

Należy dokonać redefinicji metod paint oraz sizehint class BarDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit BarDelegate(int maxrange, QObject *parent = 0); void paint(qpainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizehint(const QStyleOptionViewItem &option, const QModelIndex &index) const; private: int m_maxrange; };

BarDelegate::BarDelegate(int maxrange, QObject *parent) : QStyledItemDelegate(parent), m_maxrange(maxrange) { } QSize BarDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { return QSize(100, 1); } Implementacja konstruktora jest trywialna przypisywana jest wartość pola m_maxrange. Implementacja sizehint ustawia maksymalny rozmiar.

void BarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if(index.data().canconvert<int>()) { QRect barrect = QRect(option.rect.topLeft(), QSize(option.rect.width()*((qreal)index.data().toInt()/(qreal)m_maxRange), option.rect.height())); barrect.adjust(0, 2, 0, -2); if(option.state & QStyle::State_Selected) { painter->fillrect(option.rect, option.palette.highlight()); painter->fillrect(barrect, option.palette.highlightedtext()); } else painter->fillrect(barrect, option.palette.text()); } else QStyledItemDelegate::paint(painter, option, index); } Zakłada się, że dane można zrzutować na typ integer w przeciwnym razie wywoływane jest domyślne zachowanie

tableview->setmodel(model); tableview->setitemdelegateforcolumn(1, new BarDelegate(3000000, this));

Należy dokonać redefinicji metod: createeditor, seteditordata, setmodeldata oraz updateeditorgeometry class BarDelegate : public QStyledItemDelegate { Q_OBJECT public:... QWidget *createeditor(qwidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const; void seteditordata(qwidget *editor, const QModelIndex &index) const; void updateeditorgeometry(qwidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setmodeldata(qwidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;... Należy wykorzystywać rolę EditRole zamiast DisplayRole przy edycji

QWidget *BarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const { QSlider *slider = new QSlider(parent); slider->setrange(0, m_maxrange); slider->setorientation(qt::horizontal); slider->setautofillbackground(true); } return slider; void BarDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { QSlider *slider = qobject_cast<qslider*>(editor); if(slider) slider->setgeometry(option.rect); }

void BarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QSlider *slider = qobject_cast<qslider*>(editor); if(slider) slider->setvalue(index.data(qt::editrole).toint()); } void BarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QSlider *slider = qobject_cast<qslider*>(editor); if(slider) model->setdata(index, slider->value(), Qt::EditRole); }

tableview->setmodel(model); tableview->setitemdelegateforcolumn(1, new BarDelegate(3000000, this));

Tworząc swoje delegaty często zachodzi potrzeba przekazania dodatkowych danych między modelem a delegatem Umożliwia to mechanizm tworzenia własnych ról Użyj Qt::UserRole jako pierwszą class CustomRoleModel : public QAbstractListModel { Q_OBJECT public: enum MyTypes { FooRole = Qt::UserRole, BarRole, BazRole };...

Możliwe jest sortowanie i filtrowanie modeli wykorzystując warstwę pośrednią tzw. proxy QAbstractProxyModel dostarcza Połączenie między modelami Połączenie miedzy zaznaczonymi elementami (selection model) Klasa QSortFilterProxyModel uproszcza zadanie dostarczając metod do sortowania i filtrowania danych Właściwość dynamicsortfilter określa czy rezultat ma być buforowany czy generowany dynamicznie

Jeżeli ustawiono właściwość sortingenabled kliknięcie na nagłówek sortuje wartość w danej kolumnie Dotyczy to komponentów QTableView oraz QTreeView Wykorzystując QSortFilterProxyModel możliwe jest posortowanie określonej kolumny i określonej roli sortrole która rola jest brana pod uwagę (domyślnie DisplayRole) sortcasesensitivity czy wielkość liter ma znaczenie

QSortFilterProxyModel *sortingmodel = new QSortFilterProxyModel(this); sortingmodel->sort(0, Qt::AscendingOrder); sortingmodel->setdynamicsortfilter(true); sortingmodel->setsourcemodel(model); nonsortedview->setmodel(model); sortedview->setmodel(sortingmodel);

By zaimplementować bardziej złożony algorytm sortowania należy zredefiniować metodę lessthan bool MySortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { if(left.data().tostring().length() == right.data().tostring().length()) return left.data().tostring() < right.data().tostring(); else return (left.data().tostring().length() < right.data().tostring().length()); } MySortProxyModel *customsortmodel = new MySortProxyModel(this); customsortmodel->sort(0, Qt::DescendingOrder); customsortmodel->setdynamicsortfilter(true); customsortmodel->setsourcemodel(model); customsortedview->setmodel(customsortmodel);

Filtrowanie umożliwia redukcję liczby wierszy i kolumn w modelu: filterregexp / filterwildcard / filterfixedstring możliwe jest używanie wyrażeń regularnych bądź określonego ciągu do dopasowania filtercasesensitivity wielkość liter może być brana pod uwagę filterrole filtrowana jest określona rola filterkeycolumn klucz na podstawie którego filtrowane są rekordy (jeżeli nie podano, klucz tworzą wszystkie kolumny)

QSortFilterProxyModel *filteringmodel = new QSortFilterProxyModel(this); filteringmodel->setfilterwildcard("*stad*"); filteringmodel->setfilterkeycolumn(0); filteringmodel->setdynamicsortfilter(true); filteringmodel->setsourcemodel(model); nonfilteredview->setmodel(model); filteredview->setmodel(filteringmodel);

Implementacja bardziej złożonych filtrów wymaga redefinicji metod filteracceptrow oraz filteracceptcolumn bool filteracceptsrow(int sourcerow, const QModelIndex &sourceparent) const { const QModelIndex &index = sourcemodel()->index(sourcerow, filterkeycolumn(), sourceparent); return index.data().tostring().contains("berg") index.data().tostring().contains("stad"); } MyFilterProxyModel *customfiltermodel = new MyFilterProxyModel(this); customfiltermodel->setfilterkeycolumn(0); customfiltermodel->setdynamicsortfilter(true); customfiltermodel->setsourcemodel(model); customfilteredview->setmodel(customfiltermodel);

Wybór danych obsługiwany jest przez dedykowany model zaznaczenia (ang. selection model) Możliwym jest wprowadzenie ograniczeń zaznaczania: Pojedyncze elementy / wiersze / kolumny Pojedyncze zaznaczenie/ ciągłe / rozszerzone / wielokrotne / brak Model wybranych elementów może być współdzielony między widokami Model Delegat Model zaznaczenia Widok

view->setselectionbehavior( QAbstractItemView::SelectItems); view->setselectionmode( QAbstractItemView::SingleSelection); view->setselectionbehavior( QAbstractItemView::SelectRows); view->setselectionmode( QAbstractItemView::ContiguousSelection); view->setselectionbehavior( view->setselectionmode( QAbstractItemView::SelectColumns); QAbstractItemView::ExtendedSelection);

Współdzielenie zaznaczenia między różnymi widokami: listview->setmodel(model); tableview->setmodel(model); listview->setselectionmodel( tableview->selectionmodel());

Połączenia należy zestawiać z modelem zaznaczenia a nie konkretnym widokiem connect(view->selectionmodel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateSelectionStats())); void Widget::updateSelectionStats() { indexeslabel->settext(qstring::number(view->selectionmodel()->selectedindexes().count())); rowslabel->settext(qstring::number(view->selectionmodel()->selectedrows().count())); columnslabel->settext(qstring::number(view->selectionmodel()->selectedcolumns().count())); } removebutton->setenabled(view->selectionmodel()->selectedindexes().count() > 0);

Modele powinny dziedziczyć po klasie QAbstractItemModel Dla modeli nie wykorzystujących koncepcji drzewa, łatwiej skorzystać z prostszych klas: QAbstractListModel dla pojedynczej listy QAbstractTableModel dla tabeli

wymagane Własny model użytkownika wyświetlający dane na liście powinien zredefiniować: rowcount liczba wierszy w modelu data dostarcza dane do widoku opcjonalne ale zalecane headerdata zapewnia wiersz nagłówka dla modeli edytowalnych flags by wskazać, które elementy można modyfikować, zaznaczać itp. setdata ustawianie wartości w modelu (pod zadanym indeksem)

Minimalna definicja tego modelu (bez możliwości edycji) Zakłada się brak wiersza nagłówka. class ListModel : public QAbstractListModel { Q_OBJECT public: explicit ListModel(QObject *parent = 0); int rowcount(const QModelIndex &parent=qmodelindex()) const; QVariant data(const QModelIndex &index, int role=qt::displayrole) const; };

int ListModel::rowCount(const QModelIndex &parent) const { return 10; } QVariant ListModel::data(const QModelIndex &index, int role) const { if(!index.isvalid() role!=qt::displayrole) return QVariant(); } return QVariant(index.row()+1);

QListView *listview = new QListView(this); listview->setmodel(new ListModel(this));

Modele reprezentujące dane tabelaryczne dziedziczą po klasie QAbstractTableModel Konieczna jest redefinicja następujących metod: columncount określającą liczbę kolumn Oraz pozostałych znanych z modeli listowych: rowcount, data, headerdata, flags, setdata

Tym razem jest to wersja edytowalna class TableModel : public QAbstractTableModel { Q_OBJECT public: explicit TableModel(QObject *parent = 0); int rowcount(const QModelIndex &parent=qmodelindex()) const; int columncount(const QModelIndex &parent=qmodelindex()) const; QVariant data(const QModelIndex &index, int role=qt::displayrole) const; QVariant headerdata(int section, Qt::Orientation orientation, int role=qt::displayrole) const; bool setdata(const QModelIndex &index, const QVariant &value, int role=qt::editrole); Qt::ItemFlags flags(const QModelIndex &index) const; private: int m_data[10][10]; };

TableModel::TableModel(QObject *parent) : QAbstractTableModel(parent) { for(int i=0; i<10; ++i) for(int j=0; j<10; ++j) m_data[i][j] = (i+1)*(j+1); } int TableModel::rowCount(const QModelIndex &parent) const { return 10; } int TableModel::columnCount(const QModelIndex &parent) const { return 10; }

QVariant TableModel::data(const QModelIndex &index, int role) const { if(!index.isvalid()) return QVariant(); if(role == Qt::DisplayRole role == Qt::EditRole) return QVariant(m_data[index.row()][index.column()]); } return QVariant(); QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const { if(role!= Qt::DisplayRole) return QVariant(); } return QVariant(section+1);

Qt::ItemFlags TableModel::flags(const QModelIndex &index) const { return (Qt::ItemIsSelectable Qt::ItemIsEditable Qt::ItemIsEnabled); } bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isvalid()) return false; if(role!= Qt::EditRole) return false; if(!value.canconvert<int>()) return false; } m_data[index.row()][index.column()] = value.toint(); emit datachanged(index, index); return true;

QTableView *tableview = new QTableView(this); tableview->setmodel(new TableModel(this));

Modele drzewiaste wymagają implementacji wszystkich składowych zawartych w interfejsie QAbstractItemModel tj: Wszystkie metody dla modeli listowych i tabelarycznych oraz: parent zwraca indeks dla rodzica danego elementu index zwraca indeks (w kontekście modelu) dla zadanej pary elementu i jego rodzica.

QModelIndex() Tabele agregujące inne tabele Tylko pierwsza kolumna może reprezentować rodzica pod-tabeli Główny element nie ma rodzica, tj. zwraca najczęściej pusty (invalid) QModelIndex

parent QModelIndex index(int row, int column, const QModelIndex &parent) const Dla zadanego rodzica (może być invalid) zwraca QModelIndex dla zadanego wiersza i kolumny Model wykorzystuje metodę createindex by stworzyć poprawną instancję QModelIndex, lub zwrócić QModelIndex() dla elementów najwyższego poziomu QModelIndex createindex(int row, int column, void *ptr) const QModelIndex createindex(int row, int column, quint32 id) const

Metoda createindex zapewnia miejsce do przechowywania dowolnej liczby całkowitej bądź wskaźnika Służy to połączeniu modelu oraz warstwy danych QModelIndex createindex(int row, int column, void *ptr) const QModelIndex createindex(int row, int column, quint32 id) const

Metoda parent zwraca QModelIndex dla rodzica danego elementu... Odwołuje się do wiersza rodzica Czasem wymagane są odwołania typu: znajdź rodzica-rodzica-elementu

Metoda parent zwraca QModelIndex dla rodzica danego elementu Odwołuje się do wiersza rodzica Czasem wymagane są odwołania typu: znajdź rodzica-rodzica-elementu... Chcemy znaleźć rodzica tego elementu

Metoda parent zwraca QModelIndex dla rodzica danego elementu Odwołuje się do wiersza rodzica Czasem wymagane są odwołania typu: znajdź rodzica-rodzica-elementu... Chcemy znaleźć rodzica tego elementu Czyli ten wpis

Metoda parent zwraca QModelIndex dla rodzica danego elementu Odwołuje się do wiersza rodzica Czasem wymagane są odwołania typu: znajdź rodzica-rodzica-elementu... Chcemy znaleźć rodzica tego elementu Ten element musi wiedzieć, o który wiersz chodzi Czyli ten wpis

Metoda parent zwraca QModelIndex dla rodzica danego elementu Odwołuje się do wiersza rodzica Czasem wymagane są odwołania typu: znajdź rodzica-rodzica-elementu Każdy z tych... elementów może być najwyższego rzędu Chcemy znaleźć rodzica tego elementu Ten element musi wiedzieć, o który wiersz chodzi Czyli ten wpis

Implementacja metody parent jest prosta, pod warunkiem dostarczenia podstawowych informacji Każdy element musi znać swojego rodzica Każdy element musi wiedzieć w jakim wierszu się znajduje Alternatywna implementacja wymagałaby przejrzenia całego modelu w poszukiwaniu tych informacji

Poniżej przedstawiono prostą implementacje modelu drzewiastego do przechowywania instancji typu QObject class TreeModel : public QAbstractItemModel { Q_OBJECT public: explicit TreeModel(QObject *rootobject, QObject *parent = 0); QVariant data(const QModelIndex &index, int role=qt::displayrole) const; QVariant headerdata(int section, Qt::Orientation orientation, int role=qt::displayrole) const; QModelIndex parent(const QModelIndex &index) const; QModelIndex index(int row, int column, const QModelIndex &parent=qmodelindex()) const; int rowcount(const QModelIndex &parent=qmodelindex()) const; int columncount(const QModelIndex &parent=qmodelindex()) const; private: QObject *m_rootobject; };

QVariant TreeModel::data(const QModelIndex &index, int role) const { if(!index.isvalid() role!= Qt::DisplayRole) return QVariant(); } QObject *object = static_cast<qobject*>(index.internalpointer()); if(index.column() == 0) return object->objectname(); else return object->metaobject()->classname(); QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if(role!= Qt::DisplayRole orientation!= Qt::Horizontal) return QVariant(); } switch(section) { case 0: return QVariant("Object"); case 1: return QVariant("Class"); default: return QVariant(); }

QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const { if(column>1) return QModelIndex(); if(!parent.isvalid()) { if(row == 0) return createindex(0, column, (void*)m_rootobject); else return QModelIndex(); } QObject *parentobject = static_cast<qobject*>(parent.internalpointer()); if(row < parentobject->children().count()) return createindex(row, column, (void*)parentobject->children().at(row)); else return QModelIndex(); }

QModelIndex TreeModel::parent(const QModelIndex &index) const { if(!index.isvalid()) return QModelIndex(); QObject *object = static_cast<qobject*>(index.internalpointer()); if(object == m_rootobject) return QModelIndex(); QObject *parentobject = object->parent(); if(parentobject == m_rootobject) return createindex(0, 0, (void*)m_rootobject); } QObject *parentparentobject = parentobject->parent(); if(!parentparentobject) return QModelIndex(); else return createindex( parentparentobject->children().indexof(parentobject), 0, (void*)parentobject);

QGroupBox *rootobject = setupgroup(); QTreeView *treeview = new QTreeView(this); treeview->setmodel(new TreeModel(rootObject, this));

Niektóre elementy struktury modelu nie są weryfikowane na etapie kompilacji tj. Mogą się zepsuć podczas działania programu Wymagają testowania Pomocna w tym zakresie może być klasa ModelTest: https://wiki.qt.io/model_test

Model może się zmienić ze względu na wiele powodów: Delegat wywoła metodę setdata Warstwa danych ulegnie zmianie Zmiany mogą mieć wpływ na indeksy, dane lub cały model

Podczas każdej modyfikacji danych, sygnał datachanged powinien zostać wysłany bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isvalid() role!= Qt::EditRole!value.canConvert<int>() ) return false; } m_data[index.row()][index.column()] = value.toint(); emit datachanged(index, index); return true;

Dodawanie i usuwanie wierszy czy kolumn musi być zasygnalizowane przez model The signals emitted for columns is analogous to the signals for rows Podczas dodawania wierszy Wywołaj begininsertrows Usuń wiersze Wywołaj endinsertrows Podczas usuwania wierszy Wywołaj beginremoverows Usuń wiersze Wywołaj endremoverows

Dodawanie (na określonej pozycji) { } begininsertrows(parent, startrow, newrows.count()+startrow-1); m_data.insert(startrow, newrows); endinsertrows(); Dodawanie (na koniec) { } begininsertrows(parent, m_data.count(), newrows.count()+m_data.count()-1); m_data.append(newrows); endinsertrows();

Jeżeli model graficznie przedstawia zawarte dane, zmiana danych powinna pociągnąć za sobą aktualizację modelu Informacja o aktualizacji musi nastąpić przed fizyczną zmianą danych Cały model powinien zostać zresetowany Zresetowanie całego modelu wykonywane jest przez metodę reset

Jeżeli pobieranie danych jest operacją wolną, można dokonać reimplementacji kilku metod by przyspieszyć działanie modelu Jeżeli metoda rowcount jest wolna, zmiana haschildren pozwoli na zmianę odwołań do rowcount Metody canfetchmore i fetchmore pozwalają na pobieranie informacji na żądanie

Implementacja wspomnianych metod nie musi być kłopotliwa class LazyModel : public QAbstractItemModel { Q_OBJECT public:... bool canfetchmore(const QModelIndex &parent) const; void fetchmore(const QModelIndex &parent); bool haschildren(const QModelIndex &parent=qmodelindex()) const;...

bool LazyModel::canFetchMore(const QModelIndex &parent) const { if(parent.isvalid() && parent.internalid() == -1 && m_returnedrows < 10) return true; else return false; } void LazyModel::fetchMore(const QModelIndex &parent) { if(parent.isvalid() && parent.internalid() == -1 && m_returnedrows < 10) { begininsertrows(parent, rowcount(), rowcount()); m_returnedrows ++; endinsertrows(); } } bool LazyModel::hasChildren(const QModelIndex &parent) const { if(!parent.isvalid() parent.internalid() == -1) return true; else return false; }

QTreeView *treeview = new QTreeView(this); treeview->setmodel(new LazyModel(this));

Czy są jakieś pytania?