Как проще реализовать аналог QSqlQueryModel, получающий данные не из базы, а из XML файла?
Дано:
на форме есть таблица tableViewDocuments типа TableView, для которой в качестве DataSet используется
QSqlQueryModel* pMdl = p_DB_PostgressProcessor->CreateOutPutDocList(Id);
ui.tableViewDocuments->setModel( pMdl );
где функция CreateOutPutDocList() имеет такую реализацию
QSqlQueryModel* DB_PostgressProcessor::CreateOutPutDocList(const QString& id)
{
QString queryStr = "SELECT doc_number, name, date, description FROM bd_fp.document WHERE
id=:id ORDER BY name;";
QSqlQueryModel* pMdl = new QSqlQueryModel();
QSqlQuery query(m_DB);
query.prepare(queryStr);
query.bindValue(":id", id);
if (!query.exec())
{
QSqlError err = query.lastError();
mLogger.AddMessage(err.text());
return pMdl;
}
pMdl->setQuery(query);
return pMdl;
}
Все прекрасно работает. Однако, теперь возникла необходимость создать автономное приложение без базы данных, в котором в роли источника данных используется ХМL-файл.
Вопрос - как проще это сделать с минимальными переделками ?
Прежде чем писать с нуля свой класс аналог QSqlQueryModel, производный от QAbstractItemModel, берущий данные из XML-файла и поддержвающий всю функциональность QSqlQueryModel, хотелось бы послушать советы умных людей - возможно, что это все уже сделано, и как они решали аналогичные проблемы.
Редактирование данных в таблице не требуется, она используется только для просмотра. Ограничений на структуру XML-файла нет, его можно сделать таким, как надо.
Ответы (1 шт):
Поскольку готового решения не нашлось, то сделал собственную реализацию аналога QSqlQueryModel, которая берет данные не из базы, а из XML-файла. Я назвал ее BaseTableModelByXML. Сразу оговорюсь, поскольку для меня важным было сделать все очень быстро, то в качестве базового класса я взял не QSqlQueryModel, а QAbstractTableModel, заменив всюду приведение в исходной программной системе от QSqlQueryModel на BaseTableModelByXML, хотя без этого можно было обойтись в случаи наследования QSqlQueryModel. Ниже моя реализация, возможно она кому-нибудь поможет:
#pragma once
#include <QAbstractTableModel>
#include <QSqlRecord>
#include <QList>
class BaseTableModelByXML :
public QAbstractTableModel
{
public:
Q_OBJECT
public:
BaseTableModelByXML();
~BaseTableModelByXML();
//Абстрактные функции базового класса, которые должны быть реализованы
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant
&value, int role = Qt::EditRole);
QSqlRecord record(int row) const;
//Этих функций нет QAbstractTableModel - она добавлена для упрощения формирования списка записей типа QSqlRecord при их формировании из XML
QList <QSqlRecord> getRecords();
void addRecord(QSqlRecord rec);
protected:
//Заголовки колонок
QList <QString> lstColumnsHdr;
//Список записей. Дабы ничего не переделывать в исходной системе они имеют тип QSqlRecord
QList <QSqlRecord> lstRecords;
};
Реализация класса:
#include "BaseTableModelByXML.h"
#include <QSqlField>
BaseTableModelByXML::BaseTableModelByXML() {}
BaseTableModelByXML::~BaseTableModelByXML()
{
lstRecords.clear();
}
int BaseTableModelByXML::rowCount(const QModelIndex &parent) const
{
return lstRecords.count();
}
int BaseTableModelByXML::columnCount(const QModelIndex &parent) const
{
if (lstRecords.count() == 0) return 0;
return lstRecords[0].count();
}
QVariant BaseTableModelByXML::data(const QModelIndex &index, int role) const
{
QVariant value;
if (!index.isValid())
return value;
QSqlRecord rec = lstRecords[index.row()];
QString fName = rec.fieldName(index.column());
QSqlField fld = rec.field(fName);
switch ( role) //switch сделан "на вырост" - ролей много, в частности в этом месте можно менять цвет ячейки в зависимости от ее содержимого
{
case Qt::DisplayRole:
return fld.value();
default:
return value;
}
return value;
}
QVariant BaseTableModelByXML::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
return lstColumnsHdr[section]; //Отображаем имя колонки
else
return QString("%1").arg(section+1); //Отображаемый номер строки
}
bool BaseTableModelByXML::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
lstColumnsHdr.append(value.toString()); //Это упрощенный вариант, адаптированный к исходному коду, где происходит вызов этой функции.
return true;
}
QSqlRecord BaseTableModelByXML::record(int row) const
{
if (lstRecords.count() == 0 || row >= lstRecords.count() || row < 0)
{
QSqlRecord rtc;
return rtc;
}
return lstRecords[row];
}
void BaseTableModelByXML::addRecord(QSqlRecord rec)
{
lstRecords.append(rec);
}
QList <QSqlRecord> BaseTableModelByXML::getRecords()
{
return lstRecords;
}
Ну и в завершении, пример заполнения экземпляра класса BaseTableModelByXML из XML-файла. В моем случае за работу с разными источниками данных отвечает классы, производные от некого класса DataProcessor и, в случаи XML, - это класс XML_Processor. Опуская детали, в него загружается исходный XML-файл, который заносится в элемент doc и затем XML преобразуется в различные экземпляры классов BaseTableModelByXML. Ниже упрощенный пример:
BaseTableModelByXML* XML_Processor::createDocListModel( int *pLastColumn)
{
if (!FileIsLoaded) return 0;
BaseTableModelByXML *pMdl = new BaseTableModelByXML();
int pos = 0;
pMdl->setHeaderData(pos++, Qt::Horizontal, "Id");
pMdl->setHeaderData(pos++, Qt::Horizontal, RussianForQT5("Название"));
pMdl->setHeaderData(pos++, Qt::Horizontal, RussianForQT5("Комментарий"));
*pLastColumn = pos - 1;
QDomNodeList lst = doc.elementsByTagName("DocSection"); //doc - это объект типа QDomDocument, куда предварительно был загружен XML-файл
for (int i = 0; i < lst.count(); ++i)
{
QDomElement element = lst.at(i).toElement();
if (!element.isNull())
{
QString id = element.attribute("id", "");
QString name = element.attribute("name", "");
QString description = element.attribute("description", "");
QSqlField f1(QSqlField("Id", QVariant::String));
f1.setValue(id);
QSqlField f2(QSqlField("name", QVariant::String));
f2.setValue(name);
QSqlField f3(QSqlField("description", QVariant::String));
f3.setValue(description);
QSqlRecord rec;
rec.append(f1);
rec.append(f2);
rec.append(f3);
pMdl->addRecord(rec);
}
}
return pMdl;
}