QGIS API Documentation 3.43.0-Master (2a27c31701b)
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
7 email : elpaso@itopen.it
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#include "moc_qgsqueryresultmodel.cpp"
18#include "qgsexpression.h"
19#include "qgsapplication.h"
20#include "qgsfileutils.h"
21
22#include <QFont>
23
24const int QgsQueryResultModel::FETCH_MORE_ROWS_COUNT = 400;
25
26QgsQueryResultModel::QgsQueryResultModel( const QgsAbstractDatabaseProviderConnection::QueryResult &queryResult, QObject *parent )
27 : QAbstractTableModel( parent )
28 , mQueryResult( queryResult )
29 , mColumns( queryResult.columns() )
30{
31 qRegisterMetaType< QList<QList<QVariant>>>( "QList<QList<QVariant>>" );
32 mWorker = std::make_unique<QgsQueryResultFetcher>( &mQueryResult );
33 mWorker->moveToThread( &mWorkerThread );
34 // Forward signals to the model
35 connect( mWorker.get(), &QgsQueryResultFetcher::rowsReady, this, &QgsQueryResultModel::rowsReady );
36 connect( mWorker.get(), &QgsQueryResultFetcher::fetchingComplete, this, &QgsQueryResultModel::fetchingComplete );
37 connect( this, &QgsQueryResultModel::fetchMoreRows, mWorker.get(), &QgsQueryResultFetcher::fetchRows );
38 mWorkerThread.start();
39 if ( mQueryResult.rowCount() > 0 )
40 {
41 mRows.reserve( mQueryResult.rowCount() );
42 }
43}
44
45void QgsQueryResultModel::rowsReady( const QList<QList<QVariant>> &rows )
46{
47 beginInsertRows( QModelIndex(), mRows.count( ), mRows.count( ) + rows.count() - 1 );
48 mRows.append( rows );
49 endInsertRows();
50}
51
52
53bool QgsQueryResultModel::canFetchMore( const QModelIndex &parent ) const
54{
55 if ( parent.isValid() )
56 return false;
57 return mQueryResult.rowCount() < 0 || mRows.length() < mQueryResult.rowCount();
58}
59
60
61void QgsQueryResultModel::fetchMore( const QModelIndex &parent )
62{
63 if ( ! parent.isValid() )
64 {
65 emit fetchingStarted();
66 emit fetchMoreRows( FETCH_MORE_ROWS_COUNT );
67 }
68}
69
70void QgsQueryResultModel::cancel()
71{
72 if ( mWorker )
73 {
74 mWorker->stopFetching();
75 }
76}
77
78QgsAbstractDatabaseProviderConnection::QueryResult QgsQueryResultModel::queryResult() const
79{
80 return mQueryResult;
81}
82
83QStringList QgsQueryResultModel::columns() const
84{
85 return mColumns;
86}
87
88QgsQueryResultModel::~QgsQueryResultModel()
89{
90 if ( mWorker )
91 {
92 mWorker->stopFetching();
93 mWorkerThread.quit();
94 mWorkerThread.wait();
95 }
96 else
97 {
98 emit fetchingComplete();
99 }
100}
101
102int QgsQueryResultModel::rowCount( const QModelIndex &parent ) const
103{
104 if ( parent.isValid() )
105 return 0;
106 return mRows.count();
107}
108
109int QgsQueryResultModel::columnCount( const QModelIndex &parent ) const
110{
111 if ( parent.isValid() )
112 return 0;
113 return mColumns.count();
114}
115
116QVariant QgsQueryResultModel::data( const QModelIndex &index, int role ) const
117{
118 if ( !index.isValid() || index.row() < 0 || index.column() >= mColumns.count() ||
119 index.row() >= mRows.count( ) )
120 return QVariant();
121
122 switch ( role )
123 {
124 case Qt::DisplayRole:
125 {
126 const QList<QVariant> result = mRows.at( index.row() );
127 if ( index.column() < result.count( ) )
128 {
129 const QVariant value = result.at( index.column() );
130
131 if ( QgsVariantUtils::isNull( value ) )
132 {
134 }
135 else if ( value.type() == QVariant::ByteArray )
136 {
137 return tr( "Binary (%1)" ).arg( QgsFileUtils::representFileSize( value.value< QByteArray >().size() ) );
138 }
139 else
140 {
141 return value;
142 }
143 }
144 break;
145 }
146
147 case Qt::FontRole:
148 {
149 const QList<QVariant> result = mRows.at( index.row() );
150 if ( index.column() < result.count( ) )
151 {
152 const QVariant value = result.at( index.column() );
153
154 if ( QgsVariantUtils::isNull( value ) )
155 {
156 QFont f;
157 f.setItalic( true );
158 return f;
159 }
160 }
161 return QVariant();
162 }
163
164 case Qt::ForegroundRole:
165 {
166 const QList<QVariant> result = mRows.at( index.row() );
167 if ( index.column() < result.count( ) )
168 {
169 const QVariant value = result.at( index.column() );
170
171 if ( QgsVariantUtils::isNull( value ) )
172 {
173 return QColor( 128, 128, 128 );
174 }
175 }
176 return QVariant();
177 }
178
179 case Qt::ToolTipRole:
180 {
181 const QList<QVariant> result = mRows.at( index.row() );
182 if ( index.column() < result.count( ) )
183 {
184 const QVariant value = result.at( index.column() );
185 if ( QgsVariantUtils::isNull( value ) )
186 {
187 return QVariant();
188 }
189 else
190 {
191 return QgsExpression::formatPreviewString( value, true, 255 );
192 }
193 }
194 break;
195 }
196
197 default:
198 break;
199 }
200 return QVariant();
201}
202
203QVariant QgsQueryResultModel::headerData( int section, Qt::Orientation orientation, int role ) const
204{
205 if ( orientation == Qt::Orientation::Horizontal && section < mColumns.count() )
206 {
207 switch ( role )
208 {
209 case Qt::ItemDataRole::DisplayRole:
210 case Qt::ItemDataRole::ToolTipRole:
211 return mColumns.at( section );
212
213 default:
214 break;
215 }
216 }
217 return QAbstractTableModel::headerData( section, orientation, role );
218}
219
221
222const int QgsQueryResultFetcher::ROWS_BATCH_COUNT = 200;
223
224void QgsQueryResultFetcher::fetchRows( long long maxRows )
225{
226 long long rowCount { 0 };
227 QList<QList<QVariant>> newRows;
228 newRows.reserve( ROWS_BATCH_COUNT );
229 while ( mStopFetching == 0 && mQueryResult->hasNextRow() && ( maxRows < 0 || rowCount < maxRows ) )
230 {
231 newRows.append( mQueryResult->nextRow() );
232 ++rowCount;
233 if ( rowCount % ROWS_BATCH_COUNT == 0 && mStopFetching == 0 )
234 {
235 emit rowsReady( newRows );
236 newRows.clear();
237 }
238 }
239
240 if ( rowCount % ROWS_BATCH_COUNT && mStopFetching == 0 )
241 {
242 emit rowsReady( newRows );
243 }
244
245 emit fetchingComplete();
246}
247
248void QgsQueryResultFetcher::stopFetching()
249{
250 mStopFetching = 1;
251}
252
253
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()