Skip to content

Commit

Permalink
Add async model for virtual layers
Browse files Browse the repository at this point in the history
  • Loading branch information
pblottiere committed Mar 2, 2018
1 parent bf7df6d commit 957e92c
Show file tree
Hide file tree
Showing 17 changed files with 299 additions and 51 deletions.
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -126,6 +126,7 @@
%Include qgsvectorlayerundocommand.sip
%Include qgsvectorlayerundopassthroughcommand.sip
%Include qgsvectorlayerutils.sip
%Include qgsvirtuallayertask.sip
%Include qgsvirtuallayerdefinition.sip
%Include qgsvirtuallayerdefinitionutils.sip
%Include qgsmapthemecollection.sip
Expand Down
3 changes: 3 additions & 0 deletions python/core/qgsvectordataprovider.sip.in
Expand Up @@ -50,6 +50,7 @@ of feature and attribute information from a spatial datasource.
FastTruncate,
ReadLayerMetadata,
WriteLayerMetadata,
CancelSupport,
};

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

virtual bool cancel();

virtual bool addAttributes( const QList<QgsField> &attributes );
%Docstring
Adds new ``attributes`` to the provider. Returns true in case of success and false in case of failure.
Expand Down
3 changes: 3 additions & 0 deletions python/core/qgsvirtuallayerdefinition.sip.in
Expand Up @@ -150,6 +150,9 @@ Get the name of the field with unique identifiers
Set the name of the field with unique identifiers
%End

void setPostpone( bool postpone );
bool postpone() const;

QString geometryField() const;
%Docstring
Get the name of the geometry field. Empty if no geometry field
Expand Down
46 changes: 46 additions & 0 deletions python/core/qgsvirtuallayertask.sip
@@ -0,0 +1,46 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsvirtuallayertask.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsVirtualLayerTask : QgsTask
{
%Docstring

Initializes a virtual layer in a separated task.

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsvirtuallayertask.h"
%End
public:

QgsVirtualLayerTask( const QgsVirtualLayerDefinition &definition );

QgsVectorLayer *layer();

QgsVirtualLayerDefinition definition() const;

virtual bool run();


virtual void cancel();


};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsvirtuallayertask.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
94 changes: 71 additions & 23 deletions python/plugins/db_manager/db_plugins/vlayers/data_model.py
Expand Up @@ -19,14 +19,14 @@
***************************************************************************/
"""

from ..data_model import TableDataModel, BaseTableModel
from ..data_model import TableDataModel, BaseTableModel, SqlResultModelAsync

from .connector import VLayerRegistry, getQueryGeometryName
from .plugin import LVectorTable
from ..plugin import DbError
from ..plugin import DbError, BaseError

from qgis.PyQt.QtCore import QTime, QTemporaryFile
from qgis.core import QgsVectorLayer, QgsWkbTypes, QgsVirtualLayerDefinition
from qgis.core import QgsVectorLayer, QgsWkbTypes, QgsVirtualLayerDefinition, QgsVirtualLayerTask, QgsTask


class LTableDataModel(TableDataModel):
Expand Down Expand Up @@ -63,40 +63,88 @@ def rowCount(self, index=None):
return 0


class LSqlResultModel(BaseTableModel):
# BaseTableModel
class LSqlResultModelTask(QgsTask):

def __init__(self, subtask, db):
QgsTask.__init__(self)
self.subtask = subtask
self.db = db
self.model = None

def run(self):
try:
path = self.subtask.definition().filePath()
sql = self.subtask.definition().query()
self.model = LSqlResultModel(self.db, sql, None, self.subtask.layer(), path)
except Exception as e:
self.error = BaseError(str(e))
return False
return True

def cancelQuery(self):
self.subtask.cancel()
self.cancel()


class LSqlResultModelAsync(SqlResultModelAsync):

def __init__(self, db, sql, parent=None):
# create a virtual layer with non-geometry results
t = QTime()
t.start()
SqlResultModelAsync.__init__(self, db, sql, parent)

tf = QTemporaryFile()
tf.open()
tmp = tf.fileName()
path = tf.fileName()
tf.close()

df = QgsVirtualLayerDefinition()
df.setFilePath(tmp)
df.setQuery(sql)
p = QgsVectorLayer(df.toString(), "vv", "virtual")
self._secs = t.elapsed() / 1000.0

if not p.isValid():
data = []
header = []
raise DbError(p.dataProvider().error().summary(), sql)
df.setFilePath(path)
df.setQuery(self.sql)

self.subtask = QgsVirtualLayerTask(df)
self.task = LSqlResultModelTask(self.subtask, db)
self.task.addSubTask(self.subtask, [], QgsTask.ParentDependsOnSubTask)
self.task.taskCompleted.connect(self.modelDone)
self.task.taskTerminated.connect(self.modelDone)

def modelDone(self):
self.status = self.task.status
self.model = self.task.model
self.done.emit()


class LSqlResultModel(BaseTableModel):

def __init__(self, db, sql, parent=None, layer=None, path=None):
t = QTime()
t.start()

if not layer:
tf = QTemporaryFile()
tf.open()
path = tf.fileName()
tf.close()

df = QgsVirtualLayerDefinition()
df.setFilePath(path)
df.setQuery(sql)
layer = QgsVectorLayer(df.toString(), "vv", "virtual")
self._secs = t.elapsed() / 1000.0

data = []
header = []

if not layer.isValid():
raise DbError(layer.dataProvider().error().summary(), sql)
else:
header = [f.name() for f in p.fields()]
header = [f.name() for f in layer.fields()]
has_geometry = False
if p.geometryType() != QgsWkbTypes.NullGeometry:
gn = getQueryGeometryName(tmp)
if layer.geometryType() != QgsWkbTypes.NullGeometry:
gn = getQueryGeometryName(path)
if gn:
has_geometry = True
header += [gn]

data = []
for f in p.getFeatures():
for f in layer.getFeatures():
a = f.attributes()
if has_geometry:
if f.hasGeometry():
Expand Down
4 changes: 4 additions & 0 deletions python/plugins/db_manager/db_plugins/vlayers/plugin.py
Expand Up @@ -98,6 +98,10 @@ def sqlResultModel(self, sql, parent):
from .data_model import LSqlResultModel
return LSqlResultModel(self, sql, parent)

def sqlResultModelAsync(self, sql, parent):
from .data_model import LSqlResultModelAsync
return LSqlResultModelAsync(self, sql, parent)

def toSqlLayer(self, sql, geomCol, uniqueCol, layerName="QueryLayer", layerType=None, avoidSelectById=False, _filter=""):
df = QgsVirtualLayerDefinition()
df.setQuery(sql)
Expand Down
1 change: 0 additions & 1 deletion python/plugins/db_manager/dlg_cancel_task_query.py
Expand Up @@ -61,6 +61,5 @@ def show(self):
super(QDialog, self).show()

def hide(self):
self.cancelStatus = False
self.mGif.stop()
super(QDialog, self).hide()
35 changes: 18 additions & 17 deletions python/plugins/db_manager/dlg_sql_window.py
Expand Up @@ -187,24 +187,25 @@ def executeSqlCanceled(self):
def executeSqlCompleted(self):
self.dlg_cancel_task.hide()

if self.modelAsync.task.status() == QgsTask.Complete:
model = self.modelAsync.model
cols = []
quotedCols = []

self.viewResult.setModel(model)
self.lblResult.setText(self.tr("{0} rows, {1:.1f} seconds").format(model.affectedRows(), model.secs()))
cols = self.viewResult.model().columnNames()
for col in cols:
quotedCols.append(self.db.connector.quoteId(col))
with OverrideCursor(Qt.WaitCursor):
if self.modelAsync.task.status() == QgsTask.Complete:
model = self.modelAsync.model
cols = []
quotedCols = []

self.viewResult.setModel(model)
self.lblResult.setText(self.tr("{0} rows, {1:.1f} seconds").format(model.affectedRows(), model.secs()))
cols = self.viewResult.model().columnNames()
for col in cols:
quotedCols.append(self.db.connector.quoteId(col))

self.setColumnCombos(cols, quotedCols)
self.update()
elif not self.dlg_cancel_task.cancelStatus:
DlgDbError.showError(self.modelAsync.error, self)
self.uniqueModel.clear()
self.geomCombo.clear()
pass
self.setColumnCombos(cols, quotedCols)
self.update()
elif not self.dlg_cancel_task.cancelStatus:
DlgDbError.showError(self.modelAsync.error, self)
self.uniqueModel.clear()
self.geomCombo.clear()
pass

def executeSql(self):

Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -297,6 +297,7 @@ SET(QGIS_CORE_SRCS
qgsvectorfilewriter.cpp
qgsvectorfilewritertask.cpp
qgsvectorlayer.cpp
qgsvirtuallayertask.cpp
qgsvectorlayerfeaturecounter.cpp
qgsvectorlayercache.cpp
qgsvectorlayerdiagramprovider.cpp
Expand Down Expand Up @@ -635,6 +636,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsvectorlayereditbuffer.h
qgsvectorlayereditpassthrough.h
qgsvectorlayer.h
qgsvirtuallayertask.h
qgsvectorlayerexporter.h
qgsvectorlayerfeaturecounter.h
qgsvectorlayerjoinbuffer.h
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsvectordataprovider.cpp
Expand Up @@ -804,6 +804,11 @@ QTextCodec *QgsVectorDataProvider::textEncoding() const
return mEncoding;
}

bool QgsVectorDataProvider::cancel()
{
return false;
}

QStringList QgsVectorDataProvider::sEncodings;

QList<QgsRelation> QgsVectorDataProvider::discoverRelations( const QgsVectorLayer *, const QList<QgsVectorLayer *> & ) const
Expand Down
3 changes: 3 additions & 0 deletions src/core/qgsvectordataprovider.h
Expand Up @@ -90,6 +90,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
FastTruncate = 1 << 20, //!< Supports fast truncation of the layer (removing all features). Since QGIS 3.0
ReadLayerMetadata = 1 << 21, //!< Provider can read layer metadata from data store. Since QGIS 3.0. See QgsDataProvider::layerMetadata()
WriteLayerMetadata = 1 << 22, //!< Provider can write layer metadata to the data store. Since QGIS 3.0. See QgsDataProvider::writeLayerMetadata()
CancelSupport = 1 << 23, //!< Supports interruption of pending queries from a separated thread. Since QGIS 3.0
};

Q_DECLARE_FLAGS( Capabilities, Capability )
Expand Down Expand Up @@ -248,6 +249,8 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
*/
virtual bool truncate();

virtual bool cancel();

/**
* Adds new \a attributes to the provider. Returns true in case of success and false in case of failure.
* If attributes are added using this method then QgsVectorLayer::updateFields() must be called
Expand Down
9 changes: 9 additions & 0 deletions src/core/qgsvirtuallayerdefinition.cpp
Expand Up @@ -157,6 +157,10 @@ QgsVirtualLayerDefinition QgsVirtualLayerDefinition::fromUrl( const QUrl &url )
}
}
}
else if ( key == QLatin1String( "postpone" ) )
{
def.setPostpone( true );
}
}
def.setFields( fields );

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

if ( postpone() )
{
url.addQueryItem( QStringLiteral( "postpone" ), QLatin1String( "" ) );
}

return url;
}

Expand Down
4 changes: 4 additions & 0 deletions src/core/qgsvirtuallayerdefinition.h
Expand Up @@ -131,6 +131,9 @@ class CORE_EXPORT QgsVirtualLayerDefinition
//! Set the name of the field with unique identifiers
void setUid( const QString &uid ) { mUid = uid; }

void setPostpone( bool postpone ) { mPostpone = postpone; }
bool postpone() const { return mPostpone; }

//! Get the name of the geometry field. Empty if no geometry field
QString geometryField() const { return mGeometryField; }
//! Set the name of the geometry field
Expand Down Expand Up @@ -174,6 +177,7 @@ class CORE_EXPORT QgsVirtualLayerDefinition
QString mGeometryField;
QString mFilePath;
QgsFields mFields;
bool mPostpone = false;
QgsWkbTypes::Type mGeometryWkbType = QgsWkbTypes::Unknown;
long mGeometrySrid = 0;
};
Expand Down

0 comments on commit 957e92c

Please sign in to comment.