plugin.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. """
  2. /***************************************************************************
  3. Name : DB Manager
  4. Description : Database manager plugin for QGIS
  5. Date : May 23, 2011
  6. copyright : (C) 2011 by Giuseppe Sucameli
  7. email : brush.tyler@gmail.com
  8. ***************************************************************************/
  9. /***************************************************************************
  10. * *
  11. * This program is free software; you can redistribute it and/or modify *
  12. * it under the terms of the GNU General Public License as published by *
  13. * the Free Software Foundation; either version 2 of the License, or *
  14. * (at your option) any later version. *
  15. * *
  16. ***************************************************************************/
  17. """
  18. # this will disable the dbplugin if the connector raise an ImportError
  19. from .connector import SpatiaLiteDBConnector
  20. from qgis.PyQt.QtCore import Qt, QFileInfo, QCoreApplication
  21. from qgis.PyQt.QtGui import QIcon
  22. from qgis.PyQt.QtWidgets import QApplication, QAction, QFileDialog
  23. from qgis.core import Qgis, QgsApplication, QgsDataSourceUri, QgsSettings
  24. from qgis.gui import QgsMessageBar
  25. from ..plugin import DBPlugin, Database, Table, VectorTable, RasterTable, TableField, TableIndex, TableTrigger, \
  26. InvalidDataException
  27. def classFactory():
  28. return SpatiaLiteDBPlugin
  29. class SpatiaLiteDBPlugin(DBPlugin):
  30. @classmethod
  31. def icon(self):
  32. return QgsApplication.getThemeIcon("/mIconSpatialite.svg")
  33. @classmethod
  34. def typeName(self):
  35. return 'spatialite'
  36. @classmethod
  37. def typeNameString(self):
  38. return QCoreApplication.translate('db_manager', 'SpatiaLite')
  39. @classmethod
  40. def providerName(self):
  41. return 'spatialite'
  42. @classmethod
  43. def connectionSettingsKey(self):
  44. return '/SpatiaLite/connections'
  45. def databasesFactory(self, connection, uri):
  46. return SLDatabase(connection, uri)
  47. def connect(self, parent=None):
  48. conn_name = self.connectionName()
  49. settings = QgsSettings()
  50. settings.beginGroup("/%s/%s" % (self.connectionSettingsKey(), conn_name))
  51. if not settings.contains("sqlitepath"): # non-existent entry?
  52. raise InvalidDataException(self.tr('There is no defined database connection "{0}".').format(conn_name))
  53. database = settings.value("sqlitepath")
  54. uri = QgsDataSourceUri()
  55. uri.setDatabase(database)
  56. return self.connectToUri(uri)
  57. @classmethod
  58. def addConnection(self, conn_name, uri):
  59. settings = QgsSettings()
  60. settings.beginGroup("/%s/%s" % (self.connectionSettingsKey(), conn_name))
  61. settings.setValue("sqlitepath", uri.database())
  62. return True
  63. @classmethod
  64. def addConnectionActionSlot(self, item, action, parent, index):
  65. QApplication.restoreOverrideCursor()
  66. try:
  67. filename, selected_filter = QFileDialog.getOpenFileName(parent, "Choose SQLite/SpatiaLite file")
  68. if not filename:
  69. return
  70. finally:
  71. QApplication.setOverrideCursor(Qt.WaitCursor)
  72. conn_name = QFileInfo(filename).fileName()
  73. uri = QgsDataSourceUri()
  74. uri.setDatabase(filename)
  75. self.addConnection(conn_name, uri)
  76. index.internalPointer().itemChanged()
  77. class SLDatabase(Database):
  78. def __init__(self, connection, uri):
  79. Database.__init__(self, connection, uri)
  80. def connectorsFactory(self, uri):
  81. return SpatiaLiteDBConnector(uri)
  82. def dataTablesFactory(self, row, db, schema=None):
  83. return SLTable(row, db, schema)
  84. def vectorTablesFactory(self, row, db, schema=None):
  85. return SLVectorTable(row, db, schema)
  86. def rasterTablesFactory(self, row, db, schema=None):
  87. return SLRasterTable(row, db, schema)
  88. def info(self):
  89. from .info_model import SLDatabaseInfo
  90. return SLDatabaseInfo(self)
  91. def sqlResultModel(self, sql, parent):
  92. from .data_model import SLSqlResultModel
  93. return SLSqlResultModel(self, sql, parent)
  94. def sqlResultModelAsync(self, sql, parent):
  95. from .data_model import SLSqlResultModelAsync
  96. return SLSqlResultModelAsync(self, sql, parent)
  97. def registerDatabaseActions(self, mainWindow):
  98. action = QAction(self.tr("Run &Vacuum"), self)
  99. mainWindow.registerAction(action, self.tr("&Database"), self.runVacuumActionSlot)
  100. Database.registerDatabaseActions(self, mainWindow)
  101. def runVacuumActionSlot(self, item, action, parent):
  102. QApplication.restoreOverrideCursor()
  103. try:
  104. if not isinstance(item, (DBPlugin, Table)) or item.database() is None:
  105. parent.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."),
  106. Qgis.Info, parent.iface.messageTimeout())
  107. return
  108. finally:
  109. QApplication.setOverrideCursor(Qt.WaitCursor)
  110. self.runVacuum()
  111. def runVacuum(self):
  112. self.database().aboutToChange.emit()
  113. self.database().connector.runVacuum()
  114. self.database().refresh()
  115. def runAction(self, action):
  116. action = str(action)
  117. if action.startswith("vacuum/"):
  118. if action == "vacuum/run":
  119. self.runVacuum()
  120. return True
  121. return Database.runAction(self, action)
  122. def uniqueIdFunction(self):
  123. return None
  124. def explicitSpatialIndex(self):
  125. return True
  126. def spatialIndexClause(self, src_table, src_column, dest_table, dest_column):
  127. return """ "%s".ROWID IN (\nSELECT ROWID FROM SpatialIndex WHERE f_table_name='%s' AND search_frame="%s"."%s") """ % (src_table, src_table, dest_table, dest_column)
  128. def supportsComment(self):
  129. return False
  130. class SLTable(Table):
  131. def __init__(self, row, db, schema=None):
  132. Table.__init__(self, db, None)
  133. self.name, self.isView, self.isSysTable = row
  134. def ogrUri(self):
  135. ogrUri = "%s|layername=%s" % (self.uri().database(), self.name)
  136. return ogrUri
  137. def mimeUri(self):
  138. return Table.mimeUri(self)
  139. def toMapLayer(self, geometryType=None, crs=None):
  140. from qgis.core import QgsVectorLayer
  141. provider = self.database().dbplugin().providerName()
  142. uri = self.uri().uri()
  143. return QgsVectorLayer(uri, self.name, provider)
  144. def tableFieldsFactory(self, row, table):
  145. return SLTableField(row, table)
  146. def tableIndexesFactory(self, row, table):
  147. return SLTableIndex(row, table)
  148. def tableTriggersFactory(self, row, table):
  149. return SLTableTrigger(row, table)
  150. def tableDataModel(self, parent):
  151. from .data_model import SLTableDataModel
  152. return SLTableDataModel(self, parent)
  153. class SLVectorTable(SLTable, VectorTable):
  154. def __init__(self, row, db, schema=None):
  155. SLTable.__init__(self, row[:-5], db, schema)
  156. VectorTable.__init__(self, db, schema)
  157. # SpatiaLite does case-insensitive checks for table names, but the
  158. # SL provider didn't do the same in QGIS < 1.9, so self.geomTableName
  159. # stores the table name like stored in the geometry_columns table
  160. self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = row[-5:]
  161. def uri(self):
  162. uri = self.database().uri()
  163. uri.setDataSource('', self.geomTableName, self.geomColumn)
  164. return uri
  165. def hasSpatialIndex(self, geom_column=None):
  166. geom_column = geom_column if geom_column is not None else self.geomColumn
  167. return self.database().connector.hasSpatialIndex((self.schemaName(), self.name), geom_column)
  168. def createSpatialIndex(self, geom_column=None):
  169. self.aboutToChange.emit()
  170. ret = VectorTable.createSpatialIndex(self, geom_column)
  171. if ret is not False:
  172. self.database().refresh()
  173. return ret
  174. def deleteSpatialIndex(self, geom_column=None):
  175. self.aboutToChange.emit()
  176. ret = VectorTable.deleteSpatialIndex(self, geom_column)
  177. if ret is not False:
  178. self.database().refresh()
  179. return ret
  180. def refreshTableEstimatedExtent(self):
  181. return
  182. def runAction(self, action):
  183. if SLTable.runAction(self, action):
  184. return True
  185. return VectorTable.runAction(self, action)
  186. class SLRasterTable(SLTable, RasterTable):
  187. def __init__(self, row, db, schema=None):
  188. SLTable.__init__(self, row[:-3], db, schema)
  189. RasterTable.__init__(self, db, schema)
  190. self.prefixName, self.geomColumn, self.srid = row[-3:]
  191. self.geomType = 'RASTER'
  192. # def info(self):
  193. # from .info_model import SLRasterTableInfo
  194. # return SLRasterTableInfo(self)
  195. def rasterliteGdalUri(self):
  196. gdalUri = 'RASTERLITE:%s,table=%s' % (self.uri().database(), self.prefixName)
  197. return gdalUri
  198. def mimeUri(self):
  199. # QGIS has no provider to load rasters, let's use GDAL
  200. uri = "raster:gdal:%s:%s" % (self.name, self.uri().database())
  201. return uri
  202. def toMapLayer(self, geometryType=None, crs=None):
  203. from qgis.core import QgsRasterLayer, QgsContrastEnhancement
  204. # QGIS has no provider to load Rasterlite rasters, let's use GDAL
  205. uri = self.rasterliteGdalUri()
  206. rl = QgsRasterLayer(uri, self.name)
  207. if rl.isValid():
  208. rl.setContrastEnhancement(QgsContrastEnhancement.StretchToMinimumMaximum)
  209. return rl
  210. class SLTableField(TableField):
  211. def __init__(self, row, table):
  212. TableField.__init__(self, table)
  213. self.num, self.name, self.dataType, self.notNull, self.default, self.primaryKey = row
  214. self.hasDefault = self.default
  215. class SLTableIndex(TableIndex):
  216. def __init__(self, row, table):
  217. TableIndex.__init__(self, table)
  218. self.num, self.name, self.isUnique, self.columns = row
  219. class SLTableTrigger(TableTrigger):
  220. def __init__(self, row, table):
  221. TableTrigger.__init__(self, table)
  222. self.name, self.function = row