Skip to content

Commit

Permalink
[DBManager] add SL/Geopackage connection by drag&drop (follow b11f67b…
Browse files Browse the repository at this point in the history
…4f5), improve GPKG support:

- recognize tables with geometry,
- load GPGK layers to canvas from context menu,
- disable table editing
  • Loading branch information
brushtyler committed Sep 2, 2015
1 parent 7d79a73 commit 03916d5
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 95 deletions.
61 changes: 45 additions & 16 deletions python/plugins/db_manager/db_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ def __init__(self, parent=None):
if self.isImportVectorAvail:
self.connect(self, SIGNAL("importVector"), self.importVector)

self.hasSpatialiteSupport = "spatialite" in supportedDbTypes()

self.rootItem = TreeItem(None, None)
for dbtype in supportedDbTypes():
dbpluginclass = createDbPlugin(dbtype)
Expand Down Expand Up @@ -373,13 +375,18 @@ def flags(self, index):
if isinstance(item, TableItem):
flags |= Qt.ItemIsDragEnabled

if self.isImportVectorAvail: # allow to import a vector layer
# vectors/tables can be dropped on connected databases to be imported
if self.isImportVectorAvail:
if isinstance(item, ConnectionItem) and item.populated:
flags |= Qt.ItemIsDropEnabled

if isinstance(item, SchemaItem) or isinstance(item, TableItem):
if isinstance(item, (SchemaItem, TableItem)):
flags |= Qt.ItemIsDropEnabled

# SL/Geopackage db files can be dropped everywhere in the tree
if self.hasSpatialiteSupport:
flags |= Qt.ItemIsDropEnabled

return flags

def headerData(self, section, orientation, role):
Expand Down Expand Up @@ -508,8 +515,10 @@ def dropMimeData(self, data, action, row, column, parent):
if action == Qt.IgnoreAction:
return True

if not self.isImportVectorAvail:
return False
# vectors/tables to be imported must be dropped on connected db, schema or table
canImportLayer = self.isImportVectorAvail and parent.isValid() and \
( isinstance(parent.internalPointer(), (SchemaItem, TableItem)) or \
( isinstance(parent.internalPointer(), ConnectionItem) and parent.internalPointer().populated ) )

added = 0

Expand All @@ -518,22 +527,42 @@ def dropMimeData(self, data, action, row, column, parent):
filename = u.toLocalFile()
if filename == "":
continue
if qgis.core.QgsRasterLayer.isValidRasterFileName(filename):
layerType = 'raster'
providerKey = 'gdal'
else:
layerType = 'vector'
providerKey = 'ogr'

layerName = QFileInfo(filename).completeBaseName()

if self.importLayer(layerType, providerKey, layerName, filename, parent):
added += 1
if self.hasSpatialiteSupport:
from .db_plugins.spatialite.connector import SpatiaLiteDBConnector

if SpatiaLiteDBConnector.isValidDatabase(filename):
# retrieve the SL plugin tree item using its path
index = self._rPath2Index(["spatialite"])
if not index.isValid():
continue
item = index.internalPointer()

conn_name = QFileInfo(filename).fileName()
uri = qgis.core.QgsDataSourceURI()
uri.setDatabase(filename)
item.getItemData().addConnection(conn_name, uri)
item.emit(SIGNAL('itemChanged'), item)
added += 1
continue

if canImportLayer:
if qgis.core.QgsRasterLayer.isValidRasterFileName(filename):
layerType = 'raster'
providerKey = 'gdal'
else:
layerType = 'vector'
providerKey = 'ogr'

layerName = QFileInfo(filename).completeBaseName()
if self.importLayer(layerType, providerKey, layerName, filename, parent):
added += 1

if data.hasFormat(self.QGIS_URI_MIME):
for uri in qgis.core.QgsMimeDataUtils.decodeUriList(data):
if self.importLayer(uri.layerType, uri.providerKey, uri.name, uri.uri, parent):
added += 1
if canImportLayer:
if self.importLayer(uri.layerType, uri.providerKey, uri.name, uri.uri, parent):
added += 1

return added > 0

Expand Down
47 changes: 39 additions & 8 deletions python/plugins/db_manager/db_plugins/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ def info(self):

return DatabaseInfo(None)

def connect(self, parent=None):
raise NotImplemented

def connectToUri(self, uri):
self.db = self.databasesFactory(self, uri)
if self.db:
Expand All @@ -111,6 +114,17 @@ def reconnect(self):
return self.connectToUri(uri)
return self.connect(self.parent())

def remove(self):
settings = QSettings()
settings.beginGroup(u"/%s/%s" % (self.connectionSettingsKey(), self.connectionName()))
settings.remove("")
self.emit(SIGNAL('deleted'))
return True

@classmethod
def addConnection(self, conn_name, uri):
raise NotImplemented

@classmethod
def icon(self):
return None
Expand All @@ -135,11 +149,6 @@ def connectionSettingsKey(self):
# return the key used to store the connections in settings
pass

@classmethod
def connectionSettingsFileKey(self):
# return the filekey for the settings
pass

@classmethod
def connections(self):
# get the list of connections
Expand All @@ -154,6 +163,24 @@ def connections(self):
def databasesFactory(self, connection, uri):
return None

@classmethod
def addConnectionActionSlot(self, item, action, parent):
raise NotImplemented

def removeActionSlot(self, item, action, parent):
QApplication.restoreOverrideCursor()
try:
res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "hey!"),
QApplication.translate("DBManagerPlugin",
"Really remove connection to %s?") % item.connectionName(),
QMessageBox.Yes | QMessageBox.No)
if res != QMessageBox.Yes:
return
finally:
QApplication.setOverrideCursor(Qt.WaitCursor)

item.remove()


class DbItemObject(QObject):

Expand Down Expand Up @@ -207,6 +234,13 @@ def uri(self):
def publicUri(self):
return self.connector.publicUri()

def delete(self):
self.aboutToChange()
ret = self.connection().remove()
if ret is not False:
self.emit(SIGNAL('deleted'))
return ret

def info(self):
from .info_model import DatabaseInfo

Expand Down Expand Up @@ -636,9 +670,6 @@ def uri(self):

def mimeUri(self):
layerType = "raster" if self.type == Table.RasterType else "vector"
if self.database().dbplugin().typeName() == "spatialite" and self.database().connector.isgpkg():
url = unicode(self.database().connector._connectionInfo() + "|layername=" + self.name)
return u"%s:%s:%s:%s" % (layerType, "ogr", self.name, url)
return u"%s:%s:%s:%s" % (layerType, self.database().dbplugin().providerName(), self.name, self.uri().uri())

def toMapLayer(self):
Expand Down
5 changes: 1 addition & 4 deletions python/plugins/db_manager/db_plugins/postgis/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@ def providerName(self):
def connectionSettingsKey(self):
return '/PostgreSQL/connections'

@classmethod
def connectionSettingsFileKey(self):
return "database"

def databasesFactory(self, connection, uri):
return PGDatabase(connection, uri)

Expand Down Expand Up @@ -284,6 +280,7 @@ def gdalUri(self):
return gdalUri

def mimeUri(self):
# QGIS has no provider for PGRasters, let's use GDAL
uri = u"raster:gdal:%s:%s" % (self.name, re.sub(":", "\:", self.gdalUri()))
return uri

Expand Down
Loading

4 comments on commit 03916d5

@gioman
Copy link
Contributor

@gioman gioman commented on 03916d5 Sep 12, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Giuseppe for the fixes!

Beside the missing geopackage/browser support I still see a number of inconsistencies:

  • where the user can create a new, empty geopackage db? In "layer > new layer" there is no "new geopackage layer", that should lead as for spatialite, to a button that allows create a new empty db.
  • in DB Manager the geopackage/spatial tables are all identifies by the icon with the "?" (the type of geometry is not identified), is this correct?
  • geopackage tables all show the "no spatial index defined (create it)" link, but once clicked this does nothing.

@brushtyler
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @gioman

  • it's an OGR datasource, I'm quite sure you can just create it from New vector layer (you can save any loaded layer to GPKG from Save as entry in TOC context menu),
  • I see the right geom type here (e.g. linestring), but I've not tried all the geometry types, maybe any of them are not correctly handled,
  • as far as I've written, I just disabled editing on GPKG files to avoid issues, but it requires few other adjustaments. Please file a ticket for those, thanks.

@gioman
Copy link
Contributor

@gioman gioman commented on 03916d5 Sep 21, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @brushtyler ! :)

  • "save as" do not offer to save as geopackage. Anyway the issue I was referring is that apparently is not possible to create an empty geopackage, allowing later (maybe via DB manager) to add in it layers
  • try any of the geopackage samples http://www.geopackage.org/#sampledata
  • Ok

ciao!

@brushtyler
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I can confirm, Save as displays GeoPackage as output format here. It probably depends on the GDAL/OGR version QGIS links against (I'm on Ubuntu 14.04, GDAL/OGR v1.11.2).
  • And it works as well ;), I can see all but geomcollections geometry types icons.

Please sign in to comment.