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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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
TEMPLATE = app QT += qml quick widgets sql SOURCES += main.cpp \ database.cpp \ model.cpp \ qmldatamapper.cpp RESOURCES += qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Default rules for deployment. include(deployment.pri) HEADERS += \ database.h \ model.h \ 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 .
#ifndef DATABASE_H #define DATABASE_H #include <QObject> #include <QSql> #include <QSqlQuery> #include <QSqlError> #include <QSqlDatabase> #include <QFile> #include <QDate> #include <QDebug> #define DATABASE_HOSTNAME "ExampleDataBase" #define DATABASE_NAME "DataBase.db" #define TABLE "TableExample" #define TABLE_DATE "date" #define TABLE_TIME "time" #define TABLE_MESSAGE "message" #define TABLE_RANDOM "random" class DataBase : public QObject { Q_OBJECT public: explicit DataBase(QObject *parent = 0); ~DataBase(); /* Methods to work directly with the class. * Connect to the database and insert records into the table * */ void connectToDataBase(); bool inserIntoTable(const QVariantList &data); private: QSqlDatabase db; private: bool openDataBase(); bool restoreDataBase(); void closeDataBase(); bool createTable(); }; #endif // DATABASE_H
database.cpp
#include "database.h" DataBase::DataBase(QObject *parent) : QObject(parent) { this->connectToDataBase(); /* After that is done filling the database tables of content that will appear in the TableView * */ for(int i = 0; i < 4; i++){ QVariantList data; int random = qrand(); // Get random integers to be inserted into the database data.append(QDate::currentDate()); // Get the current date to be inserted into the database data.append(QTime::currentTime()); // Get the current time to be inserted into the database // Prepare the received random number to be inserted into the database data.append(random); // Prepare message for insertion into the database data.append("Получено сообщение от " + QString::number(random)); // Insert data into the database inserIntoTable(data); } } DataBase::~DataBase() { } void DataBase::connectToDataBase() { if(!QFile("C:/example/" DATABASE_NAME).exists()){ this->restoreDataBase(); } else { this->openDataBase(); } } bool DataBase::restoreDataBase() { if(this->openDataBase()){ if(!this->createTable()){ return false; } else { return true; } } else { qDebug() << "Failed to restore the database"; return false; } return false; } bool DataBase::openDataBase() { db = QSqlDatabase::addDatabase("QSQLITE"); db.setHostName(DATABASE_HOSTNAME); db.setDatabaseName("C:/example/" DATABASE_NAME); if(db.open()){ return true; } else { return false; } } void DataBase::closeDataBase() { db.close(); } bool DataBase::createTable() { QSqlQuery query; if(!query.exec( "CREATE TABLE " TABLE " (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " TABLE_DATE " DATE NOT NULL," TABLE_TIME " TIME NOT NULL," TABLE_RANDOM " INTEGER NOT NULL," TABLE_MESSAGE " VARCHAR(255) NOT NULL" " )" )){ qDebug() << "DataBase: error of create " << TABLE; qDebug() << query.lastError().text(); return false; } else { return true; } return false; } bool DataBase::inserIntoTable(const QVariantList &data) { QSqlQuery query; query.prepare("INSERT INTO " TABLE " ( " TABLE_DATE ", " TABLE_TIME ", " TABLE_RANDOM ", " TABLE_MESSAGE " ) " "VALUES (:Date, :Time, :Random, :Message )"); query.bindValue(":Date", data[0].toDate()); query.bindValue(":Time", data[1].toTime()); query.bindValue(":Random", data[2].toInt()); query.bindValue(":Message", data[3].toString()); if(!query.exec()){ qDebug() << "error insert into " << TABLE; qDebug() << query.lastError().text(); return false; } else { return true; } return false; }
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 .
#ifndef MODEL_H #define MODEL_H #include <QObject> #include <QSqlQueryModel> class Model : public QSqlQueryModel { Q_OBJECT public: /* We list all the roles that will be used in the TableView. * As you can see, they have to be in the memory above the parameter Qt::UserRole. * Due to the fact that the information below this address is not for customizations * */ enum Roles { DateRole = Qt::UserRole + 1, TimeRole, RandomRole, MessageRole }; explicit Model(QObject *parent = 0); // Override the method that will return the data QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; protected: /* hashed table of roles for speakers. * The method used in QAbstractItemModel base class * from which the class inherits QSqlQueryModel * */ QHash<int, QByteArray> roleNames() const; signals: public slots: }; #endif // MODEL_H
model.cpp
#include "model.h" Model::Model(QObject *parent) : QSqlQueryModel(parent) { } // The method for obtaining data from the model QVariant Model::data(const QModelIndex & index, int role) const { // Determine the number of speakers, on the role of number int columnId = role - Qt::UserRole - 1; // Create the index using the ID column QModelIndex modelIndex = this->index(index.row(), columnId); return QSqlQueryModel::data(modelIndex, Qt::DisplayRole); } // Method for role names through a hashed table. QHash<int, QByteArray> Model::roleNames() const { QHash<int, QByteArray> roles; roles[DateRole] = "date"; roles[TimeRole] = "time"; roles[RandomRole] = "random"; roles[MessageRole] = "message"; return roles; }
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.
#ifndef QMLDATAMAPPER_H #define QMLDATAMAPPER_H /**************************************************************************** ** Here the following code is the result of a software revision ** сode for the BlackBerry platform 10 of the Qt Toolkit and also ditributed under ** for the following license, which has been applied to the original software code ** ** EVILEG - Evgenii Legotckoi - 2015 ** Contact: http://www.evileg.com ** Contact: Evgenii Legotckoi (legotskoy@gmail.com) ** All rights reserved. ****************************************************************************/ /**************************************************************************** ** ** Portions Copyright (C) 2012 Research In Motion Limited. ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Research In Motion Ltd. ) ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the examples of the BB10 Platform and is derived ** from a similar file that is part of the Qt Toolkit. ** ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Research In Motion, nor the name of Nokia ** Corporation and its Subsidiary(-ies), nor the names of its ** contributors may be used to endorse or promote products ** derived from this software without specific prior written ** permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ****************************************************************************/ #include <QObject> #include <QPointer> #include <QAbstractItemModel> #include <QModelIndex> #include <QQuickItem> class QmlDataMapperPrivate; /** * Class QmlDataMapper provides mapping between the lines and the data model representation in the widget QML. * QmlDataMapper can be used to create the controls database danyh by comparing them with the data model lines. * Whenever changing the current index, each control element is updated with data from the model. */ class QmlDataMapper : public QObject { Q_OBJECT /** * Current index QmlDataMapper. If you are using SQL model based on SQL tables, * the index will be between 0 and the number of rows. . */ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) // The number of rows represented by a model Q_PROPERTY(int count READ count NOTIFY countChanged) public: explicit QmlDataMapper(QObject *parent = 0); ~QmlDataMapper(); /** * It removes all mappings that have been created by addMapping (). */ void clearMapping(); // Removes the mapping for the object of QML layer void removeMapping(QObject *object); // Methods of access to properties int currentIndex() const; int count() const; // Return them to a property that is used to update the values in the control QByteArray mappedPropertyName(QObject *object) const; // Returns the section identifier that is mapped to the given control int mappedSection(QObject *object) const; // It returns the control that is associated with the ID section in the model QObject * mappedControlAt(const int §ion) const; // Returns data model that works QmlDataMapper QAbstractItemModel * model() const; // Sets the model that will work QmlDataMapper void setModel(QAbstractItemModel *model); public Q_SLOTS: /** * This method creates a mapping between the identifier and the control section of the data model. * For SQL model identifier section is but the role of the column in the data view model * These will be installed to control property, which depend on the properties of the control * This method is used to set and control section without the properties here. * And in this case, only the name "text", in which the data will be substituted */ void addMapping(QObject *object, const int §ion); /** * This method creates a mapping between the identifier and the control section of the data model. * For SQL model identifier section is but the role of the column in the data view model * The data will be supplied to a control element in the specified property */ void addMapping(QObject *object, const int §ion, const QByteArray &propertyName); // This method discards the data in the control void revert(); //This method sets the index at a given value void setCurrentIndex(int index); // This method sets the index of the first line void toFirst(); // This method sets the index of the last row void toLast(); // This method produces the increment of the current row index void toNext(); // This method produces a decrement of the current row index void toPrevious(); // Update the specified index data void updateData(int index); Q_SIGNALS: // Сигналы уведомления об изменении для свойств класса void currentIndexChanged(int index); void countChanged(); private: // Private class, which is hidden in this class API QmlDataMapperPrivate * const d; }; #endif // QMLDATAMAPPER_H
qmldatamapper.cpp
/**************************************************************************** ** EVILEG - Evgenii Legotckoi - 2015 ** Contact: http://www.evileg.com ** Contact: Evgenii Legotckoi (legotskoy@gmail.com) ** Contact: Евгений Легоцкой (legotskoy@gmail.com) ** All rights reserved. ****************************************************************************/ /**************************************************************************** ** ** Portions Copyright (C) 2012 Research In Motion Limited. ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Research In Motion Ltd. ) ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the examples of the BB10 Platform and is derived ** from a similar file that is part of the Qt Toolkit. ** ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Research In Motion, nor the name of Nokia ** Corporation and its Subsidiary(-ies), nor the names of its ** contributors may be used to endorse or promote products ** derived from this software without specific prior written ** permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ****************************************************************************/ #include "qmldatamapper.h" #include <QDataWidgetMapper> struct Mapping { QPointer <QObject> object; int section; QByteArray propertyName; }; /** * Private class that contains all the hidden methods and variables. * It uses a concept that allows you to change the internal API without affecting the external API * */ class QmlDataMapperPrivate { public: QmlDataMapperPrivate() : m_model(0), m_currentIndex(-1) { } // Auxiliary method of updating participants on the set matching the specified parameters void updateMapping(Mapping &mapping, QObject *object, const int §ion, const QByteArray &propertyName); // This method establishes the actual data comparison of the data model in the controls void update(); // The data model, which made the work QAbstractItemModel* m_model; // The list of comparisons that are installed QVector<Mapping> m_mappings; // Current index QmlDataMapper int m_currentIndex; }; void QmlDataMapperPrivate::updateMapping(Mapping &mapping, QObject *object, const int §ion, const QByteArray &propertyName) { mapping.object = object; mapping.section = section; // If the property name is not specified, it defaults to "text" property mapping.propertyName = (propertyName.isEmpty() ? "text" : propertyName); } void QmlDataMapperPrivate::update() { // List of checks before the data update if (!m_model) return; if (m_mappings.isEmpty()) return; if (m_currentIndex == -1) return; // Enumerating all the available comparisons foreach (const Mapping &mapping, m_mappings) { if (mapping.object) { // Updating controls, setting data on the properties of the role mapping.object->setProperty(mapping.propertyName, m_model->data(m_model->index(m_currentIndex,0), mapping.section)); } } } QmlDataMapper::QmlDataMapper(QObject *parent) : QObject(parent), d(new QmlDataMapperPrivate()) { } QmlDataMapper::~QmlDataMapper() { delete d; } void QmlDataMapper::addMapping(QObject *object, const int §ion) { // Adding a comparison with the default property addMapping(object, section, "text"); } void QmlDataMapper::addMapping(QObject *object, const int §ion, const QByteArray &propertyName) { // Verification that the added mapping already exists ... for (int i = 0; i < d->m_mappings.count(); ++i) { Mapping &mapping = d->m_mappings[i]; if (mapping.object == object) { // ... in the case of the existence of the comparison, produce update data section and control properties ... d->updateMapping(mapping, object, section, propertyName); // ... and updating content control d->update(); return; } } // Otherwise, add the new mapping Mapping mapping; d->updateMapping(mapping, object, section, propertyName); d->m_mappings.append(mapping); // ... and updating content control d->update(); } void QmlDataMapper::clearMapping() { // Cleaning Comparison list d->m_mappings.clear(); } int QmlDataMapper::currentIndex() const { return d->m_currentIndex; } int QmlDataMapper::count() const { if (!d->m_model) return 0; // Return the number of rows in the data view model return d->m_model->rowCount(); } QByteArray QmlDataMapper::mappedPropertyName(QObject *object) const { foreach(const Mapping &mapping, d->m_mappings) { if (mapping.object == object) return mapping.propertyName; } return QByteArray(); } int QmlDataMapper::mappedSection(QObject *object) const { foreach(const Mapping &mapping, d->m_mappings) { if (mapping.object == object) return mapping.section; } return 0; } QObject* QmlDataMapper::mappedControlAt(const int §ion) const { foreach(const Mapping &mapping, d->m_mappings) { if (mapping.section == section) return mapping.object; } return 0; } QAbstractItemModel* QmlDataMapper::model() const { return d->m_model; } void QmlDataMapper::removeMapping(QObject *object) { for (int i = 0; i < d->m_mappings.count(); ++i) { if (d->m_mappings[i].object == object) { d->m_mappings.remove(i); return; } } } void QmlDataMapper::setModel(QAbstractItemModel *model) { d->m_model = model; // Setting the initial contents of the index data representation model d->m_currentIndex = 0; // We produce content update control d->update(); emit countChanged(); } void QmlDataMapper::revert() { // We produce updated content control d->update(); } void QmlDataMapper::setCurrentIndex(int index) { //Check the availability of the data model if (!d->m_model) return; // We obtain the number of lines const int rowCount = d->m_model->rowCount(); // ... We ignore the incorrect value of the index if (index < 0 || index >= rowCount) return; d->m_currentIndex = index; d->update(); emit currentIndexChanged(d->m_currentIndex); } void QmlDataMapper::toFirst() { setCurrentIndex(0); } void QmlDataMapper::toLast() { if (!d->m_model) return; const int rowCount = d->m_model->rowCount(); setCurrentIndex(rowCount - 1); } void QmlDataMapper::toNext() { setCurrentIndex(d->m_currentIndex + 1); } void QmlDataMapper::toPrevious() { setCurrentIndex(d->m_currentIndex - 1); } void QmlDataMapper::updateData(int index) { // Set the desired index d->m_currentIndex = index; // and update the values in the controls d->update(); emit countChanged(); }
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.
#include <QApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include <qmldatamapper.h> #include <database.h> #include <model.h> int main(int argc, char *argv[]) { QApplication app(argc, argv); QQmlApplicationEngine engine; /// Initialize database DataBase database; /// We declare and initialize the data model representation Model *model = new Model(); /** Since we otnasledovalis from QSqlQueryModel, * the data sample, we need to perform SQL-query * in which we select all the needed fields from the desired table to us * */ model->setQuery("SELECT " TABLE_DATE ", " TABLE_TIME ", " TABLE_RANDOM ", " TABLE_MESSAGE " FROM " TABLE); // We declare and initialize the objectQmlDataMapper QmlDataMapper *mapper = new QmlDataMapper(); mapper->setModel(model); /** And it is already familiar from the lessons of the signals and slots in QML. * We put the resulting model in QML context to be able to refer to the model name "myModel" * */ engine.rootContext()->setContextProperty("myModel", model); /* А также даём доступ к Mapper в контексте QML * */ engine.rootContext()->setContextProperty("mapper", mapper); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
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.
import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.1 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") RowLayout { id: row anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.margins: 5 height: 35 Button { id: button text: qsTr("Open Mapper") width: 150 Layout.fillHeight: true onClicked: { dialog.show() } } } TableView { id: tableView anchors.top: row.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: 5 TableViewColumn { role: "date" // These roles are roles names match with a C ++ model title: "Date" } TableViewColumn { role: "time" // These roles are roles names match with a C ++ model title: "Time" } TableViewColumn { role: "random" // These roles are roles names match with a C ++ model title: "Random" } TableViewColumn { role: "message" // These roles are roles names match with a C ++ model title: "Message" } model: myModel rowDelegate: Rectangle { anchors.fill: parent color: styleData.selected ? 'skyblue' : (styleData.alternate ? 'whitesmoke' : 'white'); MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { tableView.selection.clear() tableView.selection.select(styleData.row) tableView.currentRow = styleData.row tableView.focus = true } onDoubleClicked: { /* doubleclick on the line at the open information * of the corresponding line dialog box * */ dialog.editEntry(styleData.row) } } } } DialogMapper { id: dialog } }
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 .
import QtQuick 2.0 import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.1 Dialog { title: qsTr("Dialog Mapper") height: 220 width: 480 // function, which opens with the data from the first line of the dialog box function show() { open() mapper.updateData(0) } /* The function, which opens with the line of the data dialog box, * on which was promoted doubleclick * */ function editEntry(row) { open() mapper.updateData(row) } contentItem: Rectangle { implicitHeight: 220 implicitWidth: 480 GridLayout { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.margins: 5 rowSpacing: 10 columnSpacing: 10 rows: 4 columns: 2 Text { text: qsTr("Дата") Layout.fillWidth: true } TextField { id: dateField // With this id we send object to Data Mapper Layout.preferredWidth: 200 } Text { text: qsTr("Время") Layout.fillWidth: true } TextField { id: timeField // With this id we send object to Data Mapper Layout.preferredWidth: 200 } Text { text: qsTr("Random number") Layout.fillWidth: true } TextField { id: randomField // With this id we send object to Data Mapper Layout.preferredWidth: 200 } Text { text: qsTr("Message") Layout.fillWidth: true } TextField { id: messageField // With this id we send object to Data Mapper Layout.preferredWidth: 200 } } Rectangle { color: "#eeeeee" height: 50 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right RowLayout { anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right anchors.margins: 5 spacing: 10 Button { id: buttonPrevious text: qsTr("Previous") Layout.preferredWidth: 80 onClicked: { mapper.toPrevious() } } Button { id: buttonNext text: qsTr("Next") Layout.preferredWidth: 80 onClicked: { mapper.toNext() } } Rectangle { Layout.fillWidth: true color: "#eeeeee" } Button { id: buttonOk text: qsTr("Ok") Layout.preferredWidth: 80 onClicked: close() } Button { id: buttonCancel text: qsTr("Cancel") Layout.preferredWidth: 80 onClicked: close() } } } } /* According to the result of the creation of the dialog box to add * a Data Mapper QML objects by their id, with an indication of the role of numbers * in the data model and object property, which will be substituted in the data. * * То есть: dateField - this id object TextField * 0x0100 + 1 - this DateRole of model, which equal Qt::UserRole + 1 * "text" - this property of TextField object * */ Component.onCompleted: { mapper.addMapping(dateField, (0x0100 + 1), "text") mapper.addMapping(timeField, (0x0100 + 2), "text") mapper.addMapping(randomField, (0x0100 + 3), "text") mapper.addMapping(messageField, (0x0100 + 4), "text") } }
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