How to Use a QSqlQueryModel in QML: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
Line 1: Line 1:
'''English''' [[How to use a QSqlQueryModel in QML Spanish|Spanish]] [[HowToUseAQSqlQueryModelInQML Italian|Italian]]
[[Category:Snippets]]<br />[[Category:HowTo]]<br />[[Category:Developing_with_Qt::Qt Quick]]<br />[[Category:Developing_with_Qt::Qt Quick::QML]]<br />[[Category:Developing_with_Qt::Qt-Quick::Tutorial]]


=How to use a QSqlQueryModel in <span class="caps">QML</span>=
'''English''' [[How_to_use_a_QSqlQueryModel_in_QML_Spanish|Spanish]] [[HowToUseAQSqlQueryModelInQML_Italian|Italian]]


==Initial fully detailed approach==
[toc depth=&quot;3&amp;quot; align_right=&quot;no&amp;quot;]


===Introduction===
= How to use a QSqlQueryModel in QML =


The software I develop, Photo Parata, is a client server application that uses a sql backend. Most of the time the data Photo Parata displays requires some joins. Because of this, most of the time the models are derived from QSqlQueryModel, not QSqlTableModel.<br /> For QSqlRelationalTableModel, you can find an working but unexplained example at [[QML and QSqlTableModel|http://wiki.qt.io/QML_and_QSqlTableModel]]
== Initial fully detailed approach ==


In this how to, I will walk you through the steps of setting up a custom model for <span class="caps">QML</span>, derived from QSqlQueryModel.
=== Introduction ===


I would like to thank [http://cdumez.blogspot.com/ Christophe Dumez] ''[cdumez.blogspot.com]'' for his blog [http://cdumez.blogspot.com/2010/11/how-to-use-c-list-model-in-qml.html How to use C++ list model in <span class="caps">QML</span>] ''[cdumez.blogspot.com]''. It was this blog that allowed me to piece the following together.
The software I develop, Photo Parata, is a client server application that uses a sql backend. Most of the time the data Photo Parata displays requires some joins. Because of this, most of the time the models are derived from QSqlQueryModel, not QSqlTableModel.<br />For QSqlRelationalTableModel, you can find an working but unexplained example at http://wiki.qt.io/QML_and_QSqlTableModel


Other useful sources were : [http://www.developer.nokia.com/Community/Wiki/Using_QStandardItemModel_in_QML Using_QStandardItemModel_in_QML] ''[developer.nokia.com]'' and [http://forum.qt.io/viewthread/12188 QSqlTableModel in <span class="caps">QML</span>] ''[qt.io]''
In this how to, I will walk you through the steps of setting up a custom model for QML, derived from QSqlQueryModel.


The data source for this example was lifted from one of the Sql examples that ships with Qt, [http://doc.qt.nokia.com/stable/sql-masterdetail-database-h.html examples\sql\masterdetail] ''[doc.qt.nokia.com]''
I would like to thank &quot;Christophe Dumez&amp;quot;:http://cdumez.blogspot.com/ for his blog &quot;How to use C++ list model in QML&amp;quot;:http://cdumez.blogspot.com/2010/11/how-to-use-c-list-model-in-qml.html. It was this blog that allowed me to piece the following together.


===Step 1: Create a C++ class that derives from QSqlQueryModel:===
Other useful sources were : &quot;Using_QStandardItemModel_in_QML (wiki page)&quot;:http://www.developer.nokia.com/Community/Wiki/Using_QStandardItemModel_in_QML and &quot;QSqlTableModel in QML (forum thread)&quot;:http://forum.qt.io/viewthread/12188


All the magic happens in the constructor and in the overloaded data() method.
The data source for this example was lifted from one of the Sql examples that ships with Qt, &quot;examples\sql\masterdetail&amp;quot;:http://doc.qt.nokia.com/stable/sql-masterdetail-database-h.html


===Step 2: Implement two static constants===
=== Step 1: Create a C++ class that derives from QSqlQueryModel: ===


I always have two constant static variables in each of my models that derive from QSqlQueryModel, <span class="caps">COLUMN</span>_NAMES and <span class="caps">SQL</span>_SELECT. The order of the column names in <span class="caps">COLUMN</span>_NAMES must match the order they are listed in the <span class="caps">SELECT</span> statement
All the magic happens in the constructor and in the overloaded data() method.


===Step 3: Set the roleNames in the constructor===
<code><br />class ArtistsSqlModel : public QSqlQueryModel<br />{<br /> Q_OBJECT<br />public:<br /> explicit ArtistsSqlModel(QObject '''parent);<br /> void refresh();<br /> QVariant data(const QModelIndex &amp;index, int role) const;<br />signals:<br />public slots:<br />private:<br /> const static char''' COLUMN_NAMES[];<br /> const static char* SQL_SELECT;<br />};<br /></code>


This is where all the magic really happens. The <span class="caps">QML</span> will reference the different columns by the role names set on the model.
=== Step 2: Implement two static constants ===


===Step 4: implement the data() method and the refresh() method:===
I always have two constant static variables in each of my models that derive from QSqlQueryModel, COLUMN_NAMES and SQL_SELECT. The order of the column names in COLUMN_NAMES must match the order they are listed in the SELECT statement


As long as the role that is requested is not a user role, return the default. But if the role is a user role, return the correct column:
<code>const char* ArtistsSqlModel::COLUMN_NAMES[] = {<br /> &quot;artist&amp;quot;,<br /> &quot;title&amp;quot;,<br /> &quot;year&amp;quot;,<br /> NULL<br />};<br />const char* ArtistsSqlModel::SQL_SELECT =<br />&quot;SELECT artists.artist, albums.title, albums.year&amp;quot;<br />&quot; FROM albums&amp;quot;<br />&quot; JOIN artists ON albums.artistid = artists.id&amp;quot;;<br /></code>


The refresh() method is the most important, without it, the model won’t show anything at all. The best is to stick to the setQuery method from QSqlQueryModel.<br />
=== Step 3: Set the roleNames in the constructor ===


===Step 5: Allow <span class="caps">QML</span> to see the model:===
This is where all the magic really happens. The QML will reference the different columns by the role names set on the model.


Create an instance of the model (make note that the constructor of the model did query the DB the first time). Then set it as a property on the viewer’s context, in this case I called it artistModel:
<code>ArtistsSqlModel::ArtistsSqlModel(QObject *parent) :<br /> QSqlQueryModel(parent)<br />{<br /> int idx = 0;<br /> QHash&amp;lt;int, QByteArray&amp;gt; roleNames;<br /> while( COLUMN_NAMES[idx]) {<br /> roleNames[Qt::UserRole + idx + 1] = COLUMN_NAMES[idx];<br /> idx++;<br /> }<br /> setRoleNames(roleNames);<br /> refresh();<br />}</code>


===Step 6: Create the <span class="caps">QML</span> list===
=== Step 4: implement the data() method and the refresh() method: ===


Since the model was exposed in step 5, the model exists and is ready to be used in <span class="caps">QML</span>. Simply set the model of the ListView to the name give in step 5.
As long as the role that is requested is not a user role, return the default. But if the role is a user role, return the correct column:


===Step 7: Create the <span class="caps">QML</span> delegate used by the list===
<code>QVariant ArtistsSqlModel::data(const QModelIndex &amp;index, int role) const<br />{<br /> QVariant value = QSqlQueryModel::data(index, role);<br /> if(role &lt; Qt::UserRole)<br /> {<br /> value = QSqlQueryModel::data(index, role);<br /> }<br /> else<br /> {<br /> int columnIdx = role - Qt::UserRole - 1;<br /> QModelIndex modelIndex = this-&gt;index(index.row(), columnIdx);<br /> value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);<br /> }<br /> return value;<br />} </code>


And finally implementation of the delegate. Notice here how the names set in the roleModel are used as the values to bind to the text property of the Text objects:
The refresh() method is the most important, without it, the model won't show anything at all. The best is to stick to the setQuery method from QSqlQueryModel.<br /><code>void ArtistsSqlModel::refresh()<br />{<br /> this-&gt;setQuery(SQL_SELECT);<br />}<br /></code>


'''ArtistItemDelegate.qml'''<br />
=== Step 5: Allow QML to see the model: ===


Source code is no longer available on my website, so feel free to contact me for the complete source code, I will be happy to share!
Create an instance of the model (make note that the constructor of the model did query the DB the first time). Then set it as a property on the viewer’s context, in this case I called it artistModel:
 
==A more generic approach==
 
Based on the initial wiki article I came up with a more generic approach that allow to use the same class for all your models instead of creating a derived class for each model.
 
Here it is :
 
'''sqlquerymodel.h'''<br />
 
'''sqlquerymodel.cpp'''<br />
 
And use it like this :<br />


===Categories:===
<code><br /> ArtistsSqlModel *artistsSqlModel = new ArtistsSqlModel( qApp);<br /> QmlApplicationViewer viewer;<br /> viewer.rootContext()<s>&gt;setContextProperty(&quot;artistsModel&amp;quot;, artistsSqlModel);<br /> viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);<br /> viewer.setMainQmlFile&amp;amp;#40;QLatin1String(&quot;qml/SQLListView/main.qml&amp;quot;&amp;#41;);<br /> viewer.showExpanded();
<br /></code>
<br />h3. Step 6: Create the QML list
<br />Since the model was exposed in step 5, the model exists and is ready to be used in QML. Simply set the model of the ListView to the name give in step 5.
<br /><code>import QtQuick 1.1
<br />Rectangle {<br /> width: 500<br /> height: 500<br /> MouseArea {<br /> anchors.fill: parent
<br /> Text {<br /> id: text1<br /> anchors.verticalCenterOffset: 20<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> text: qsTr(&quot;Testing&amp;quot;)<br /> font.pixelSize: 12<br /> }
<br /> ListView {<br /> id: list_view1<br /> x: 125<br /> y: 100<br /> width: 110<br /> height: 160<br /> delegate: ArtistItemDelegate {}<br /> model: artistsModel<br /> }<br /> }<br />}
<br /></code>
<br />h3. Step 7: Create the QML delegate used by the list
<br />And finally implementation of the delegate. Notice here how the names set in the roleModel are used as the values to bind to the text property of the Text objects:
<br />'''ArtistItemDelegate.qml'''<br /><code>import QtQuick 1.1
<br />Item {<br /> id: delegate<br /> width: delegate.ListView.view.width;<br /> height: 30<br /> clip: true<br /> anchors.margins: 4
<br /> Row {<br /> anchors.margins: 4<br /> anchors.fill: parent<br /> spacing: 4;
<br /> Text {<br /> text: artist<br /> width: 150<br /> }
<br /> Text {<br /> text: title<br /> width: 300;<br /> }
<br /> Text {<br /> text: year<br /> width: 50;<br /> }<br /> }<br />}</code>


* [[:Category:Developing with Qt|Developing_with_Qt]]
<br />Source code is no longer available on my website, so feel free to contact me for the complete source code, I will be happy to share!
** [[:Category:Developing with Qt::Qt-Quick|Qt Quick]]
* [[:Category:Developing with Qt::Qt-Quick::QML|QML]]


* [[:Category:Developing with Qt::Qt-Quick::Tutorial|Tutorial]]
<br />h2. A more generic approach
<br />Based on the initial wiki article I came up with a more generic approach that allow to use the same class for all your models instead of creating a derived class for each model.
<br />Here it is :
<br />'''sqlquerymodel.h'''<br /><code><br />#include &lt;QSqlQueryModel&amp;gt;
<br />class SqlQueryModel : public QSqlQueryModel<br />{<br /> Q_OBJECT
<br /> void generateRoleNames();
<br />public:<br /> explicit SqlQueryModel(QObject *parent = 0);
<br /> void setQuery(const QString &amp;query, const QSqlDatabase &amp;db = QSqlDatabase());<br /> void setQuery(const QSqlQuery &amp;query);<br /> QVariant data(const QModelIndex &amp;index, int role) const;
<br />signals:
<br />public slots:
<br />};<br /></code>
<br />'''sqlquerymodel.cpp'''<br /><code><br />#include &quot;sqlquerymodel.h&amp;quot;<br />#include &lt;QSqlRecord&amp;gt;<br />#include &lt;QSqlField&amp;gt;<br />#include &lt;QDebug&amp;gt;
<br />SqlQueryModel::SqlQueryModel(QObject *parent) :<br /> QSqlQueryModel(parent)<br />{
<br />}
<br />void SqlQueryModel::setQuery(const QString &amp;query, const QSqlDatabase &amp;db)<br />{<br /> QSqlQueryModel::setQuery(query,db);<br /> generateRoleNames();<br />}
<br />void SqlQueryModel::setQuery(const QSqlQuery &amp; query)<br />{<br /> QSqlQueryModel::setQuery(query);<br /> generateRoleNames();<br />}
<br />void SqlQueryModel::generateRoleNames()<br />{<br /> QHash&amp;lt;int, QByteArray&amp;gt; roleNames;<br /> for( int i = 0; i &lt; record().count(); i+'') {<br /> roleNames[Qt::UserRole'' i + 1] = record().fieldName(i).toAscii();<br /> }<br /> setRoleNames(roleNames);<br />}
<br />QVariant SqlQueryModel::data(const QModelIndex &amp;index, int role) const<br />{<br /> QVariant value = QSqlQueryModel::data(index, role);<br /> if(role &lt; Qt::UserRole)<br /> {<br /> value = QSqlQueryModel::data(index, role);<br /> }<br /> else<br /> {<br /> int columnIdx = role</s> Qt::UserRole - 1;<br /> QModelIndex modelIndex = this-&gt;index(index.row(), columnIdx);<br /> value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);<br /> }<br /> return value;<br />}<br /></code>


* [[:Category:HowTo|HowTo]]
And use it like this :<br /><code><br /> SqlQueryModel '''model1 = new SqlQueryModel(0);<br /> model1-&gt;setQuery(&quot;SELECT''' FROM table WHERE column='value'&quot;);<br /> SqlQueryModel '''model2 = new SqlQueryModel(0);<br /> model2-&gt;setQuery(&quot;SELECT''' FROM anothertable WHERE anothercolumn='value'&quot;);<br /> QmlApplicationViewer viewer;<br /> viewer.rootContext()<s>&gt;setContextProperty(&quot;myFirstModel&amp;quot;, model1);<br /> viewer.rootContext()</s>&gt;setContextProperty(&quot;mySecondModel&amp;quot;, model2);
* [[:Category:snippets|snippets]]

Revision as of 14:27, 23 February 2015





English Spanish Italian

[toc depth="3&quot; align_right="no&quot;]

How to use a QSqlQueryModel in QML

Initial fully detailed approach

Introduction

The software I develop, Photo Parata, is a client server application that uses a sql backend. Most of the time the data Photo Parata displays requires some joins. Because of this, most of the time the models are derived from QSqlQueryModel, not QSqlTableModel.
For QSqlRelationalTableModel, you can find an working but unexplained example at http://wiki.qt.io/QML_and_QSqlTableModel

In this how to, I will walk you through the steps of setting up a custom model for QML, derived from QSqlQueryModel.

I would like to thank "Christophe Dumez&quot;:http://cdumez.blogspot.com/ for his blog "How to use C++ list model in QML&quot;:http://cdumez.blogspot.com/2010/11/how-to-use-c-list-model-in-qml.html. It was this blog that allowed me to piece the following together.

Other useful sources were : "Using_QStandardItemModel_in_QML (wiki page)":http://www.developer.nokia.com/Community/Wiki/Using_QStandardItemModel_in_QML and "QSqlTableModel in QML (forum thread)":http://forum.qt.io/viewthread/12188

The data source for this example was lifted from one of the Sql examples that ships with Qt, "examples\sql\masterdetail&quot;:http://doc.qt.nokia.com/stable/sql-masterdetail-database-h.html

Step 1: Create a C++ class that derives from QSqlQueryModel:

All the magic happens in the constructor and in the overloaded data() method.

<br />class ArtistsSqlModel : public QSqlQueryModel<br />{<br /> Q_OBJECT<br />public:<br /> explicit ArtistsSqlModel(QObject '''parent);<br /> void refresh();<br /> QVariant data(const QModelIndex &amp;index, int role) const;<br />signals:<br />public slots:<br />private:<br /> const static char''' COLUMN_NAMES[];<br /> const static char* SQL_SELECT;<br />};<br />

Step 2: Implement two static constants

I always have two constant static variables in each of my models that derive from QSqlQueryModel, COLUMN_NAMES and SQL_SELECT. The order of the column names in COLUMN_NAMES must match the order they are listed in the SELECT statement

const char* ArtistsSqlModel::COLUMN_NAMES[] = {<br /> &quot;artist&amp;quot;,<br /> &quot;title&amp;quot;,<br /> &quot;year&amp;quot;,<br /> NULL<br />};<br />const char* ArtistsSqlModel::SQL_SELECT =<br />&quot;SELECT artists.artist, albums.title, albums.year&amp;quot;<br />&quot; FROM albums&amp;quot;<br />&quot; JOIN artists ON albums.artistid = artists.id&amp;quot;;<br />

Step 3: Set the roleNames in the constructor

This is where all the magic really happens. The QML will reference the different columns by the role names set on the model.

ArtistsSqlModel::ArtistsSqlModel(QObject *parent) :<br /> QSqlQueryModel(parent)<br />{<br /> int idx = 0;<br /> QHash&amp;lt;int, QByteArray&amp;gt; roleNames;<br /> while( COLUMN_NAMES[idx]) {<br /> roleNames[Qt::UserRole + idx + 1] = COLUMN_NAMES[idx];<br /> idx++;<br /> }<br /> setRoleNames(roleNames);<br /> refresh();<br />}

Step 4: implement the data() method and the refresh() method:

As long as the role that is requested is not a user role, return the default. But if the role is a user role, return the correct column:

QVariant ArtistsSqlModel::data(const QModelIndex &amp;index, int role) const<br />{<br /> QVariant value = QSqlQueryModel::data(index, role);<br /> if(role &lt; Qt::UserRole)<br /> {<br /> value = QSqlQueryModel::data(index, role);<br /> }<br /> else<br /> {<br /> int columnIdx = role - Qt::UserRole - 1;<br /> QModelIndex modelIndex = this-&gt;index(index.row(), columnIdx);<br /> value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);<br /> }<br /> return value;<br />}

The refresh() method is the most important, without it, the model won't show anything at all. The best is to stick to the setQuery method from QSqlQueryModel.

void ArtistsSqlModel::refresh()<br />{<br /> this-&gt;setQuery(SQL_SELECT);<br />}<br />

Step 5: Allow QML to see the model:

Create an instance of the model (make note that the constructor of the model did query the DB the first time). Then set it as a property on the viewer’s context, in this case I called it artistModel:

<br /> ArtistsSqlModel *artistsSqlModel = new ArtistsSqlModel( qApp);<br /> QmlApplicationViewer viewer;<br /> viewer.rootContext()<s>&gt;setContextProperty(&quot;artistsModel&amp;quot;, artistsSqlModel);<br /> viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);<br /> viewer.setMainQmlFile&amp;amp;#40;QLatin1String(&quot;qml/SQLListView/main.qml&amp;quot;&amp;#41;);<br /> viewer.showExpanded();
<br />


h3. Step 6: Create the QML list
Since the model was exposed in step 5, the model exists and is ready to be used in QML. Simply set the model of the ListView to the name give in step 5.


import QtQuick 1.1
<br />Rectangle {<br /> width: 500<br /> height: 500<br /> MouseArea {<br /> anchors.fill: parent
<br /> Text {<br /> id: text1<br /> anchors.verticalCenterOffset: 20<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> text: qsTr(&quot;Testing&amp;quot;)<br /> font.pixelSize: 12<br /> }
<br /> ListView {<br /> id: list_view1<br /> x: 125<br /> y: 100<br /> width: 110<br /> height: 160<br /> delegate: ArtistItemDelegate {}<br /> model: artistsModel<br /> }<br /> }<br />}
<br />


h3. Step 7: Create the QML delegate used by the list
And finally implementation of the delegate. Notice here how the names set in the roleModel are used as the values to bind to the text property of the Text objects:


ArtistItemDelegate.qml

import QtQuick 1.1
<br />Item {<br /> id: delegate<br /> width: delegate.ListView.view.width;<br /> height: 30<br /> clip: true<br /> anchors.margins: 4
<br /> Row {<br /> anchors.margins: 4<br /> anchors.fill: parent<br /> spacing: 4;
<br /> Text {<br /> text: artist<br /> width: 150<br /> }
<br /> Text {<br /> text: title<br /> width: 300;<br /> }
<br /> Text {<br /> text: year<br /> width: 50;<br /> }<br /> }<br />}


Source code is no longer available on my website, so feel free to contact me for the complete source code, I will be happy to share!


h2. A more generic approach
Based on the initial wiki article I came up with a more generic approach that allow to use the same class for all your models instead of creating a derived class for each model.
Here it is :


sqlquerymodel.h

<br />#include &lt;QSqlQueryModel&amp;gt;
<br />class SqlQueryModel : public QSqlQueryModel<br />{<br /> Q_OBJECT
<br /> void generateRoleNames();
<br />public:<br /> explicit SqlQueryModel(QObject *parent = 0);
<br /> void setQuery(const QString &amp;query, const QSqlDatabase &amp;db = QSqlDatabase());<br /> void setQuery(const QSqlQuery &amp;query);<br /> QVariant data(const QModelIndex &amp;index, int role) const;
<br />signals:
<br />public slots:
<br />};<br />


sqlquerymodel.cpp

<br />#include &quot;sqlquerymodel.h&amp;quot;<br />#include &lt;QSqlRecord&amp;gt;<br />#include &lt;QSqlField&amp;gt;<br />#include &lt;QDebug&amp;gt;
<br />SqlQueryModel::SqlQueryModel(QObject *parent) :<br /> QSqlQueryModel(parent)<br />{ 
<br />}
<br />void SqlQueryModel::setQuery(const QString &amp;query, const QSqlDatabase &amp;db)<br />{<br /> QSqlQueryModel::setQuery(query,db);<br /> generateRoleNames();<br />}
<br />void SqlQueryModel::setQuery(const QSqlQuery &amp; query)<br />{<br /> QSqlQueryModel::setQuery(query);<br /> generateRoleNames();<br />}
<br />void SqlQueryModel::generateRoleNames()<br />{<br /> QHash&amp;lt;int, QByteArray&amp;gt; roleNames;<br /> for( int i = 0; i &lt; record().count(); i+'') {<br /> roleNames[Qt::UserRole'' i + 1] = record().fieldName(i).toAscii();<br /> }<br /> setRoleNames(roleNames);<br />}
<br />QVariant SqlQueryModel::data(const QModelIndex &amp;index, int role) const<br />{<br /> QVariant value = QSqlQueryModel::data(index, role);<br /> if(role &lt; Qt::UserRole)<br /> {<br /> value = QSqlQueryModel::data(index, role);<br /> }<br /> else<br /> {<br /> int columnIdx = role</s> Qt::UserRole - 1;<br /> QModelIndex modelIndex = this-&gt;index(index.row(), columnIdx);<br /> value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);<br /> }<br /> return value;<br />}<br />

And use it like this :

SqlQueryModel model1 = new SqlQueryModel(0);
model1->setQuery("SELECT
FROM table WHERE column='value'");
SqlQueryModel model2 = new SqlQueryModel(0);
model2->setQuery("SELECT
FROM anothertable WHERE anothercolumn='value'");
QmlApplicationViewer viewer;
viewer.rootContext()>setContextProperty("myFirstModel&quot;, model1);
viewer.rootContext()
>setContextProperty("mySecondModel&quot;, model2);