QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsqueryresultmodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsqueryresultmodel.cpp - QgsQueryResultModel
3
4 ---------------------
5 begin : 24.12.2020
6 copyright : (C) 2020 by Alessandro Pasotti
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16#include "qgsqueryresultmodel.h"
17
18#include "qgsapplication.h"
19#include "qgsexpression.h"
20#include "qgsfileutils.h"
21
22#include <QFont>
23
24#include "moc_qgsqueryresultmodel.cpp"
25
26const int QgsQueryResultModel::FETCH_MORE_ROWS_COUNT = 400;
27
28QgsQueryResultModel::QgsQueryResultModel( const QgsAbstractDatabaseProviderConnection::QueryResult &queryResult, QObject *parent )
29 : QAbstractTableModel( parent )
30 , mQueryResult( queryResult )
31 , mColumns( queryResult.columns() )
32{
33 qRegisterMetaType< QList<QList<QVariant>>>( "QList<QList<QVariant>>" );
34 mWorker = std::make_unique<QgsQueryResultFetcher>( &mQueryResult );
35 mWorker->moveToThread( &mWorkerThread );
36 // Forward signals to the model
37 connect( mWorker.get(), &QgsQueryResultFetcher::rowsReady, this, &QgsQueryResultModel::rowsReady );
38 connect( mWorker.get(), &QgsQueryResultFetcher::fetchingComplete, this, &QgsQueryResultModel::fetchingComplete );
39 connect( this, &QgsQueryResultModel::fetchMoreRows, mWorker.get(), &QgsQueryResultFetcher::fetchRows );
40 mWorkerThread.start();
41 if ( mQueryResult.rowCount() > 0 )
42 {
43 mRows.reserve( mQueryResult.rowCount() );
44 }
45}
46
47void QgsQueryResultModel::rowsReady( const QList<QList<QVariant>> &rows )
48{
49 beginInsertRows( QModelIndex(), mRows.count( ), mRows.count( ) + rows.count() - 1 );
50 mRows.append( rows );
51 endInsertRows();
52}
53
54
55bool QgsQueryResultModel::canFetchMore( const QModelIndex &parent ) const
56{
57 if ( parent.isValid() )
58 return false;
59 return mQueryResult.rowCount() < 0 || mRows.length() < mQueryResult.rowCount();
60}
61
62
63void QgsQueryResultModel::fetchMore( const QModelIndex &parent )
64{
65 if ( ! parent.isValid() )
66 {
67 emit fetchingStarted();
68 emit fetchMoreRows( FETCH_MORE_ROWS_COUNT );
69 }
70}
71
72void QgsQueryResultModel::cancel()
73{
74 if ( mWorker )
75 {
76 mWorker->stopFetching();
77 }
78}
79
80QgsAbstractDatabaseProviderConnection::QueryResult QgsQueryResultModel::queryResult() const
81{
82 return mQueryResult;
83}
84
85QStringList QgsQueryResultModel::columns() const
86{
87 return mColumns;
88}
89
90QgsQueryResultModel::~QgsQueryResultModel()
91{
92 if ( mWorker )
93 {
94 mWorker->stopFetching();
95 mWorkerThread.quit();
96 mWorkerThread.wait();
97 }
98 else
99 {
100 emit fetchingComplete();
101 }
102}
103
104int QgsQueryResultModel::rowCount( const QModelIndex &parent ) const
105{
106 if ( parent.isValid() )
107 return 0;
108 return mRows.count();
109}
110
111int QgsQueryResultModel::columnCount( const QModelIndex &parent ) const
112{
113 if ( parent.isValid() )
114 return 0;
115 return mColumns.count();
116}
117
118QVariant QgsQueryResultModel::data( const QModelIndex &index, int role ) const
119{
120 if ( !index.isValid() || index.row() < 0 || index.column() >= mColumns.count() ||
121 index.row() >= mRows.count( ) )
122 return QVariant();
123
124 switch ( role )
125 {
126 case Qt::DisplayRole:
127 {
128 const QList<QVariant> result = mRows.at( index.row() );
129 if ( index.column() < result.count( ) )
130 {
131 const QVariant value = result.at( index.column() );
132
133 if ( QgsVariantUtils::isNull( value ) )
134 {
136 }
137 else if ( value.type() == QVariant::ByteArray )
138 {
139 return tr( "Binary (%1)" ).arg( QgsFileUtils::representFileSize( value.value< QByteArray >().size() ) );
140 }
141 else
142 {
143 return value;
144 }
145 }
146 break;
147 }
148
149 case Qt::FontRole:
150 {
151 const QList<QVariant> result = mRows.at( index.row() );
152 if ( index.column() < result.count( ) )
153 {
154 const QVariant value = result.at( index.column() );
155
156 if ( QgsVariantUtils::isNull( value ) )
157 {
158 QFont f;
159 f.setItalic( true );
160 return f;
161 }
162 }
163 return QVariant();
164 }
165
166 case Qt::ForegroundRole:
167 {
168 const QList<QVariant> result = mRows.at( index.row() );
169 if ( index.column() < result.count( ) )
170 {
171 const QVariant value = result.at( index.column() );
172
173 if ( QgsVariantUtils::isNull( value ) )
174 {
175 return QColor( 128, 128, 128 );
176 }
177 }
178 return QVariant();
179 }
180
181 case Qt::ToolTipRole:
182 {
183 const QList<QVariant> result = mRows.at( index.row() );
184 if ( index.column() < result.count( ) )
185 {
186 const QVariant value = result.at( index.column() );
187 if ( QgsVariantUtils::isNull( value ) )
188 {
189 return QVariant();
190 }
191 else
192 {
193 return QgsExpression::formatPreviewString( value, true, 255 );
194 }
195 }
196 break;
197 }
198
199 default:
200 break;
201 }
202 return QVariant();
203}
204
205QVariant QgsQueryResultModel::headerData( int section, Qt::Orientation orientation, int role ) const
206{
207 if ( orientation == Qt::Orientation::Horizontal && section < mColumns.count() )
208 {
209 switch ( role )
210 {
211 case Qt::ItemDataRole::DisplayRole:
212 case Qt::ItemDataRole::ToolTipRole:
213 return mColumns.at( section );
214
215 default:
216 break;
217 }
218 }
219 return QAbstractTableModel::headerData( section, orientation, role );
220}
221
223
224const int QgsQueryResultFetcher::ROWS_BATCH_COUNT = 200;
225
226void QgsQueryResultFetcher::fetchRows( long long maxRows )
227{
228 long long rowCount { 0 };
229 QList<QList<QVariant>> newRows;
230 newRows.reserve( ROWS_BATCH_COUNT );
231 while ( mStopFetching == 0 && mQueryResult->hasNextRow() && ( maxRows < 0 || rowCount < maxRows ) )
232 {
233 newRows.append( mQueryResult->nextRow() );
234 ++rowCount;
235 if ( rowCount % ROWS_BATCH_COUNT == 0 && mStopFetching == 0 )
236 {
237 emit rowsReady( newRows );
238 newRows.clear();
239 }
240 }
241
242 if ( rowCount % ROWS_BATCH_COUNT && mStopFetching == 0 )
243 {
244 emit rowsReady( newRows );
245 }
246
247 emit fetchingComplete();
248}
249
250void QgsQueryResultFetcher::stopFetching()
251{
252 mStopFetching = 1;
253}
254
255
static QString nullRepresentation()
Returns the string used to represent the value NULL throughout QGIS.
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true, int maximumPreviewLength=60)
Formats an expression result for friendly display to the user.
static QString representFileSize(qint64 bytes)
Returns the human size from bytes.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
The QueryResult class represents the result of a query executed by execSql().