QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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() || index.row() >= mRows.count() )
121 return QVariant();
122
123 switch ( role )
124 {
125 case Qt::DisplayRole:
126 {
127 const QList<QVariant> result = mRows.at( index.row() );
128 if ( index.column() < result.count() )
129 {
130 const QVariant value = result.at( index.column() );
131
132 if ( QgsVariantUtils::isNull( value ) )
133 {
135 }
136 else if ( value.type() == QVariant::ByteArray )
137 {
138 return tr( "Binary (%1)" ).arg( QgsFileUtils::representFileSize( value.value< QByteArray >().size() ) );
139 }
140 else
141 {
142 return value;
143 }
144 }
145 break;
146 }
147
148 case Qt::FontRole:
149 {
150 const QList<QVariant> result = mRows.at( index.row() );
151 if ( index.column() < result.count() )
152 {
153 const QVariant value = result.at( index.column() );
154
155 if ( QgsVariantUtils::isNull( value ) )
156 {
157 QFont f;
158 f.setItalic( true );
159 return f;
160 }
161 }
162 return QVariant();
163 }
164
165 case Qt::ForegroundRole:
166 {
167 const QList<QVariant> result = mRows.at( index.row() );
168 if ( index.column() < result.count() )
169 {
170 const QVariant value = result.at( index.column() );
171
172 if ( QgsVariantUtils::isNull( value ) )
173 {
174 return QColor( 128, 128, 128 );
175 }
176 }
177 return QVariant();
178 }
179
180 case Qt::ToolTipRole:
181 {
182 const QList<QVariant> result = mRows.at( index.row() );
183 if ( index.column() < result.count() )
184 {
185 const QVariant value = result.at( index.column() );
186 if ( QgsVariantUtils::isNull( value ) )
187 {
188 return QVariant();
189 }
190 else
191 {
192 return QgsExpression::formatPreviewString( value, true, 255 );
193 }
194 }
195 break;
196 }
197
198 default:
199 break;
200 }
201 return QVariant();
202}
203
204QVariant QgsQueryResultModel::headerData( int section, Qt::Orientation orientation, int role ) const
205{
206 if ( orientation == Qt::Orientation::Horizontal && section < mColumns.count() )
207 {
208 switch ( role )
209 {
210 case Qt::ItemDataRole::DisplayRole:
211 case Qt::ItemDataRole::ToolTipRole:
212 return mColumns.at( section );
213
214 default:
215 break;
216 }
217 }
218 return QAbstractTableModel::headerData( section, orientation, role );
219}
220
222
223const int QgsQueryResultFetcher::ROWS_BATCH_COUNT = 200;
224
225void QgsQueryResultFetcher::fetchRows( long long maxRows )
226{
227 long long rowCount { 0 };
228 QList<QList<QVariant>> newRows;
229 newRows.reserve( ROWS_BATCH_COUNT );
230 while ( mStopFetching == 0 && mQueryResult->hasNextRow() && ( maxRows < 0 || rowCount < maxRows ) )
231 {
232 newRows.append( mQueryResult->nextRow() );
233 ++rowCount;
234 if ( rowCount % ROWS_BATCH_COUNT == 0 && mStopFetching == 0 )
235 {
236 emit rowsReady( newRows );
237 newRows.clear();
238 }
239 }
240
241 if ( rowCount % ROWS_BATCH_COUNT && mStopFetching == 0 )
242 {
243 emit rowsReady( newRows );
244 }
245
246 emit fetchingComplete();
247}
248
249void QgsQueryResultFetcher::stopFetching()
250{
251 mStopFetching = 1;
252}
253
254
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().