Skip to content

Commit 957e92c

Browse files
committed
Add async model for virtual layers
1 parent bf7df6d commit 957e92c

17 files changed

+299
-51
lines changed

python/core/core_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
%Include qgsvectorlayerundocommand.sip
127127
%Include qgsvectorlayerundopassthroughcommand.sip
128128
%Include qgsvectorlayerutils.sip
129+
%Include qgsvirtuallayertask.sip
129130
%Include qgsvirtuallayerdefinition.sip
130131
%Include qgsvirtuallayerdefinitionutils.sip
131132
%Include qgsmapthemecollection.sip

python/core/qgsvectordataprovider.sip.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ of feature and attribute information from a spatial datasource.
5050
FastTruncate,
5151
ReadLayerMetadata,
5252
WriteLayerMetadata,
53+
CancelSupport,
5354
};
5455

5556
typedef QFlags<QgsVectorDataProvider::Capability> Capabilities;
@@ -231,6 +232,8 @@ Providers with the FastTruncate capability will use an optimised method to trunc
231232
.. seealso:: :py:func:`deleteFeatures`
232233
%End
233234

235+
virtual bool cancel();
236+
234237
virtual bool addAttributes( const QList<QgsField> &attributes );
235238
%Docstring
236239
Adds new ``attributes`` to the provider. Returns true in case of success and false in case of failure.

python/core/qgsvirtuallayerdefinition.sip.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ Get the name of the field with unique identifiers
150150
Set the name of the field with unique identifiers
151151
%End
152152

153+
void setPostpone( bool postpone );
154+
bool postpone() const;
155+
153156
QString geometryField() const;
154157
%Docstring
155158
Get the name of the geometry field. Empty if no geometry field

python/core/qgsvirtuallayertask.sip

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/qgsvirtuallayertask.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
class QgsVirtualLayerTask : QgsTask
13+
{
14+
%Docstring
15+
16+
Initializes a virtual layer in a separated task.
17+
18+
.. versionadded:: 3.0
19+
%End
20+
21+
%TypeHeaderCode
22+
#include "qgsvirtuallayertask.h"
23+
%End
24+
public:
25+
26+
QgsVirtualLayerTask( const QgsVirtualLayerDefinition &definition );
27+
28+
QgsVectorLayer *layer();
29+
30+
QgsVirtualLayerDefinition definition() const;
31+
32+
virtual bool run();
33+
34+
35+
virtual void cancel();
36+
37+
38+
};
39+
40+
/************************************************************************
41+
* This file has been generated automatically from *
42+
* *
43+
* src/core/qgsvirtuallayertask.h *
44+
* *
45+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
46+
************************************************************************/

python/plugins/db_manager/db_plugins/vlayers/data_model.py

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
***************************************************************************/
2020
"""
2121

22-
from ..data_model import TableDataModel, BaseTableModel
22+
from ..data_model import TableDataModel, BaseTableModel, SqlResultModelAsync
2323

2424
from .connector import VLayerRegistry, getQueryGeometryName
2525
from .plugin import LVectorTable
26-
from ..plugin import DbError
26+
from ..plugin import DbError, BaseError
2727

2828
from qgis.PyQt.QtCore import QTime, QTemporaryFile
29-
from qgis.core import QgsVectorLayer, QgsWkbTypes, QgsVirtualLayerDefinition
29+
from qgis.core import QgsVectorLayer, QgsWkbTypes, QgsVirtualLayerDefinition, QgsVirtualLayerTask, QgsTask
3030

3131

3232
class LTableDataModel(TableDataModel):
@@ -63,40 +63,88 @@ def rowCount(self, index=None):
6363
return 0
6464

6565

66-
class LSqlResultModel(BaseTableModel):
67-
# BaseTableModel
66+
class LSqlResultModelTask(QgsTask):
67+
68+
def __init__(self, subtask, db):
69+
QgsTask.__init__(self)
70+
self.subtask = subtask
71+
self.db = db
72+
self.model = None
73+
74+
def run(self):
75+
try:
76+
path = self.subtask.definition().filePath()
77+
sql = self.subtask.definition().query()
78+
self.model = LSqlResultModel(self.db, sql, None, self.subtask.layer(), path)
79+
except Exception as e:
80+
self.error = BaseError(str(e))
81+
return False
82+
return True
83+
84+
def cancelQuery(self):
85+
self.subtask.cancel()
86+
self.cancel()
87+
88+
89+
class LSqlResultModelAsync(SqlResultModelAsync):
6890

6991
def __init__(self, db, sql, parent=None):
70-
# create a virtual layer with non-geometry results
71-
t = QTime()
72-
t.start()
92+
SqlResultModelAsync.__init__(self, db, sql, parent)
7393

7494
tf = QTemporaryFile()
7595
tf.open()
76-
tmp = tf.fileName()
96+
path = tf.fileName()
7797
tf.close()
7898

7999
df = QgsVirtualLayerDefinition()
80-
df.setFilePath(tmp)
81-
df.setQuery(sql)
82-
p = QgsVectorLayer(df.toString(), "vv", "virtual")
83-
self._secs = t.elapsed() / 1000.0
84-
85-
if not p.isValid():
86-
data = []
87-
header = []
88-
raise DbError(p.dataProvider().error().summary(), sql)
100+
df.setFilePath(path)
101+
df.setQuery(self.sql)
102+
103+
self.subtask = QgsVirtualLayerTask(df)
104+
self.task = LSqlResultModelTask(self.subtask, db)
105+
self.task.addSubTask(self.subtask, [], QgsTask.ParentDependsOnSubTask)
106+
self.task.taskCompleted.connect(self.modelDone)
107+
self.task.taskTerminated.connect(self.modelDone)
108+
109+
def modelDone(self):
110+
self.status = self.task.status
111+
self.model = self.task.model
112+
self.done.emit()
113+
114+
115+
class LSqlResultModel(BaseTableModel):
116+
117+
def __init__(self, db, sql, parent=None, layer=None, path=None):
118+
t = QTime()
119+
t.start()
120+
121+
if not layer:
122+
tf = QTemporaryFile()
123+
tf.open()
124+
path = tf.fileName()
125+
tf.close()
126+
127+
df = QgsVirtualLayerDefinition()
128+
df.setFilePath(path)
129+
df.setQuery(sql)
130+
layer = QgsVectorLayer(df.toString(), "vv", "virtual")
131+
self._secs = t.elapsed() / 1000.0
132+
133+
data = []
134+
header = []
135+
136+
if not layer.isValid():
137+
raise DbError(layer.dataProvider().error().summary(), sql)
89138
else:
90-
header = [f.name() for f in p.fields()]
139+
header = [f.name() for f in layer.fields()]
91140
has_geometry = False
92-
if p.geometryType() != QgsWkbTypes.NullGeometry:
93-
gn = getQueryGeometryName(tmp)
141+
if layer.geometryType() != QgsWkbTypes.NullGeometry:
142+
gn = getQueryGeometryName(path)
94143
if gn:
95144
has_geometry = True
96145
header += [gn]
97146

98-
data = []
99-
for f in p.getFeatures():
147+
for f in layer.getFeatures():
100148
a = f.attributes()
101149
if has_geometry:
102150
if f.hasGeometry():

python/plugins/db_manager/db_plugins/vlayers/plugin.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ def sqlResultModel(self, sql, parent):
9898
from .data_model import LSqlResultModel
9999
return LSqlResultModel(self, sql, parent)
100100

101+
def sqlResultModelAsync(self, sql, parent):
102+
from .data_model import LSqlResultModelAsync
103+
return LSqlResultModelAsync(self, sql, parent)
104+
101105
def toSqlLayer(self, sql, geomCol, uniqueCol, layerName="QueryLayer", layerType=None, avoidSelectById=False, _filter=""):
102106
df = QgsVirtualLayerDefinition()
103107
df.setQuery(sql)

python/plugins/db_manager/dlg_cancel_task_query.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,5 @@ def show(self):
6161
super(QDialog, self).show()
6262

6363
def hide(self):
64-
self.cancelStatus = False
6564
self.mGif.stop()
6665
super(QDialog, self).hide()

python/plugins/db_manager/dlg_sql_window.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -187,24 +187,25 @@ def executeSqlCanceled(self):
187187
def executeSqlCompleted(self):
188188
self.dlg_cancel_task.hide()
189189

190-
if self.modelAsync.task.status() == QgsTask.Complete:
191-
model = self.modelAsync.model
192-
cols = []
193-
quotedCols = []
194-
195-
self.viewResult.setModel(model)
196-
self.lblResult.setText(self.tr("{0} rows, {1:.1f} seconds").format(model.affectedRows(), model.secs()))
197-
cols = self.viewResult.model().columnNames()
198-
for col in cols:
199-
quotedCols.append(self.db.connector.quoteId(col))
190+
with OverrideCursor(Qt.WaitCursor):
191+
if self.modelAsync.task.status() == QgsTask.Complete:
192+
model = self.modelAsync.model
193+
cols = []
194+
quotedCols = []
195+
196+
self.viewResult.setModel(model)
197+
self.lblResult.setText(self.tr("{0} rows, {1:.1f} seconds").format(model.affectedRows(), model.secs()))
198+
cols = self.viewResult.model().columnNames()
199+
for col in cols:
200+
quotedCols.append(self.db.connector.quoteId(col))
200201

201-
self.setColumnCombos(cols, quotedCols)
202-
self.update()
203-
elif not self.dlg_cancel_task.cancelStatus:
204-
DlgDbError.showError(self.modelAsync.error, self)
205-
self.uniqueModel.clear()
206-
self.geomCombo.clear()
207-
pass
202+
self.setColumnCombos(cols, quotedCols)
203+
self.update()
204+
elif not self.dlg_cancel_task.cancelStatus:
205+
DlgDbError.showError(self.modelAsync.error, self)
206+
self.uniqueModel.clear()
207+
self.geomCombo.clear()
208+
pass
208209

209210
def executeSql(self):
210211

src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ SET(QGIS_CORE_SRCS
297297
qgsvectorfilewriter.cpp
298298
qgsvectorfilewritertask.cpp
299299
qgsvectorlayer.cpp
300+
qgsvirtuallayertask.cpp
300301
qgsvectorlayerfeaturecounter.cpp
301302
qgsvectorlayercache.cpp
302303
qgsvectorlayerdiagramprovider.cpp
@@ -635,6 +636,7 @@ SET(QGIS_CORE_MOC_HDRS
635636
qgsvectorlayereditbuffer.h
636637
qgsvectorlayereditpassthrough.h
637638
qgsvectorlayer.h
639+
qgsvirtuallayertask.h
638640
qgsvectorlayerexporter.h
639641
qgsvectorlayerfeaturecounter.h
640642
qgsvectorlayerjoinbuffer.h

src/core/qgsvectordataprovider.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,11 @@ QTextCodec *QgsVectorDataProvider::textEncoding() const
804804
return mEncoding;
805805
}
806806

807+
bool QgsVectorDataProvider::cancel()
808+
{
809+
return false;
810+
}
811+
807812
QStringList QgsVectorDataProvider::sEncodings;
808813

809814
QList<QgsRelation> QgsVectorDataProvider::discoverRelations( const QgsVectorLayer *, const QList<QgsVectorLayer *> & ) const

src/core/qgsvectordataprovider.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
9090
FastTruncate = 1 << 20, //!< Supports fast truncation of the layer (removing all features). Since QGIS 3.0
9191
ReadLayerMetadata = 1 << 21, //!< Provider can read layer metadata from data store. Since QGIS 3.0. See QgsDataProvider::layerMetadata()
9292
WriteLayerMetadata = 1 << 22, //!< Provider can write layer metadata to the data store. Since QGIS 3.0. See QgsDataProvider::writeLayerMetadata()
93+
CancelSupport = 1 << 23, //!< Supports interruption of pending queries from a separated thread. Since QGIS 3.0
9394
};
9495

9596
Q_DECLARE_FLAGS( Capabilities, Capability )
@@ -248,6 +249,8 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
248249
*/
249250
virtual bool truncate();
250251

252+
virtual bool cancel();
253+
251254
/**
252255
* Adds new \a attributes to the provider. Returns true in case of success and false in case of failure.
253256
* If attributes are added using this method then QgsVectorLayer::updateFields() must be called

src/core/qgsvirtuallayerdefinition.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinition::fromUrl( const QUrl &url )
157157
}
158158
}
159159
}
160+
else if ( key == QLatin1String( "postpone" ) )
161+
{
162+
def.setPostpone( true );
163+
}
160164
}
161165
def.setFields( fields );
162166

@@ -209,6 +213,11 @@ QUrl QgsVirtualLayerDefinition::toUrl() const
209213
url.addQueryItem( QStringLiteral( "field" ), f.name() + ":text" );
210214
}
211215

216+
if ( postpone() )
217+
{
218+
url.addQueryItem( QStringLiteral( "postpone" ), QLatin1String( "" ) );
219+
}
220+
212221
return url;
213222
}
214223

src/core/qgsvirtuallayerdefinition.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ class CORE_EXPORT QgsVirtualLayerDefinition
131131
//! Set the name of the field with unique identifiers
132132
void setUid( const QString &uid ) { mUid = uid; }
133133

134+
void setPostpone( bool postpone ) { mPostpone = postpone; }
135+
bool postpone() const { return mPostpone; }
136+
134137
//! Get the name of the geometry field. Empty if no geometry field
135138
QString geometryField() const { return mGeometryField; }
136139
//! Set the name of the geometry field
@@ -174,6 +177,7 @@ class CORE_EXPORT QgsVirtualLayerDefinition
174177
QString mGeometryField;
175178
QString mFilePath;
176179
QgsFields mFields;
180+
bool mPostpone = false;
177181
QgsWkbTypes::Type mGeometryWkbType = QgsWkbTypes::Unknown;
178182
long mGeometrySrid = 0;
179183
};

0 commit comments

Comments
 (0)