Evgenii Legotckoi
Nov. 11, 2015, 7:25 p.m.

QML - Lesson 015. Development QML Data Mapper – Analog of QDataWidgetMapper

In the process of transferring a GUI softiny with QWidgets on QML QDataWidgetMapper found no analogue for QML. This fact was somewhat spoiling the mood. But there was nothing, but to look for other ways to implement or make their full version of this, your Qml Data Mapper.

Nevertheless, searching on the web expanses yielded the results as an example in 2011 by Nokia developers to implement the SQL Widget Mapper for BlackBerry based on Cascades (framework for native development on QML Qt under BlackBerry, respectively). By evening meditation on the present example was able to saw through the code for the current QML Qt, which is quite nice to feel like when working under the Desktop, and when running under Android, (cross-platform no matter how how).

To demonstrate the QML Data Mapper will write an application that works with the SQLite database. Each time an application writes a few lines in a database, from which data are collected using a data model to inherit from QSqlQueryModel and displayed in QML TableView. The main window of the application there is a button, pressing that cause, which displays information on the first row of the table dialog. When you double-click on one of the rows in a table is also called a dialog box, but the information on this line. Also present button in the dialog box to scroll through the information from the table.


Changes to implement QML Data Mapper

  1. In the first place changes affected the type of object that is passed to this operation Data Mapper. In the case of BlackBerry transmitted object Control framework Cascades class. The new version of the object passed to QObject class. Or just an object of class QQuickItem , which are objects QML interface. This is possible because QQuickItem inherited from QObject.
  2. The second change is that as a section parameter tabular data models, where will be taken the data for substitution in QML objects, variables of type QString transferred, whereas in my case it was more convenient to pass an int value to match the column's role in the data model, implementation QML for which was given in the previous lesson on data from QSqlQueryModel in QML TableView. Therefore the object type has been changed to int.
  3. Changing the type of the data model. In the case of BlackBerry used bb::cascades::DataModel. While this option works with objects QAbstractItemModel, and inherit from this class.
  4. Next Change - a simplification code update() method in the class private QmlDataMapperPrivate, which was due to the difference of calling QAbstractItemModel::data() method of the same name bb::cascades::DataModel.
  5. Ad addMapping() methods as public Q_SLOTS instead of the usual public. This change was due to the fact that when it was necessary to get to the object QML from a C ++ layer, which should work QML Data Mapper, and that is the dialog box hidden in these wilds object hierarchy QML: rootObject ->TableView->Tab->ItemTab->Dialog . ... I just have not mastered such perversion. Especially because the Data Mapper still registered in QML and his handling of slots goes QML, to swipe a line, for example, why should the same not be done with addMapping() method. Thus, there is no need to search for methods of using findChild required object. We simply pass the object from QML layer by id property.
  6. A new method updateDate (int index). When setting parameters in the Data Mapper objects QML did not immediately receive the data update, the data had to be diluted with a crutch.

Projectstructure for QML Data Mapper

The project consists of the known files on a lesson with QML QSqlQueryModel , and also has a new class QmlDataMapper . And also added DialogMapper.qml project file, which is a dialog box that controls will be compared the data from SQLite database data model.

QmlSqlQueryModel.pro

  1. TEMPLATE = app
  2.  
  3. QT += qml quick widgets sql
  4.  
  5. SOURCES += main.cpp \
  6. database.cpp \
  7. model.cpp \
  8. qmldatamapper.cpp
  9.  
  10. RESOURCES += qml.qrc
  11.  
  12. # Additional import path used to resolve QML modules in Qt Creator's code model
  13. QML_IMPORT_PATH =
  14.  
  15. # Default rules for deployment.
  16. include(deployment.pri)
  17.  
  18. HEADERS += \
  19. database.h \
  20. model.h \
  21. qmldatamapper.h

database.h

The database is created and filled with data using a wrapper class, which is also used in the classroom to work with QSqlTableModel and QSqlRelationalTableModel .

ATTENTION!!! - The database file is created in the folder C:/example , so the correct method or DataBase::connectToDataBase() example or create a folder on drive C .

  1. #ifndef DATABASE_H
  2. #define DATABASE_H
  3.  
  4. #include <QObject>
  5. #include <QSql>
  6. #include <QSqlQuery>
  7. #include <QSqlError>
  8. #include <QSqlDatabase>
  9. #include <QFile>
  10. #include <QDate>
  11. #include <QDebug>
  12.  
  13. #define DATABASE_HOSTNAME "ExampleDataBase"
  14. #define DATABASE_NAME "DataBase.db"
  15.  
  16. #define TABLE "TableExample"
  17. #define TABLE_DATE "date"
  18. #define TABLE_TIME "time"
  19. #define TABLE_MESSAGE "message"
  20. #define TABLE_RANDOM "random"
  21.  
  22. class DataBase : public QObject
  23. {
  24. Q_OBJECT
  25. public:
  26. explicit DataBase(QObject *parent = 0);
  27. ~DataBase();
  28. /* Methods to work directly with the class.
  29.   * Connect to the database and insert records into the table
  30. * */
  31. void connectToDataBase();
  32. bool inserIntoTable(const QVariantList &data);
  33.  
  34. private:
  35. QSqlDatabase db;
  36.  
  37. private:
  38. bool openDataBase();
  39. bool restoreDataBase();
  40. void closeDataBase();
  41. bool createTable();
  42. };
  43.  
  44. #endif // DATABASE_H

database.cpp

  1. #include "database.h"
  2.  
  3. DataBase::DataBase(QObject *parent) : QObject(parent)
  4. {
  5. this->connectToDataBase();
  6. /* After that is done filling the database tables of content that will appear in the TableView
  7. * */
  8. for(int i = 0; i < 4; i++){
  9. QVariantList data;
  10. int random = qrand(); // Get random integers to be inserted into the database
  11. data.append(QDate::currentDate()); // Get the current date to be inserted into the database
  12. data.append(QTime::currentTime()); // Get the current time to be inserted into the database
  13. // Prepare the received random number to be inserted into the database
  14. data.append(random);
  15. // Prepare message for insertion into the database
  16. data.append("Получено сообщение от " + QString::number(random));
  17. // Insert data into the database
  18. inserIntoTable(data);
  19. }
  20. }
  21.  
  22. DataBase::~DataBase()
  23. {
  24.  
  25. }
  26.  
  27. void DataBase::connectToDataBase()
  28. {
  29. if(!QFile("C:/example/" DATABASE_NAME).exists()){
  30. this->restoreDataBase();
  31. } else {
  32. this->openDataBase();
  33. }
  34. }
  35.  
  36. bool DataBase::restoreDataBase()
  37. {
  38. if(this->openDataBase()){
  39. if(!this->createTable()){
  40. return false;
  41. } else {
  42. return true;
  43. }
  44. } else {
  45. qDebug() << "Failed to restore the database";
  46. return false;
  47. }
  48. return false;
  49. }
  50.  
  51. bool DataBase::openDataBase()
  52. {
  53. db = QSqlDatabase::addDatabase("QSQLITE");
  54. db.setHostName(DATABASE_HOSTNAME);
  55. db.setDatabaseName("C:/example/" DATABASE_NAME);
  56. if(db.open()){
  57. return true;
  58. } else {
  59. return false;
  60. }
  61. }
  62.  
  63. void DataBase::closeDataBase()
  64. {
  65. db.close();
  66. }
  67.  
  68. bool DataBase::createTable()
  69. {
  70. QSqlQuery query;
  71. if(!query.exec( "CREATE TABLE " TABLE " ("
  72. "id INTEGER PRIMARY KEY AUTOINCREMENT, "
  73. TABLE_DATE " DATE NOT NULL,"
  74. TABLE_TIME " TIME NOT NULL,"
  75. TABLE_RANDOM " INTEGER NOT NULL,"
  76. TABLE_MESSAGE " VARCHAR(255) NOT NULL"
  77. " )"
  78. )){
  79. qDebug() << "DataBase: error of create " << TABLE;
  80. qDebug() << query.lastError().text();
  81. return false;
  82. } else {
  83. return true;
  84. }
  85. return false;
  86. }
  87.  
  88. bool DataBase::inserIntoTable(const QVariantList &data)
  89. {
  90. QSqlQuery query;
  91. query.prepare("INSERT INTO " TABLE " ( " TABLE_DATE ", "
  92. TABLE_TIME ", "
  93. TABLE_RANDOM ", "
  94. TABLE_MESSAGE " ) "
  95. "VALUES (:Date, :Time, :Random, :Message )");
  96. query.bindValue(":Date", data[0].toDate());
  97. query.bindValue(":Time", data[1].toTime());
  98. query.bindValue(":Random", data[2].toInt());
  99. query.bindValue(":Message", data[3].toString());
  100.  
  101. if(!query.exec()){
  102. qDebug() << "error insert into " << TABLE;
  103. qDebug() << query.lastError().text();
  104. return false;
  105. } else {
  106. return true;
  107. }
  108. return false;
  109. }

model.h

Model data representation remains unchanged from the previous lesson. Note that the transmit section of a room in QML layer should be in accordance with the role of numbers in the data model. That is, if the column, which will be compared with the control, has the role of the number of Qt:UserRole + 4 , then QML layer matching the installation must be carried out following the combination 0x0100 + 4 .

  1. #ifndef MODEL_H
  2. #define MODEL_H
  3.  
  4. #include <QObject>
  5. #include <QSqlQueryModel>
  6.  
  7. class Model : public QSqlQueryModel
  8. {
  9. Q_OBJECT
  10. public:
  11. /* We list all the roles that will be used in the TableView.
  12.   * As you can see, they have to be in the memory above the parameter Qt::UserRole.
  13.   * Due to the fact that the information below this address is not for customizations
  14. * */
  15. enum Roles {
  16. DateRole = Qt::UserRole + 1,
  17. TimeRole,
  18. RandomRole,
  19. MessageRole
  20. };
  21.  
  22. explicit Model(QObject *parent = 0);
  23.  
  24. // Override the method that will return the data
  25. QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
  26.  
  27. protected:
  28. /* hashed table of roles for speakers.
  29.      * The method used in QAbstractItemModel base class
  30.   * from which the class inherits QSqlQueryModel
  31. * */
  32. QHash<int, QByteArray> roleNames() const;
  33.  
  34. signals:
  35.  
  36. public slots:
  37. };
  38.  
  39. #endif // MODEL_H

model.cpp

  1. #include "model.h"
  2.  
  3. Model::Model(QObject *parent) :
  4. QSqlQueryModel(parent)
  5. {
  6.  
  7. }
  8.  
  9. // The method for obtaining data from the model
  10. QVariant Model::data(const QModelIndex & index, int role) const {
  11.  
  12. // Determine the number of speakers, on the role of number
  13. int columnId = role - Qt::UserRole - 1;
  14. // Create the index using the ID column
  15. QModelIndex modelIndex = this->index(index.row(), columnId);
  16.  
  17. return QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
  18. }
  19.  
  20. // Method for role names through a hashed table.
  21. QHash<int, QByteArray> Model::roleNames() const {
  22.  
  23. QHash<int, QByteArray> roles;
  24. roles[DateRole] = "date";
  25. roles[TimeRole] = "time";
  26. roles[RandomRole] = "random";
  27. roles[MessageRole] = "message";
  28. return roles;
  29. }

qmldatamapper.h

Now what concerns the very Data Mapper class for QML. This class can be used without any changes to any other data models, which are organized on the principle of identity, as well as above the given data model, as well as inherited from QAbstractItemModel and its derived classes.

  1. #ifndef QMLDATAMAPPER_H
  2. #define QMLDATAMAPPER_H
  3.  
  4. /****************************************************************************
  5. ** Here the following code is the result of a software revision
  6.  ** сode for the BlackBerry platform 10 of the Qt Toolkit and also ditributed under
  7.  ** for the following license, which has been applied to the original software code
  8. **
  9. ** EVILEG - Evgenii Legotckoi - 2015
  10. ** Contact: http://www.evileg.com
  11. ** Contact: Evgenii Legotckoi (legotskoy@gmail.com)
  12. ** All rights reserved.
  13. ****************************************************************************/
  14.  
  15. /****************************************************************************
  16. **
  17. ** Portions Copyright (C) 2012 Research In Motion Limited.
  18. ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
  19. ** All rights reserved.
  20. ** Contact: Research In Motion Ltd. )
  21. ** Contact: Nokia Corporation (qt-info@nokia.com)
  22. **
  23. ** This file is part of the examples of the BB10 Platform and is derived
  24. ** from a similar file that is part of the Qt Toolkit.
  25. **
  26. ** You may use this file under the terms of the BSD license as follows:
  27. **
  28. ** "Redistribution and use in source and binary forms, with or without
  29. ** modification, are permitted provided that the following conditions are
  30. ** met:
  31. ** * Redistributions of source code must retain the above copyright
  32. ** notice, this list of conditions and the following disclaimer.
  33. ** * Redistributions in binary form must reproduce the above copyright
  34. ** notice, this list of conditions and the following disclaimer in
  35. ** the documentation and/or other materials provided with the
  36. ** distribution.
  37. ** * Neither the name of Research In Motion, nor the name of Nokia
  38. ** Corporation and its Subsidiary(-ies), nor the names of its
  39. ** contributors may be used to endorse or promote products
  40. ** derived from this software without specific prior written
  41. ** permission.
  42. **
  43. ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  44. ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  45. ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  46. ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  47. ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  48. ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  49. ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  50. ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  51. ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  52. ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  53. ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  54. **
  55. ****************************************************************************/
  56.  
  57. #include <QObject>
  58. #include <QPointer>
  59. #include <QAbstractItemModel>
  60. #include <QModelIndex>
  61. #include <QQuickItem>
  62.  
  63. class QmlDataMapperPrivate;
  64.  
  65. /**
  66. * Class QmlDataMapper provides mapping between the lines and the data model representation in the widget QML.
  67. * QmlDataMapper can be used to create the controls database danyh by comparing them with the data model lines.
  68. * Whenever changing the current index, each control element is updated with data from the model.
  69. */
  70.  
  71. class QmlDataMapper : public QObject
  72. {
  73. Q_OBJECT
  74.  
  75. /**
  76. * Current index QmlDataMapper. If you are using SQL model based on SQL tables,
  77.   * the index will be between 0 and the number of rows. .
  78. */
  79. Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
  80.  
  81. // The number of rows represented by a model
  82. Q_PROPERTY(int count READ count NOTIFY countChanged)
  83. public:
  84. explicit QmlDataMapper(QObject *parent = 0);
  85.  
  86. ~QmlDataMapper();
  87.  
  88. /**
  89. * It removes all mappings that have been created by addMapping ().
  90. */
  91. void clearMapping();
  92.  
  93. // Removes the mapping for the object of QML layer
  94. void removeMapping(QObject *object);
  95.  
  96. // Methods of access to properties
  97. int currentIndex() const;
  98. int count() const;
  99.  
  100. // Return them to a property that is used to update the values in the control
  101. QByteArray mappedPropertyName(QObject *object) const;
  102.  
  103. // Returns the section identifier that is mapped to the given control
  104. int mappedSection(QObject *object) const;
  105.  
  106. // It returns the control that is associated with the ID section in the model
  107. QObject * mappedControlAt(const int &section) const;
  108.  
  109. // Returns data model that works QmlDataMapper
  110. QAbstractItemModel * model() const;
  111.  
  112. // Sets the model that will work QmlDataMapper
  113. void setModel(QAbstractItemModel *model);
  114.  
  115. public Q_SLOTS:
  116. /**
  117. * This method creates a mapping between the identifier and the control section of the data model.
  118. * For SQL model identifier section is but the role of the column in the data view model
  119. * These will be installed to control property, which depend on the properties of the control
  120. * This method is used to set and control section without the properties here.
  121. * And in this case, only the name "text", in which the data will be substituted
  122. */
  123. void addMapping(QObject *object, const int &section);
  124.  
  125. /**
  126. * This method creates a mapping between the identifier and the control section of the data model.
  127. * For SQL model identifier section is but the role of the column in the data view model
  128. * The data will be supplied to a control element in the specified property
  129. */
  130. void addMapping(QObject *object, const int &section, const QByteArray &propertyName);
  131.  
  132. // This method discards the data in the control
  133. void revert();
  134.  
  135. //This method sets the index at a given value
  136. void setCurrentIndex(int index);
  137.  
  138. // This method sets the index of the first line
  139. void toFirst();
  140.  
  141. // This method sets the index of the last row
  142. void toLast();
  143.  
  144. // This method produces the increment of the current row index
  145. void toNext();
  146.  
  147. // This method produces a decrement of the current row index
  148. void toPrevious();
  149.  
  150. // Update the specified index data
  151. void updateData(int index);
  152.  
  153. Q_SIGNALS:
  154. // Сигналы уведомления об изменении для свойств класса
  155. void currentIndexChanged(int index);
  156. void countChanged();
  157.  
  158. private:
  159. // Private class, which is hidden in this class API
  160. QmlDataMapperPrivate * const d;
  161. };
  162.  
  163. #endif // QMLDATAMAPPER_H

qmldatamapper.cpp

  1. /****************************************************************************
  2. ** EVILEG - Evgenii Legotckoi - 2015
  3. ** Contact: http://www.evileg.com
  4. ** Contact: Evgenii Legotckoi (legotskoy@gmail.com)
  5. ** Contact: Евгений Легоцкой (legotskoy@gmail.com)
  6. ** All rights reserved.
  7. ****************************************************************************/
  8.  
  9. /****************************************************************************
  10. **
  11. ** Portions Copyright (C) 2012 Research In Motion Limited.
  12. ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
  13. ** All rights reserved.
  14. ** Contact: Research In Motion Ltd. )
  15. ** Contact: Nokia Corporation (qt-info@nokia.com)
  16. **
  17. ** This file is part of the examples of the BB10 Platform and is derived
  18. ** from a similar file that is part of the Qt Toolkit.
  19. **
  20. ** You may use this file under the terms of the BSD license as follows:
  21. **
  22. ** "Redistribution and use in source and binary forms, with or without
  23. ** modification, are permitted provided that the following conditions are
  24. ** met:
  25. ** * Redistributions of source code must retain the above copyright
  26. ** notice, this list of conditions and the following disclaimer.
  27. ** * Redistributions in binary form must reproduce the above copyright
  28. ** notice, this list of conditions and the following disclaimer in
  29. ** the documentation and/or other materials provided with the
  30. ** distribution.
  31. ** * Neither the name of Research In Motion, nor the name of Nokia
  32. ** Corporation and its Subsidiary(-ies), nor the names of its
  33. ** contributors may be used to endorse or promote products
  34. ** derived from this software without specific prior written
  35. ** permission.
  36. **
  37. ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  38. ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  39. ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  40. ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  41. ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  42. ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  43. ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  44. ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  45. ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  46. ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  47. ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  48. **
  49. ****************************************************************************/
  50.  
  51. #include "qmldatamapper.h"
  52. #include <QDataWidgetMapper>
  53.  
  54. struct Mapping {
  55. QPointer <QObject> object;
  56. int section;
  57. QByteArray propertyName;
  58. };
  59.  
  60. /**
  61. * Private class that contains all the hidden methods and variables.
  62. * It uses a concept that allows you to change the internal API without affecting the external API
  63. *
  64. */
  65. class QmlDataMapperPrivate
  66. {
  67. public:
  68. QmlDataMapperPrivate()
  69. : m_model(0), m_currentIndex(-1)
  70. {
  71. }
  72.  
  73. // Auxiliary method of updating participants on the set matching the specified parameters
  74. void updateMapping(Mapping &mapping, QObject *object, const int &section, const QByteArray &propertyName);
  75.  
  76. // This method establishes the actual data comparison of the data model in the controls
  77. void update();
  78.  
  79. // The data model, which made the work
  80. QAbstractItemModel* m_model;
  81.  
  82. // The list of comparisons that are installed
  83. QVector<Mapping> m_mappings;
  84.  
  85. // Current index QmlDataMapper
  86. int m_currentIndex;
  87. };
  88.  
  89. void QmlDataMapperPrivate::updateMapping(Mapping &mapping, QObject *object, const int &section, const QByteArray &propertyName)
  90. {
  91. mapping.object = object;
  92. mapping.section = section;
  93.  
  94. // If the property name is not specified, it defaults to "text" property
  95. mapping.propertyName = (propertyName.isEmpty() ? "text" : propertyName);
  96. }
  97.  
  98. void QmlDataMapperPrivate::update()
  99. {
  100. // List of checks before the data update
  101. if (!m_model)
  102. return;
  103.  
  104. if (m_mappings.isEmpty())
  105. return;
  106.  
  107. if (m_currentIndex == -1)
  108. return;
  109.  
  110. // Enumerating all the available comparisons
  111. foreach (const Mapping &mapping, m_mappings) {
  112. if (mapping.object) {
  113. // Updating controls, setting data on the properties of the role
  114. mapping.object->setProperty(mapping.propertyName, m_model->data(m_model->index(m_currentIndex,0), mapping.section));
  115. }
  116. }
  117. }
  118.  
  119. QmlDataMapper::QmlDataMapper(QObject *parent)
  120. : QObject(parent), d(new QmlDataMapperPrivate())
  121. {
  122.  
  123. }
  124.  
  125. QmlDataMapper::~QmlDataMapper()
  126. {
  127. delete d;
  128. }
  129.  
  130.  
  131. void QmlDataMapper::addMapping(QObject *object, const int &section)
  132. {
  133. // Adding a comparison with the default property
  134. addMapping(object, section, "text");
  135. }
  136.  
  137. void QmlDataMapper::addMapping(QObject *object, const int &section, const QByteArray &propertyName)
  138. {
  139. // Verification that the added mapping already exists ...
  140. for (int i = 0; i < d->m_mappings.count(); ++i) {
  141. Mapping &mapping = d->m_mappings[i];
  142. if (mapping.object == object) {
  143. // ... in the case of the existence of the comparison, produce update data section and control properties ...
  144. d->updateMapping(mapping, object, section, propertyName);
  145.  
  146. // ... and updating content control
  147. d->update();
  148. return;
  149. }
  150. }
  151.  
  152. // Otherwise, add the new mapping
  153. Mapping mapping;
  154. d->updateMapping(mapping, object, section, propertyName);
  155. d->m_mappings.append(mapping);
  156.  
  157. // ... and updating content control
  158. d->update();
  159. }
  160.  
  161. void QmlDataMapper::clearMapping()
  162. {
  163. // Cleaning Comparison list
  164. d->m_mappings.clear();
  165. }
  166.  
  167. int QmlDataMapper::currentIndex() const
  168. {
  169. return d->m_currentIndex;
  170. }
  171.  
  172. int QmlDataMapper::count() const
  173. {
  174. if (!d->m_model)
  175. return 0;
  176.  
  177. // Return the number of rows in the data view model
  178. return d->m_model->rowCount();
  179. }
  180.  
  181. QByteArray QmlDataMapper::mappedPropertyName(QObject *object) const
  182. {
  183. foreach(const Mapping &mapping, d->m_mappings) {
  184. if (mapping.object == object)
  185. return mapping.propertyName;
  186. }
  187.  
  188. return QByteArray();
  189. }
  190.  
  191. int QmlDataMapper::mappedSection(QObject *object) const
  192. {
  193. foreach(const Mapping &mapping, d->m_mappings) {
  194. if (mapping.object == object)
  195. return mapping.section;
  196. }
  197.  
  198. return 0;
  199. }
  200.  
  201. QObject* QmlDataMapper::mappedControlAt(const int &section) const
  202. {
  203. foreach(const Mapping &mapping, d->m_mappings) {
  204. if (mapping.section == section)
  205. return mapping.object;
  206. }
  207.  
  208. return 0;
  209. }
  210.  
  211. QAbstractItemModel* QmlDataMapper::model() const
  212. {
  213. return d->m_model;
  214. }
  215.  
  216. void QmlDataMapper::removeMapping(QObject *object)
  217. {
  218. for (int i = 0; i < d->m_mappings.count(); ++i) {
  219. if (d->m_mappings[i].object == object) {
  220. d->m_mappings.remove(i);
  221. return;
  222. }
  223. }
  224. }
  225.  
  226. void QmlDataMapper::setModel(QAbstractItemModel *model)
  227. {
  228. d->m_model = model;
  229.  
  230. // Setting the initial contents of the index data representation model
  231. d->m_currentIndex = 0;
  232.  
  233. // We produce content update control
  234. d->update();
  235. emit countChanged();
  236. }
  237.  
  238. void QmlDataMapper::revert()
  239. {
  240. // We produce updated content control
  241. d->update();
  242. }
  243.  
  244. void QmlDataMapper::setCurrentIndex(int index)
  245. {
  246. //Check the availability of the data model
  247. if (!d->m_model)
  248. return;
  249.  
  250. // We obtain the number of lines
  251. const int rowCount = d->m_model->rowCount();
  252. // ... We ignore the incorrect value of the index
  253. if (index < 0 || index >= rowCount)
  254. return;
  255.  
  256. d->m_currentIndex = index;
  257. d->update();
  258. emit currentIndexChanged(d->m_currentIndex);
  259. }
  260.  
  261. void QmlDataMapper::toFirst()
  262. {
  263. setCurrentIndex(0);
  264. }
  265.  
  266. void QmlDataMapper::toLast()
  267. {
  268. if (!d->m_model)
  269. return;
  270.  
  271. const int rowCount = d->m_model->rowCount();
  272.  
  273. setCurrentIndex(rowCount - 1);
  274. }
  275.  
  276. void QmlDataMapper::toNext()
  277. {
  278. setCurrentIndex(d->m_currentIndex + 1);
  279. }
  280.  
  281. void QmlDataMapper::toPrevious()
  282. {
  283. setCurrentIndex(d->m_currentIndex - 1);
  284. }
  285.  
  286. void QmlDataMapper::updateData(int index)
  287. {
  288. // Set the desired index
  289. d->m_currentIndex = index;
  290.  
  291. // and update the values in the controls
  292. d->update();
  293. emit countChanged();
  294. }

main.cpp

To use the Data Mapper in C++ part need only declare it, initialize and register to access it from QML layer using the method setContextProperty( "mapper", mapper). And do not forget to put it in the model data representation method setModel() .

The rest of the work with the Data Mapper will be made in QML layer. Including setting comparison object data model columns.

  1. #include <QApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQmlContext>
  4.  
  5. #include <qmldatamapper.h>
  6. #include <database.h>
  7. #include <model.h>
  8.  
  9. int main(int argc, char *argv[])
  10. {
  11. QApplication app(argc, argv);
  12. QQmlApplicationEngine engine;
  13.  
  14. /// Initialize database
  15. DataBase database;
  16. /// We declare and initialize the data model representation
  17. Model *model = new Model();
  18. /** Since we otnasledovalis from QSqlQueryModel,
  19.   * the data sample, we need to perform SQL-query
  20.   * in which we select all the needed fields from the desired table to us
  21. * */
  22. model->setQuery("SELECT " TABLE_DATE ", " TABLE_TIME ", " TABLE_RANDOM ", " TABLE_MESSAGE
  23. " FROM " TABLE);
  24.  
  25. // We declare and initialize the objectQmlDataMapper
  26. QmlDataMapper *mapper = new QmlDataMapper();
  27. mapper->setModel(model);
  28.  
  29. /** And it is already familiar from the lessons of the signals and slots in QML.
  30.   * We put the resulting model in QML context to be able to refer to the model name "myModel"
  31. * */
  32. engine.rootContext()->setContextProperty("myModel", model);
  33.  
  34. /* А также даём доступ к Mapper в контексте QML
  35. * */
  36. engine.rootContext()->setContextProperty("mapper", mapper);
  37.  
  38. engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
  39.  
  40. return app.exec();
  41. }

main.qml

This file layer declared button, by pressing which will open a dialog box, and TableView, in which data from the data model will be displayed. Also, the dialog box will be opened by double-clicking on an entry in the TableView . The button will open a dialog box in which information will be displayed on the very first line in the table. Double-click to open a dialog with information about that line on which we clicked.

  1. import QtQuick 2.5
  2. import QtQuick.Controls 1.4
  3. import QtQuick.Layouts 1.1
  4.  
  5. ApplicationWindow {
  6. visible: true
  7. width: 640
  8. height: 480
  9. title: qsTr("Hello World")
  10.  
  11. RowLayout {
  12. id: row
  13.  
  14. anchors.top: parent.top
  15. anchors.left: parent.left
  16. anchors.right: parent.right
  17. anchors.margins: 5
  18.  
  19. height: 35
  20.  
  21. Button {
  22. id: button
  23. text: qsTr("Open Mapper")
  24. width: 150
  25.  
  26. Layout.fillHeight: true
  27.  
  28. onClicked: {
  29. dialog.show()
  30. }
  31. }
  32.  
  33. }
  34.  
  35. TableView {
  36. id: tableView
  37. anchors.top: row.bottom
  38. anchors.left: parent.left
  39. anchors.right: parent.right
  40. anchors.bottom: parent.bottom
  41. anchors.margins: 5
  42.  
  43. TableViewColumn {
  44. role: "date" // These roles are roles names match with a C ++ model
  45. title: "Date"
  46. }
  47.  
  48. TableViewColumn {
  49. role: "time" // These roles are roles names match with a C ++ model
  50. title: "Time"
  51. }
  52.  
  53. TableViewColumn {
  54. role: "random" // These roles are roles names match with a C ++ model
  55. title: "Random"
  56. }
  57.  
  58. TableViewColumn {
  59. role: "message" // These roles are roles names match with a C ++ model
  60. title: "Message"
  61. }
  62.  
  63. model: myModel
  64.  
  65. rowDelegate: Rectangle {
  66. anchors.fill: parent
  67. color: styleData.selected ? 'skyblue' : (styleData.alternate ? 'whitesmoke' : 'white');
  68. MouseArea {
  69. anchors.fill: parent
  70. acceptedButtons: Qt.RightButton | Qt.LeftButton
  71. onClicked: {
  72. tableView.selection.clear()
  73. tableView.selection.select(styleData.row)
  74. tableView.currentRow = styleData.row
  75. tableView.focus = true
  76. }
  77.  
  78. onDoubleClicked: {
  79. /* doubleclick on the line at the open information
  80.   * of the corresponding line dialog box
  81. * */
  82. dialog.editEntry(styleData.row)
  83. }
  84. }
  85. }
  86. }
  87.  
  88. DialogMapper {
  89. id: dialog
  90. }
  91. }

DialogMapper.qml

The dialog is written in a separate file. Working with QML Data Mapper is reduced only to the addition of QML objects in the Data Mapper, Flipped rows of data models, as well as installing the line index in the Data Mapper data for a particular row of the table, if the dialog box by double clicking on a line in the TableView .

  1. import QtQuick 2.0
  2. import QtQuick.Controls 1.4
  3. import QtQuick.Dialogs 1.2
  4. import QtQuick.Layouts 1.1
  5.  
  6. Dialog {
  7. title: qsTr("Dialog Mapper")
  8. height: 220
  9. width: 480
  10.  
  11. // function, which opens with the data from the first line of the dialog box
  12. function show() {
  13. open()
  14. mapper.updateData(0)
  15. }
  16.  
  17. /* The function, which opens with the line of the data dialog box,
  18.   * on which was promoted doubleclick
  19. * */
  20. function editEntry(row) {
  21. open()
  22. mapper.updateData(row)
  23. }
  24.  
  25. contentItem: Rectangle {
  26. implicitHeight: 220
  27. implicitWidth: 480
  28.  
  29. GridLayout {
  30. anchors.top: parent.top
  31. anchors.left: parent.left
  32. anchors.right: parent.right
  33. anchors.margins: 5
  34. rowSpacing: 10
  35. columnSpacing: 10
  36. rows: 4
  37. columns: 2
  38.  
  39. Text {
  40. text: qsTr("Дата")
  41. Layout.fillWidth: true
  42. }
  43.  
  44. TextField {
  45. id: dateField // With this id we send object to Data Mapper
  46. Layout.preferredWidth: 200
  47. }
  48.  
  49. Text {
  50. text: qsTr("Время")
  51. Layout.fillWidth: true
  52. }
  53.  
  54. TextField {
  55. id: timeField // With this id we send object to Data Mapper
  56. Layout.preferredWidth: 200
  57. }
  58.  
  59. Text {
  60. text: qsTr("Random number")
  61. Layout.fillWidth: true
  62. }
  63.  
  64. TextField {
  65. id: randomField // With this id we send object to Data Mapper
  66. Layout.preferredWidth: 200
  67. }
  68.  
  69. Text {
  70. text: qsTr("Message")
  71. Layout.fillWidth: true
  72. }
  73.  
  74. TextField {
  75. id: messageField // With this id we send object to Data Mapper
  76. Layout.preferredWidth: 200
  77. }
  78. }
  79.  
  80. Rectangle {
  81. color: "#eeeeee"
  82. height: 50
  83. anchors.bottom: parent.bottom
  84. anchors.left: parent.left
  85. anchors.right: parent.right
  86.  
  87. RowLayout {
  88. anchors.bottom: parent.bottom
  89. anchors.left: parent.left
  90. anchors.right: parent.right
  91. anchors.margins: 5
  92. spacing: 10
  93.  
  94. Button {
  95. id: buttonPrevious
  96. text: qsTr("Previous")
  97. Layout.preferredWidth: 80
  98.  
  99. onClicked: {
  100. mapper.toPrevious()
  101. }
  102. }
  103.  
  104. Button {
  105. id: buttonNext
  106. text: qsTr("Next")
  107. Layout.preferredWidth: 80
  108.  
  109. onClicked: {
  110. mapper.toNext()
  111. }
  112. }
  113.  
  114. Rectangle {
  115. Layout.fillWidth: true
  116. color: "#eeeeee"
  117. }
  118.  
  119. Button {
  120. id: buttonOk
  121. text: qsTr("Ok")
  122. Layout.preferredWidth: 80
  123. onClicked: close()
  124. }
  125.  
  126. Button {
  127. id: buttonCancel
  128. text: qsTr("Cancel")
  129. Layout.preferredWidth: 80
  130. onClicked: close()
  131. }
  132. }
  133. }
  134. }
  135.  
  136. /* According to the result of the creation of the dialog box to add
  137.   * a Data Mapper QML objects by their id, with an indication of the role of numbers
  138.   * in the data model and object property, which will be substituted in the data.
  139. *
  140. * То есть: dateField - this id object TextField
  141. * 0x0100 + 1 - this DateRole of model, which equal Qt::UserRole + 1
  142. * "text" - this property of TextField object
  143. * */
  144. Component.onCompleted: {
  145. mapper.addMapping(dateField, (0x0100 + 1), "text")
  146. mapper.addMapping(timeField, (0x0100 + 2), "text")
  147. mapper.addMapping(randomField, (0x0100 + 3), "text")
  148. mapper.addMapping(messageField, (0x0100 + 4), "text")
  149. }
  150. }

Conclusion

Summing up the above given information, we can say that everything is new - is well forgotten old. In fact, you can shovel existing developments on BlackBerry and port code under current QML Qt for Desktop and Android , elaborating or missing unrealized objects and classes for working with QML. However, Qt developers to know better how to build a development framework.

This class is not a complete analog QDataWidgetMapper, because it does not implement edit entries in the database. But at the same time it performs its intended purpose.

You can download the source code of the project on the following link: The source project QmlDataMapper

Video

Do you like it? Share on social networks!

Comments

Only authorized users can post comments.
Please, Log in or Sign up
  • Last comments
  • AK
    April 1, 2025, 11:41 a.m.
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    March 9, 2025, 9:02 p.m.
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    March 9, 2025, 4:14 p.m.
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
  • ИМ
    Nov. 22, 2024, 9:51 p.m.
    Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
  • Evgenii Legotckoi
    Oct. 31, 2024, 11:37 p.m.
    Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup