Skip to content

Commit

Permalink
Add a cancel button for Postgis and Spatialite
Browse files Browse the repository at this point in the history
  • Loading branch information
pblottiere committed Mar 2, 2018
1 parent 0081f78 commit bf7df6d
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 33 deletions.
3 changes: 3 additions & 0 deletions python/plugins/db_manager/db_plugins/connector.py
Expand Up @@ -42,6 +42,9 @@ def __del__(self):
def uri(self):
return QgsDataSourceUri(self._uri.uri(False))

def cancel(self):
pass

def publicUri(self):
publicUri = QgsDataSourceUri.removePassword(self._uri.uri(False))
return QgsDataSourceUri(publicUri)
Expand Down
40 changes: 37 additions & 3 deletions python/plugins/db_manager/db_plugins/data_model.py
Expand Up @@ -22,11 +22,18 @@
from builtins import str
from builtins import range

from qgis.PyQt.QtCore import Qt, QTime, QRegExp, QAbstractTableModel
from qgis.PyQt.QtGui import QFont, QStandardItemModel, QStandardItem
from qgis.PyQt.QtCore import (Qt,
QTime,
QRegExp,
QAbstractTableModel,
pyqtSignal,
QObject)
from qgis.PyQt.QtGui import (QFont,
QStandardItemModel,
QStandardItem)
from qgis.PyQt.QtWidgets import QApplication

from .plugin import DbError
from .plugin import DbError, BaseError


class BaseTableModel(QAbstractTableModel):
Expand Down Expand Up @@ -139,6 +146,33 @@ def rowCount(self, index=None):
return self.table.rowCount if self.table.rowCount is not None and self.columnCount(index) > 0 else 0


class SqlResultModelAsync(QObject):

done = pyqtSignal()

def __init__(self, db, sql, parent=None):
QObject.__init__(self)
self.db = db
self.sql = sql
self.parent = parent
self.error = BaseError('')
self.status = None
self.model = None
self.task = None

def cancel(self):
if self.task:
self.task.cancelQuery()

def modelDone(self):
if self.task:
self.status = self.task.status
self.model = self.task.model
self.error = self.task.error

self.done.emit()


class SqlResultModel(BaseTableModel):

def __init__(self, db, sql, parent=None):
Expand Down
5 changes: 5 additions & 0 deletions python/plugins/db_manager/db_plugins/plugin.py
Expand Up @@ -256,6 +256,11 @@ def sqlResultModel(self, sql, parent):

return SqlResultModel(self, sql, parent)

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

return SqlResultModelAsync(self, sql, parent)

def columnUniqueValuesModel(self, col, table, limit=10):
l = ""
if limit is not None:
Expand Down
4 changes: 4 additions & 0 deletions python/plugins/db_manager/db_plugins/postgis/connector.py
Expand Up @@ -186,6 +186,10 @@ def _checkRasterColumnsTable(self):
self.has_raster_columns_access = self.getTablePrivileges('raster_columns')[0]
return self.has_raster_columns

def cancel(self):
if self.connection:
self.connection.cancel()

def getInfo(self):
c = self._execute(None, u"SELECT version()")
res = self._fetchone(c)
Expand Down
41 changes: 39 additions & 2 deletions python/plugins/db_manager/db_plugins/postgis/data_model.py
Expand Up @@ -20,8 +20,9 @@
***************************************************************************/
"""


from ..data_model import TableDataModel, SqlResultModel
from qgis.core import QgsTask
from ..plugin import BaseError
from ..data_model import TableDataModel, SqlResultModel, SqlResultModelAsync


class PGTableDataModel(TableDataModel):
Expand Down Expand Up @@ -79,5 +80,41 @@ def fetchMoreData(self, row_start):
self.fetchedFrom = row_start


class PGSqlResultModelTask(QgsTask):

def __init__(self, db, sql, parent):
QgsTask.__init__(self)
self.db = db
self.sql = sql
self.parent = parent
self.error = BaseError('')
self.model = None

def run(self):

try:
self.model = PGSqlResultModel(self.db, self.sql, self.parent)
except BaseError as e:
self.error = e
QgsMessageLog.logMessage(e.msg)
return False

return True

def cancelQuery(self):
self.db.connector.cancel()
self.cancel()


class PGSqlResultModelAsync(SqlResultModelAsync):

def __init__(self, db, sql, parent):
SqlResultModelAsync.__init__(self, db, sql, parent)

self.task = PGSqlResultModelTask(db, sql, parent)
self.task.taskCompleted.connect(self.modelDone)
self.task.taskTerminated.connect(self.modelDone)


class PGSqlResultModel(SqlResultModel):
pass
5 changes: 5 additions & 0 deletions python/plugins/db_manager/db_plugins/postgis/plugin.py
Expand Up @@ -134,6 +134,11 @@ def sqlResultModel(self, sql, parent):

return PGSqlResultModel(self, sql, parent)

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

return PGSqlResultModelAsync(self, sql, parent)

def registerDatabaseActions(self, mainWindow):
Database.registerDatabaseActions(self, mainWindow)

Expand Down
6 changes: 6 additions & 0 deletions python/plugins/db_manager/db_plugins/spatialite/connector.py
Expand Up @@ -59,6 +59,12 @@ def __init__(self, uri):
def _connectionInfo(self):
return str(self.dbname)

def cancel(self):
# https://www.sqlite.org/c3ref/interrupt.html
# This function causes any pending database operation to abort and return at its earliest opportunity.
if self.connection:
self.connection.interrupt()

@classmethod
def isValidDatabase(self, path):
if not QFile.exists(path):
Expand Down
47 changes: 46 additions & 1 deletion python/plugins/db_manager/db_plugins/spatialite/data_model.py
Expand Up @@ -20,7 +20,10 @@
***************************************************************************/
"""

from ..data_model import TableDataModel, SqlResultModel
from qgis.core import QgsTask
from ..plugin import BaseError
from ..data_model import TableDataModel, SqlResultModel, SqlResultModelAsync
from .plugin import SLDatabase


class SLTableDataModel(TableDataModel):
Expand Down Expand Up @@ -60,5 +63,47 @@ def rowCount(self, index=None):
return self.fetchedCount


class SLSqlResultModelTask(QgsTask):

def __init__(self, db, sql, parent):
QgsTask.__init__(self)
self.db = db
self.sql = sql
self.parent = parent
self.error = BaseError('')
self.model = None
self.clone = None

def run(self):
try:
self.clone = SLDatabase(None, self.db.connector.uri())

# import time
# self.clone.connector.connection.create_function("sleep", 1, time.sleep)

self.model = SLSqlResultModel(self.clone, self.sql, None)
except BaseError as e:
self.error = e
QgsMessageLog.logMessage(e.msg)
return False

return True

def cancelQuery(self):
if self.clone:
self.clone.connector.cancel()
self.cancel()


class SLSqlResultModelAsync(SqlResultModelAsync):

def __init__(self, db, sql, parent):
SqlResultModelAsync.__init__(self, db, sql, parent)

self.task = SLSqlResultModelTask(db, sql, parent)
self.task.taskCompleted.connect(self.modelDone)
self.task.taskTerminated.connect(self.modelDone)


class SLSqlResultModel(SqlResultModel):
pass
5 changes: 5 additions & 0 deletions python/plugins/db_manager/db_plugins/spatialite/plugin.py
Expand Up @@ -130,6 +130,11 @@ def sqlResultModel(self, sql, parent):

return SLSqlResultModel(self, sql, parent)

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

return SLSqlResultModelAsync(self, sql, parent)

def registerDatabaseActions(self, mainWindow):
action = QAction(self.tr("Run &Vacuum"), self)
mainWindow.registerAction(action, self.tr("&Database"), self.runVacuumActionSlot)
Expand Down
66 changes: 66 additions & 0 deletions python/plugins/db_manager/dlg_cancel_task_query.py
@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-

"""
/***************************************************************************
Name : DB Manager
Description : Database manager plugin for QGIS
Date : January 15, 2018
copyright : (C) 2018 by Paul Blottiere
email : paul.blottiere@oslandia.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
"""

from qgis.PyQt.QtWidgets import QDialog, QLabel, QHBoxLayout
from qgis.PyQt.QtGui import QMovie
from qgis.PyQt.QtCore import QSize, Qt, pyqtSignal

from qgis.core import QgsApplication

from .ui.ui_DlgCancelTaskQuery import Ui_DlgCancelTaskQuery as Ui_Dialog


class DlgCancelTaskQuery(QDialog, Ui_Dialog):

canceled = pyqtSignal()

def __init__(self, parent=None):
QDialog.__init__(self, parent)
self.setupUi(self)

gif = QgsApplication.iconPath("/mIconLoading.gif")
self.mGif = QMovie(gif)
self.mGif.setScaledSize(QSize(16, 16))

self.mMovie.setMovie(self.mGif)
self.setWindowModality(Qt.ApplicationModal)

self.mCancelButton.clicked.connect(self.cancel)

self.cancelStatus = False

def cancel(self):
self.mLabel.setText("Stopping SQL...")
self.cancelStatus = True
self.mCancelButton.setEnabled(False)
self.canceled.emit()

def show(self):
self.cancelStatus = False
self.mGif.start()
self.mCancelButton.setEnabled(True)
self.mLabel.setText("Executing SQL...")
super(QDialog, self).show()

def hide(self):
self.cancelStatus = False
self.mGif.stop()
super(QDialog, self).hide()

0 comments on commit bf7df6d

Please sign in to comment.