Model View Programming/ro: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
m (AutoSpider moved page Model View Programming Romanian to Model View Programming/ro: Localisation)
 
(5 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{Cleanup | reason=Auto-imported from ExpressionEngine.}}
[[Category:Learning]]
[[Category:Learning]]


Line 5: Line 7:
== Generalități ==
== Generalități ==


În Qt există un set de clase de vizualizare a datelor cu ajutorul "arhitecturii Model-View":http://en.wikipedia.org/wiki/Model–view–controller pentru a gestiona relațiile dintre date și modul în care sunt acestea prezentate utilizatorului. Separația funcțională introdusă de această arhitectură dă programatorului o mai mare flexibilitate în a personaliza prezentarea datelor și oferă un model de interfață care permite o gamă mai largă de surse de date ce pot fi folosite.
În Qt există un set de clase de vizualizare a datelor cu ajutorul [http://en.wikipedia.org/wiki/Model–view–controller arhitecturii Model-View] pentru a gestiona relațiile dintre date și modul în care sunt acestea prezentate utilizatorului. Separația funcțională introdusă de această arhitectură dă programatorului o mai mare flexibilitate în a personaliza prezentarea datelor și oferă un model de interfață care permite o gamă mai largă de surse de date ce pot fi folosite.


'''Arhitectura Model-View-Controller''' este un &quot;design pattern&amp;quot;:http://en.wikipedia.org/wiki/Design_pattern originar din SmallTalk care este adesea folosit atunci când se construiesc interfețe cu utilizatorul. Model-View-Controller conține trei tipuri de obiecte:<br />* '''Modelul''' este un obiect de aplicație;<br />* '''View'''-ul este prezentarea pe ecran;<br />* '''Controller'''-ul definește modul în care interfața cu utilizatorul reacționează la comenzile date de utilizator.
'''Arhitectura Model-View-Controller''' este un [http://en.wikipedia.org/wiki/Design_pattern design pattern] originar din SmallTalk care este adesea folosit atunci când se construiesc interfețe cu utilizatorul. Model-View-Controller conține trei tipuri de obiecte:
* '''Modelul''' este un obiect de aplicație;
* '''View'''-ul este prezentarea pe ecran;
* '''Controller'''-ul definește modul în care interfața cu utilizatorul reacționează la comenzile date de utilizator.


Înainte de această arhitectură, cele trei obiecte erau de regulă puse laolalta. '''Arhitectura Model-View-Controller''' le decuplează pentru a crește flexibilatea codului și să-l facă reutilizabil.
Înainte de această arhitectură, cele trei obiecte erau de regulă puse laolalta. '''Arhitectura Model-View-Controller''' le decuplează pentru a crește flexibilatea codului și să-l facă reutilizabil.


Dacă '''view'''-ul și '''controller'''<s>ul sunt combinate, rezultatul va fi o '''arhitectură model-view'''. Chiar și în acest caz, datele sunt separate de modul în care ele sunt prezentate, dar oferă niște mecanisme mai ușoare de lucru bazate pe aceleași principii. Această separație face posibilă afișarea a acelorași date în mai multe feluri fără a fi nevoie să schimbăm modul în care sunt structurate datele. Pentru a permite un mod flexibil de gestionare a comenzile utilizatorului, se introduce conceptul de '''delegat'''. Delegații ne permit să modificăm modul în care datele sunt afișate și editate.  
Dacă '''view'''-ul și '''controller'''-ul sunt combinate, rezultatul va fi o '''arhitectură model-view'''. Chiar și în acest caz, datele sunt separate de modul în care ele sunt prezentate, dar oferă niște mecanisme mai ușoare de lucru bazate pe aceleași principii. Această separație face posibilă afișarea a acelorași date în mai multe feluri fără a fi nevoie să schimbăm modul în care sunt structurate datele. Pentru a permite un mod flexibil de gestionare a comenzile utilizatorului, se introduce conceptul de '''delegat'''. Delegații ne permit să modificăm modul în care datele sunt afișate și editate.  
 
 
Clasele unei astfel de arhitecturi sunt clasele de '''model''', de '''view''' și '''delegații'''. Modelul comunică cu o sursă de date și oferă o interfață de comunicare cu celelalte componente. View-ul obține indexul de la care își va extrage datele din model pentru a le prezenta. Atunci când datele sunt editate delegatul va comunica direct cu modelul folosind indexul. Toate aceste clase vor comunica între ele utilizând semnale și slot-uri. Semnalele de la model vor informa despre schimbările de date. Semnalele de la view vor comunica comenzile date de utilizator. Semnalele de la delegat vor fi folosite în timpul editării datelor să informeze modelul și view-ul în legătură cu starea editorului.
 
[[Image:http://doc.qt.nokia.com/4.7-snapshot/images/modelview-overview.png|http://doc.qt.nokia.com/4.7-snapshot/images/modelview-overview.png]]
 


<br />Clasele unei astfel de arhitecturi sunt clasele de '''model''', de '''view''' și '''delegații'''. Modelul comunică cu o sursă de date și oferă o interfață de comunicare cu celelalte componente. View-ul obține indexul de la care își va extrage datele din model pentru a le prezenta. Atunci când datele sunt editate delegatul va comunica direct cu modelul folosind indexul. Toate aceste clase vor comunica între ele utilizând semnale și slot-uri. Semnalele de la model vor informa despre schimbările de date. Semnalele de la view vor comunica comenzile date de utilizator. Semnalele de la delegat vor fi folosite în timpul editării datelor să informeze modelul și view-ul în legătură cu starea editorului.
Toate clasele care implementează modele sunt bazate pe clasa [http://doc.qt.nokia.com/4.7-snapshot/qabstractitemmodel.html QAbstractItemModel]. Această clasă definește o interfață care să poată fi folosită de view-uri și de delegați pentru a accesa date. Datele propriu-zise nu trebuie neapărat reținute într-un model. Ele pot fi păstrate într-o structură de date oferită de o clasă separată, un fișier, o bază de date sau altă componentă de aplicație.
<br />[[Image:http://doc.qt.nokia.com/4.7-snapshot/images/modelview-overview.png|http://doc.qt.nokia.com/4.7-snapshot/images/modelview-overview.png]]


<br /> Toate clasele care implementează modele sunt bazate pe clasa &quot;QAbstractItemModel&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qabstractitemmodel.html. Această clasă definește o interfață care să poată fi folosită de view-uri și de delegați pentru a accesa date. Datele propriu-zise nu trebuie neapărat reținute într-un model. Ele pot fi păstrate într-o structură de date oferită de o clasă separată, un fișier, o bază de date sau altă componentă de aplicație.


<br />Qt oferă niște modele gata definite care pot fi folosite pentru a gestiona un set de date:<br />* &quot;QStringListModel&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qstringlistmodel.html</s> pentru a reține o listă de QString;<br />* &quot;QStandardItemModel&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qstandarditemmodel.html - poate lucra cu structuri de date ierarhice care conțin orice tip de date;<br />* &quot;QFileSystemModel&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qfilesystemmodel.html - reține informații despre fișiere și directoare;<br />* &quot;QSqlQueryModel&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qsqlquerymodel.html,&quot;QSqlTableModel&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qsqltablemodel.html,&quot;QSqlRelationalTableModel&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qsqlrelationaltablemodel.html- sunt folosite pentru a accesa baze de date conform convențiilor arhitecturii Model-View.
Qt oferă niște modele gata definite care pot fi folosite pentru a gestiona un set de date:
* [http://doc.qt.nokia.com/4.7-snapshot/qstringlistmodel.html QStringListModel]- pentru a reține o listă de QString;
* [http://doc.qt.nokia.com/4.7-snapshot/qstandarditemmodel.html QStandardItemModel] - poate lucra cu structuri de date ierarhice care conțin orice tip de date;
* [http://doc.qt.nokia.com/4.7-snapshot/qfilesystemmodel.html QFileSystemModel] - reține informații despre fișiere și directoare;
* [http://doc.qt.nokia.com/4.7-snapshot/qsqlquerymodel.html QSqlQueryModel], [http://doc.qt.nokia.com/4.7-snapshot/qsqltablemodel.html QSqlTableModel], [http://doc.qt.nokia.com/4.7-snapshot/qsqlrelationaltablemodel.html QSqlRelationalTableModel] - sunt folosite pentru a accesa baze de date conform convențiilor arhitecturii Model-View.


Implementări complete sunt disponibile pentru următoarele view-uri:<br />* &quot;QListView&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qlistview.html - pentru o listă de elemente;<br />* &quot;QTableView&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qtableview.html - poate a afișa datele dintr-un tabel;<br />* &quot;QTreeView&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qtreeview.html - pentru a afișa date dintr-un model ierarhic;
Implementări complete sunt disponibile pentru următoarele view-uri:
* [http://doc.qt.nokia.com/4.7-snapshot/qlistview.html QListView] - pentru o listă de elemente;
* [http://doc.qt.nokia.com/4.7-snapshot/qtableview.html QTableView] - poate a afișa datele dintr-un tabel;
* [http://doc.qt.nokia.com/4.7-snapshot/qtreeview.html QTreeView] - pentru a afișa date dintr-un model ierarhic;


Toate clasele menționate mai sus moștenesc clasa &quot;QAbstractItemView&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qabstractitemview.html și pot fi și ele la rândul lor moștenite pentru a le oferi mai multe funcționalități. Delegații moștenesc clasa &quot;QAbstractItemDelegate&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qabstractitemdelegate.html. Delegatul implicit folosit de view-uri este &quot;QStyledItemDelegate&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qstyleditemdelegate.html.
Toate clasele menționate mai sus moștenesc clasa [http://doc.qt.nokia.com/4.7-snapshot/qabstractitemview.html QAbstractItemView] și pot fi și ele la rândul lor moștenite pentru a le oferi mai multe funcționalități. Delegații moștenesc clasa [http://doc.qt.nokia.com/4.7-snapshot/qabstractitemdelegate.html QAbstractItemDelegate]. Delegatul implicit folosit de view-uri este [http://doc.qt.nokia.com/4.7-snapshot/qstyleditemdelegate.html QStyledItemDelegate].


Există două metode de a sorta elementele într-o arhitectură model-view. Dacă modelul este sortabil se poate reimplementa funcția &quot;void sort()&quot;:http://doc.qt.nokia.com/4.7-snapshot/qabstractitemmodel.html#sort. '''QTableView''' și '''QTreeView''' dau posibilitatea modelul să fie sortat printr-un click pe antetul tabelului.
Există două metode de a sorta elementele într-o arhitectură model-view. Dacă modelul este sortabil se poate reimplementa funcția [http://doc.qt.nokia.com/4.7-snapshot/qabstractitemmodel.html#sort void sort()]. '''QTableView''' și '''QTreeView''' dau posibilitatea modelul să fie sortat printr-un click pe antetul tabelului.


== Prezentarea datelor într-un tabel ==
== Prezentarea datelor într-un tabel ==
Line 30: Line 46:
Clasele '''QSqlQueryModel''', '''QSqlTableModel''', '''SqlRelationalTableModel''' pot fi folosite ca surse de date pentru clasele de tip view ca '''QListView''', '''QTableView''', și '''QTreeView'''. În practică '''QTableView''' este cel mai des folosit, deoarece în esență un rezultat de la o interogare SQL este o structură de date bidimensională.
Clasele '''QSqlQueryModel''', '''QSqlTableModel''', '''SqlRelationalTableModel''' pot fi folosite ca surse de date pentru clasele de tip view ca '''QListView''', '''QTableView''', și '''QTreeView'''. În practică '''QTableView''' este cel mai des folosit, deoarece în esență un rezultat de la o interogare SQL este o structură de date bidimensională.


Înainte de a prezenta diferite date într-un tabel, în arhitectura Model/View este mai întâi nevoie să ne generăm un model pe care să-l asociem mai târziu tabelului. Acest lucru se poate face prin intermediul unor clase precum '''QSqlQueryModel''', '''QSqlTableModel''', '''SqlRelationalTableModel''' cum de altfel &quot;s-a putut vedea aici &quot;:http://developer.qt.nokia.com/wiki/Working_with_Databases_Romanian.
Înainte de a prezenta diferite date într-un tabel, în arhitectura Model/View este mai întâi nevoie să ne generăm un model pe care să-l asociem mai târziu tabelului. Acest lucru se poate face prin intermediul unor clase precum '''QSqlQueryModel''', '''QSqlTableModel''', '''SqlRelationalTableModel''' cum de altfel [http://developer.qt.nokia.com/wiki/Working_with_Databases_Romanian s-a putut vedea aici ].


Putem folosi același model pentru mai multe view-uri. Dacă utilizatorul editează modelul într-una din view-uri aceste schimbări se vor reflecta imediat. De asemenea putem personaliza și antetul unui view prin apelul funcției &quot;setHeaderData()&quot;:http://doc.qt.nokia.com/4.7-snapshot/qabstractitemmodel.html#setHeaderData a unui model.
Putem folosi același model pentru mai multe view-uri. Dacă utilizatorul editează modelul într-una din view-uri aceste schimbări se vor reflecta imediat. De asemenea putem personaliza și antetul unui view prin apelul funcției [http://doc.qt.nokia.com/4.7-snapshot/qabstractitemmodel.html#setHeaderData setHeaderData()] a unui model.


Uneori dorim să presortăm datele dintr-un model. Clasa &quot;QSortFilterProxyModel &quot;:http://doc.qt.nokia.com/4.7-snapshot/qsortfilterproxymodel.html poate fi interpusă între un model și un view pentru sortări și filtrări a datelor. Această clasă transformă structura modelului sursă prin maparea indexurilor modelului în noi indexi. Această abordare permite ca un model sursă să fie restructurat fără a efectura transformări asupra modelului sursă și fără a duplica informații în memorie.
Uneori dorim să presortăm datele dintr-un model. Clasa [http://doc.qt.nokia.com/4.7-snapshot/qsortfilterproxymodel.html QSortFilterProxyModel ] poate fi interpusă între un model și un view pentru sortări și filtrări a datelor. Această clasă transformă structura modelului sursă prin maparea indexurilor modelului în noi indexi. Această abordare permite ca un model sursă să fie restructurat fără a efectura transformări asupra modelului sursă și fără a duplica informații în memorie.


Să presupunem că dorim să sortăm și să filtrăm informațiile dintr-un model. Următorul cod va crea un model care va fi dat ca model sursă pentru un model '''QSortFilterProxyModel''' :
Să presupunem că dorim să sortăm și să filtrăm informațiile dintr-un model. Următorul cod va crea un model care va fi dat ca model sursă pentru un model '''QSortFilterProxyModel''' :


<code> model = new QSqlQueryModel(this);<br /> proxyModel = new MySortFilterProxyModel(this);<br /> proxyModel-&gt;setSourceModel(model);</code>
<code> model = new QSqlQueryModel(this);
proxyModel = new MySortFilterProxyModel(this);
proxyModel->setSourceModel(model);</code>


Iar pentru a permite sortarea trebuie să setăm proprietatea '''dynamicSortFilter''':
Iar pentru a permite sortarea trebuie să setăm proprietatea '''dynamicSortFilter''':


<code> proxyModel-&gt;setDynamicSortFilter(true);<code>
<code> proxyModel->setDynamicSortFilter(true);<code>


Acest lucru ne va permite să sortăm informațiile dintr-un tabel printr-un click pe antetul view-ului.
Acest lucru ne va permite să sortăm informațiile dintr-un tabel printr-un click pe antetul view-ului.


Pentru a filtra datele ne folosim de funcția &quot;setFilterRegExp() &quot;:http://doc.qt.nokia.com/4.7-snapshot/qsortfilterproxymodel.html#filterRegExp-prop pentru a specifica filtrul folosit și de &quot;setFilterKeyColumn&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qsortfilterproxymodel.html#filterKeyColumn-prop pentru a specifica coloană pe care aplicăm respectivul filtru.
Pentru a filtra datele ne folosim de funcția [http://doc.qt.nokia.com/4.7-snapshot/qsortfilterproxymodel.html#filterRegExp-prop setFilterRegExp() ] pentru a specifica filtrul folosit și de [http://doc.qt.nokia.com/4.7-snapshot/qsortfilterproxymodel.html#filterKeyColumn-prop setFilterKeyColumn] pentru a specifica coloană pe care aplicăm respectivul filtru.


== Folosirea delegaților în arhitectura Model-View-Controller ==
== Folosirea delegaților în arhitectura Model-View-Controller ==


Un view standard oferit de Qt folosește pentru editare instanțe ale clasei &quot;QItemDelegate&amp;quot;:http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html. Toate rolurile standard sunt interpretate de delegatul implicit al unui view. În următorul exemplu se va folosi un &quot;QDoubleSpinBox &quot;:http://doc.qt.nokia.com/4.7-snapshot/qdoublespinbox.html pentru a oferi facilități de editare pentru un delegat și vom redefini modul în care afișăm datele dintr-o celulă. Vom moșteni clasa '''QItemDelegate''' și vom reimplementa o serie de funcții :
Un view standard oferit de Qt folosește pentru editare instanțe ale clasei [http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html QItemDelegate]. Toate rolurile standard sunt interpretate de delegatul implicit al unui view. În următorul exemplu se va folosi un [http://doc.qt.nokia.com/4.7-snapshot/qdoublespinbox.html QDoubleSpinBox ] pentru a oferi facilități de editare pentru un delegat și vom redefini modul în care afișăm datele dintr-o celulă. Vom moșteni clasa '''QItemDelegate''' și vom reimplementa o serie de funcții :


</code>class CustomSpinBoxDelegate: public QItemDelegate {<br />Q_OBJECT<br />public:<br /> CustomSpinBoxDelegate(QObject *parent = 0);<br /> QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &amp;option;,const QModelIndex &amp;index;) const;<br /> void setEditorData(QWidget *editor, const QModelIndex &amp;index;) const;<br /> void setModelData(QWidget *editor, QAbstractItemModel *model,<br /> const QModelIndex &amp;index;) const;<br /> void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &amp;option;, const QModelIndex &amp;index;) const;<br /> void paint(QPainter *painter, const QStyleOptionViewItem &amp;option;,<br /> const QModelIndex &amp;index;) const;};<code>
</code>class CustomSpinBoxDelegate: public QItemDelegate {
Q_OBJECT
public:
CustomSpinBoxDelegate(QObject *parent = 0);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option;,const QModelIndex &index;) const;
void setEditorData(QWidget *editor, const QModelIndex &index;) const;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index;) const;
void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option;, const QModelIndex &index;) const;
void paint(QPainter *painter, const QStyleOptionViewItem &option;,
const QModelIndex &index;) const;};<code>


Când un tabel are nevoie de un editor, el îl preia de la delegat. Acest editor se crează prin apelul funcției &quot;createEditor()&quot;:http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#createEditor al obiectului de tip delegat:
Când un tabel are nevoie de un editor, el îl preia de la delegat. Acest editor se crează prin apelul funcției [http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#createEditor createEditor()] al obiectului de tip delegat:


</code>QWidget *CustomSpinBoxDelegate::createEditor(QWidget '''parent,<br /> const QStyleOptionViewItem &amp;/''' option '''/, const QModelIndex &amp;/''' index */) const {<br /> QDoubleSpinBox *editor = new QDoubleSpinBox(parent);<br /> editor-&gt;setAccelerated(true);<br /> editor-&gt;setRange(0, 10000);<br /> return editor;}<code>
</code>QWidget *CustomSpinBoxDelegate::createEditor(QWidget '''parent,
const QStyleOptionViewItem &/''' option */, const QModelIndex &/''' index */) const {
QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
editor->setAccelerated(true);
editor->setRange(0, 10000);
return editor;}<code>


Nu e nevoie să păstrăm un pointer către editor, deoarece tabelul își asumă responsabilitatea dealocării acestuia când nu va mai fi nevoie de el. Când utilizatorul a terminat de editat, tabelul va apela funcția &quot;setModelData()&quot;:http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#setModelData pentru a salva datele:
Nu e nevoie să păstrăm un pointer către editor, deoarece tabelul își asumă responsabilitatea dealocării acestuia când nu va mai fi nevoie de el. Când utilizatorul a terminat de editat, tabelul va apela funcția [http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#setModelData setModelData()] pentru a salva datele:


</code>void CustomSpinBoxDelegate::setModelData(QWidget *editor,<br /> QAbstractItemModel *model, const QModelIndex &amp;index;) const {<br /> QDoubleSpinBox '''spinBox = static_cast&amp;lt;QDoubleSpinBox'''&gt; (editor);<br /> spinBox-&gt;interpretText();<br /> qreal value = spinBox-&gt;value();<br /> model-&gt;setData(index, value, Qt::EditRole);<br />}<code>
</code>void CustomSpinBoxDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model, const QModelIndex &index;) const {
QDoubleSpinBox '''spinBox = static_cast<QDoubleSpinBox'''> (editor);
spinBox->interpretText();
qreal value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}<code>


De asemenea delegatul trebuie să conțină și funcții prin care copiem datele din model în editor. În acest caz preluăm datele păstrate în model și-l atribuim editorului:
De asemenea delegatul trebuie să conțină și funcții prin care copiem datele din model în editor. În acest caz preluăm datele păstrate în model și-l atribuim editorului:


</code>void CustomSpinBoxDelegate::setEditorData(QWidget *editor,<br /> const QModelIndex &amp;index;) const {<br /> qreal value = index.model()<s>&gt;data(index, Qt::EditRole).toDouble();<br /> QDoubleSpinBox '''spinBox = static_cast&amp;lt;QDoubleSpinBox'''&gt; (editor);<br /> spinBox</s>&gt;setValue(value);<br />}<code>
</code>void CustomSpinBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index;) const {
qreal value = index.model()->data(index, Qt::EditRole).toDouble();
QDoubleSpinBox '''spinBox = static_cast<QDoubleSpinBox'''> (editor);
spinBox->setValue(value);
}<code>


Iar dacă dorim să modificăm modul în care datele sunt afișate trebuie să reimplementăm funcția &quot;paint()&quot;:http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#paint :
Iar dacă dorim să modificăm modul în care datele sunt afișate trebuie să reimplementăm funcția [http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#paint paint()] :


</code>void CustomSpinBoxDelegate::paint(QPainter *painter,<br /> const QStyleOptionViewItem &amp;option;, const QModelIndex &amp;index;) const {<br /> if (option.state &amp; QStyle::State_Selected) {<br /> painter-&gt;setCompositionMode (QPainter:: CompositionMode_SourceAtop);<br /> painter-&gt;fillRect(option.rect, option.palette.highlight());<br /> painter-&gt;drawText(option.rect, Qt::AlignCenter, QString::number(index.data().toDouble(), 'f', 2));<br /> painter-&gt;setCompositionMode(QPainter::CompositionMode_SourceOver);<br /> } else {<br /> painter-&gt;drawText(option.rect, Qt::AlignCenter, QString::number(index.data().toDouble(), 'f', 2));<br /> }<br />}<code>
</code>void CustomSpinBoxDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option;, const QModelIndex &index;) const {
if (option.state & QStyle::State_Selected) {
painter->setCompositionMode (QPainter:: CompositionMode_SourceAtop);
painter->fillRect(option.rect, option.palette.highlight());
painter->drawText(option.rect, Qt::AlignCenter, QString::number(index.data().toDouble(), 'f', 2));
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
} else {
painter->drawText(option.rect, Qt::AlignCenter, QString::number(index.data().toDouble(), 'f', 2));
}
}<code>

Latest revision as of 16:01, 16 March 2015

This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine.
Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean.

Arhitectura Model-View-Controller (MVC)

Generalități

În Qt există un set de clase de vizualizare a datelor cu ajutorul arhitecturii Model-View pentru a gestiona relațiile dintre date și modul în care sunt acestea prezentate utilizatorului. Separația funcțională introdusă de această arhitectură dă programatorului o mai mare flexibilitate în a personaliza prezentarea datelor și oferă un model de interfață care permite o gamă mai largă de surse de date ce pot fi folosite.

Arhitectura Model-View-Controller este un design pattern originar din SmallTalk care este adesea folosit atunci când se construiesc interfețe cu utilizatorul. Model-View-Controller conține trei tipuri de obiecte:

  • Modelul este un obiect de aplicație;
  • View-ul este prezentarea pe ecran;
  • Controller-ul definește modul în care interfața cu utilizatorul reacționează la comenzile date de utilizator.

Înainte de această arhitectură, cele trei obiecte erau de regulă puse laolalta. Arhitectura Model-View-Controller le decuplează pentru a crește flexibilatea codului și să-l facă reutilizabil.

Dacă view-ul și controller-ul sunt combinate, rezultatul va fi o arhitectură model-view. Chiar și în acest caz, datele sunt separate de modul în care ele sunt prezentate, dar oferă niște mecanisme mai ușoare de lucru bazate pe aceleași principii. Această separație face posibilă afișarea a acelorași date în mai multe feluri fără a fi nevoie să schimbăm modul în care sunt structurate datele. Pentru a permite un mod flexibil de gestionare a comenzile utilizatorului, se introduce conceptul de delegat. Delegații ne permit să modificăm modul în care datele sunt afișate și editate.


Clasele unei astfel de arhitecturi sunt clasele de model, de view și delegații. Modelul comunică cu o sursă de date și oferă o interfață de comunicare cu celelalte componente. View-ul obține indexul de la care își va extrage datele din model pentru a le prezenta. Atunci când datele sunt editate delegatul va comunica direct cu modelul folosind indexul. Toate aceste clase vor comunica între ele utilizând semnale și slot-uri. Semnalele de la model vor informa despre schimbările de date. Semnalele de la view vor comunica comenzile date de utilizator. Semnalele de la delegat vor fi folosite în timpul editării datelor să informeze modelul și view-ul în legătură cu starea editorului.

http://doc.qt.nokia.com/4.7-snapshot/images/modelview-overview.png


Toate clasele care implementează modele sunt bazate pe clasa QAbstractItemModel. Această clasă definește o interfață care să poată fi folosită de view-uri și de delegați pentru a accesa date. Datele propriu-zise nu trebuie neapărat reținute într-un model. Ele pot fi păstrate într-o structură de date oferită de o clasă separată, un fișier, o bază de date sau altă componentă de aplicație.


Qt oferă niște modele gata definite care pot fi folosite pentru a gestiona un set de date:

Implementări complete sunt disponibile pentru următoarele view-uri:

  • QListView - pentru o listă de elemente;
  • QTableView - poate a afișa datele dintr-un tabel;
  • QTreeView - pentru a afișa date dintr-un model ierarhic;

Toate clasele menționate mai sus moștenesc clasa QAbstractItemView și pot fi și ele la rândul lor moștenite pentru a le oferi mai multe funcționalități. Delegații moștenesc clasa QAbstractItemDelegate. Delegatul implicit folosit de view-uri este QStyledItemDelegate.

Există două metode de a sorta elementele într-o arhitectură model-view. Dacă modelul este sortabil se poate reimplementa funcția void sort(). QTableView și QTreeView dau posibilitatea modelul să fie sortat printr-un click pe antetul tabelului.

Prezentarea datelor într-un tabel

Clasele QSqlQueryModel, QSqlTableModel, SqlRelationalTableModel pot fi folosite ca surse de date pentru clasele de tip view ca QListView, QTableView, și QTreeView. În practică QTableView este cel mai des folosit, deoarece în esență un rezultat de la o interogare SQL este o structură de date bidimensională.

Înainte de a prezenta diferite date într-un tabel, în arhitectura Model/View este mai întâi nevoie să ne generăm un model pe care să-l asociem mai târziu tabelului. Acest lucru se poate face prin intermediul unor clase precum QSqlQueryModel, QSqlTableModel, SqlRelationalTableModel cum de altfel s-a putut vedea aici .

Putem folosi același model pentru mai multe view-uri. Dacă utilizatorul editează modelul într-una din view-uri aceste schimbări se vor reflecta imediat. De asemenea putem personaliza și antetul unui view prin apelul funcției setHeaderData() a unui model.

Uneori dorim să presortăm datele dintr-un model. Clasa QSortFilterProxyModel poate fi interpusă între un model și un view pentru sortări și filtrări a datelor. Această clasă transformă structura modelului sursă prin maparea indexurilor modelului în noi indexi. Această abordare permite ca un model sursă să fie restructurat fără a efectura transformări asupra modelului sursă și fără a duplica informații în memorie.

Să presupunem că dorim să sortăm și să filtrăm informațiile dintr-un model. Următorul cod va crea un model care va fi dat ca model sursă pentru un model QSortFilterProxyModel :

 model = new QSqlQueryModel(this);
 proxyModel = new MySortFilterProxyModel(this);
 proxyModel->setSourceModel(model);

Iar pentru a permite sortarea trebuie să setăm proprietatea dynamicSortFilter:

 proxyModel->setDynamicSortFilter(true);<code>

Acest lucru ne va permite  sortăm informațiile dintr-un tabel printr-un click pe antetul view-ului.

Pentru a filtra datele ne folosim de funcția [http://doc.qt.nokia.com/4.7-snapshot/qsortfilterproxymodel.html#filterRegExp-prop setFilterRegExp() ] pentru a specifica filtrul folosit și de [http://doc.qt.nokia.com/4.7-snapshot/qsortfilterproxymodel.html#filterKeyColumn-prop setFilterKeyColumn] pentru a specifica coloană pe care aplicăm respectivul filtru.

== Folosirea delegaților în arhitectura Model-View-Controller ==

Un view standard oferit de Qt folosește pentru editare instanțe ale clasei [http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html QItemDelegate]. Toate rolurile standard sunt interpretate de delegatul implicit al unui view. În următorul exemplu se va folosi un [http://doc.qt.nokia.com/4.7-snapshot/qdoublespinbox.html QDoubleSpinBox ] pentru a oferi facilități de editare pentru un delegat și vom redefini modul în care afișăm datele dintr-o celulă. Vom moșteni clasa '''QItemDelegate''' și vom reimplementa o serie de funcții :

class CustomSpinBoxDelegate: public QItemDelegate {

Q_OBJECT public:

CustomSpinBoxDelegate(QObject *parent = 0);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option;,const QModelIndex &index;) const;
void setEditorData(QWidget *editor, const QModelIndex &index;) const;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index;) const;
void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option;, const QModelIndex &index;) const;
void paint(QPainter *painter, const QStyleOptionViewItem &option;,

const QModelIndex &index;) const;};

Când un tabel are nevoie de un editor, el îl preia de la delegat. Acest editor se crează prin apelul funcției [http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#createEditor createEditor()] al obiectului de tip delegat:

QWidget *CustomSpinBoxDelegate::createEditor(QWidget parent,

const QStyleOptionViewItem &/ option */, const QModelIndex &/ index */) const {
QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
editor->setAccelerated(true);
editor->setRange(0, 10000);

return editor;}

Nu e nevoie  păstrăm un pointer către editor, deoarece tabelul își asumă responsabilitatea dealocării acestuia când nu va mai fi nevoie de el. Când utilizatorul a terminat de editat, tabelul va apela funcția [http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#setModelData setModelData()] pentru a salva datele:

void CustomSpinBoxDelegate::setModelData(QWidget *editor,

QAbstractItemModel *model, const QModelIndex &index;) const {
QDoubleSpinBox spinBox = static_cast<QDoubleSpinBox> (editor);
spinBox->interpretText();
qreal value = spinBox->value();
model->setData(index, value, Qt::EditRole);

}

De asemenea delegatul trebuie  conțină și funcții prin care copiem datele din model în editor. În acest caz preluăm datele păstrate în model și-l atribuim editorului:

void CustomSpinBoxDelegate::setEditorData(QWidget *editor,

const QModelIndex &index;) const {
qreal value = index.model()->data(index, Qt::EditRole).toDouble();
QDoubleSpinBox spinBox = static_cast<QDoubleSpinBox> (editor);
spinBox->setValue(value);

}

Iar dacă dorim  modificăm modul în care datele sunt afișate trebuie  reimplementăm funcția [http://doc.qt.nokia.com/4.7-snapshot/qitemdelegate.html#paint paint()] :

void CustomSpinBoxDelegate::paint(QPainter *painter,

const QStyleOptionViewItem &option;, const QModelIndex &index;) const {
if (option.state & QStyle::State_Selected) {
painter->setCompositionMode (QPainter:: CompositionMode_SourceAtop);
painter->fillRect(option.rect, option.palette.highlight());
painter->drawText(option.rect, Qt::AlignCenter, QString::number(index.data().toDouble(), 'f', 2));
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
} else {
painter->drawText(option.rect, Qt::AlignCenter, QString::number(index.data().toDouble(), 'f', 2));
}

}