Example #1
0
class GdalToolsOptionsTable(QWidget, Ui_OptionsTable):
    rowAdded = pyqtSignal(int)
    rowRemoved = pyqtSignal()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.setupUi(self)

        self.table.cellChanged.connect(self.cellValueChanged)

        self.table.itemSelectionChanged.connect(self.enableDeleteButton)
        self.btnAdd.clicked.connect(self.addNewRow)
        self.btnDel.clicked.connect(self.deleteRow)

        self.btnDel.setEnabled(False)

    def enableDeleteButton(self):
        self.btnDel.setEnabled(self.table.currentRow() >= 0)

    def addNewRow(self):
        self.table.insertRow(self.table.rowCount())
        # select the added row
        newRow = self.table.rowCount() - 1
        item = QTableWidgetItem()
        self.table.setItem(newRow, 0, item)
        self.table.setCurrentItem(item)
        self.rowAdded.emit(newRow)

    def deleteRow(self):
        if self.table.currentRow() >= 0:
            self.table.removeRow(self.table.currentRow())
            # select the previous row or the next one if there is no previous row
            item = self.table.item(self.table.currentRow(), 0)
            self.table.setCurrentItem(item)
            self.rowRemoved.emit()

    def options(self):
        options = []
        for row in range(0, self.table.rowCount()):
            name = self.table.item(row, 0)
            if not name:
                continue

            value = self.table.item(row, 1)
            if not value:
                continue

            options.append(name.text() + "=" + value.text())
        return options
Example #2
0
class NumberInputPanel(BASE, WIDGET):

    hasChanged = pyqtSignal()

    def __init__(self, number, minimum, maximum, isInteger):
        super(NumberInputPanel, self).__init__(None)
        self.setupUi(self)

        self.isInteger = isInteger
        if self.isInteger:
            self.spnValue.setDecimals(0)
        else:
            #Guess reasonable step value
            if (maximum == 0 or maximum) and (minimum == 0 or minimum):
                self.spnValue.setSingleStep(self.calculateStep(minimum, maximum))

        if maximum == 0 or maximum:
            self.spnValue.setMaximum(maximum)
        else:
            self.spnValue.setMaximum(99999999)
        if minimum == 0 or minimum:
            self.spnValue.setMinimum(minimum)
        else:
            self.spnValue.setMinimum(-99999999)

        #Set default value
        if number == 0 or number:
            self.spnValue.setValue(float(number))
            self.spnValue.setClearValue(float(number))
        elif minimum == 0 or minimum:
            self.spnValue.setValue(float(minimum))
            self.spnValue.setClearValue(float(minimum))
        else:
            self.spnValue.setValue(0)
            self.spnValue.setClearValue(0)

        self.btnCalc.clicked.connect(self.showNumberInputDialog)

        self.spnValue.valueChanged.connect(lambda: self.hasChanged.emit())

    def showNumberInputDialog(self):
        dlg = NumberInputDialog(self.isInteger)
        dlg.exec_()
        if dlg.value is not None:
            self.spnValue.setValue(dlg.value)

    def getValue(self):
        return self.spnValue.value()

    def calculateStep(self, minimum, maximum):
        valueRange = maximum - minimum
        if valueRange <= 1.0:
            step = valueRange / 10.0
            # round to 1 significant figure
            return round(step, -int(floor(log10(step))))
        else:
            return 1.0
Example #3
0
class DbItemObject(QObject):
    changed = pyqtSignal()
    aboutToChange = pyqtSignal()
    deleted = pyqtSignal()

    def __init__(self, parent=None):
        QObject.__init__(self, parent)

    def database(self):
        return None

    def refresh(self):
        self.changed.emit()  # refresh the item data reading them from the db

    def info(self):
        pass

    def runAction(self):
        pass

    def registerActions(self, mainWindow):
        pass
Example #4
0
class MultipleInputPanel(BASE, WIDGET):

    selectionChanged = pyqtSignal()

    def __init__(self, options=None, datatype=None):
        super(MultipleInputPanel, self).__init__(None)
        self.setupUi(self)

        self.leText.setEnabled(False)
        self.leText.setText(self.tr('0 elements selected'))

        self.btnSelect.clicked.connect(self.showSelectionDialog)

        self.options = options
        self.datatype = datatype
        self.selectedoptions = []

    def setSelectedItems(self, selected):
        # No checking is performed!
        self.selectedoptions = selected
        self.leText.setText(
            self.tr('%d elements selected') % len(self.selectedoptions))

    def showSelectionDialog(self):
        if self.datatype is None:
            dlg = MultipleInputDialog(self.options, self.selectedoptions)
        else:
            dlg = MultipleFileInputDialog(self.selectedoptions)
        dlg.exec_()
        if dlg.selectedoptions is not None:
            self.selectedoptions = dlg.selectedoptions
            self.leText.setText(
                self.tr('%d elements selected') % len(self.selectedoptions))
            self.selectionChanged.emit()

    def updateForOptions(self, options):
        selectedoptions = []
        selected = [self.options[i] for i in self.selectedoptions]
        for sel in selected:
            try:
                idx = options.index(sel)
                selectedoptions.append(idx)
            except ValueError:
                pass
        self.options = options
        self.setSelectedItems(selectedoptions)
Example #5
0
class Relay(QObject):
    """ Relay object for transmitting signals from QPHttp with adding the repoName information """
    # ----------------------------------------- #
    anythingChanged = pyqtSignal(unicode, int, int)

    def __init__(self, key):
        QObject.__init__(self)
        self.key = key

    def stateChanged(self, state):
        self.anythingChanged.emit(self.key, state, 0)

    # ----------------------------------------- #
    def dataReadProgress(self, done, total):
        state = 4
        if total > 0:
            progress = int(float(done) / float(total) * 100)
        else:
            progress = 0
        self.anythingChanged.emit(self.key, state, progress)
Example #6
0
class TableFieldsDelegate(QItemDelegate):
    """ delegate with some special item editors """

    columnNameChanged = pyqtSignal()

    def __init__(self, field_types, parent=None):
        QItemDelegate.__init__(self, parent)
        self.fieldTypes = field_types

    def createEditor(self, parent, option, index):
        # special combobox for field type
        if index.column() == 1:
            cbo = QComboBox(parent)
            cbo.setEditable(True)
            cbo.setAutoCompletion(True)
            cbo.setFrame(False)
            for item in self.fieldTypes:
                cbo.addItem(item)
            return cbo
        return QItemDelegate.createEditor(self, parent, option, index)

    def setEditorData(self, editor, index):
        """ load data from model to editor """
        m = index.model()
        if index.column() == 1:
            txt = m.data(index, Qt.DisplayRole)
            editor.setEditText(txt)
        else:
            # use default
            QItemDelegate.setEditorData(self, editor, index)

    def setModelData(self, editor, model, index):
        """ save data from editor back to model """
        if index.column() == 1:
            model.setData(index, editor.currentText())
        else:
            # use default
            QItemDelegate.setModelData(self, editor, model, index)
            if index.column() == 0:
                self.columnNameChanged.emit()
Example #7
0
class TreeItem(QObject):
    itemRemoved = pyqtSignal()
    itemChanged = pyqtSignal()

    def __init__(self, data, parent=None):
        QObject.__init__(self, parent)
        self.populated = False
        self.itemData = data
        self.childItems = []
        if parent:
            parent.appendChild(self)

    def childRemoved(self):
        self.itemWasChanged()

    def itemWasChanged(self):
        self.itemChanged.emit()

    def itemWasRemoved(self):
        self.itemRemoved.emit()

    def populate(self):
        self.populated = True
        return True

    def getItemData(self):
        return self.itemData

    def appendChild(self, child):
        self.childItems.append(child)
        child.itemRemoved.connect(self.childRemoved)

    def child(self, row):
        return self.childItems[row]

    def removeChild(self, row):
        if row >= 0 and row < len(self.childItems):
            self.childItems[row].itemData.deleteLater()
            self.childItems[row].itemRemoved.disconnect(self.childRemoved)
            del self.childItems[row]

    def childCount(self):
        return len(self.childItems)

    def columnCount(self):
        return 1

    def row(self):
        if self.parent():
            for row, item in enumerate(self.parent().childItems):
                if item is self:
                    return row
        return 0

    def data(self, column):
        return "" if column == 0 else None

    def icon(self):
        return None

    def path(self):
        pathList = []
        if self.parent():
            pathList.extend(self.parent().path())
        pathList.append(self.data(0))
        return pathList
Example #8
0
class DBModel(QAbstractItemModel):
    importVector = pyqtSignal(QgsVectorLayer, Database, QgsDataSourceURI, QModelIndex)
    notPopulated = pyqtSignal(QModelIndex)

    def __init__(self, parent=None):
        global isImportVectorAvail

        QAbstractItemModel.__init__(self, parent)
        self.treeView = parent
        self.header = [self.tr('Databases')]

        if isImportVectorAvail:
            self.importVector.connect(self.vectorImport)

        self.hasSpatialiteSupport = "spatialite" in supportedDbTypes()

        self.rootItem = TreeItem(None, None)
        for dbtype in supportedDbTypes():
            dbpluginclass = createDbPlugin(dbtype)
            item = PluginItem(dbpluginclass, self.rootItem)
            item.itemChanged.connect(self.refreshItem)

    def refreshItem(self, item):
        if isinstance(item, TreeItem):
            # find the index for the tree item using the path
            index = self._rPath2Index(item.path())
        else:
            # find the index for the db item
            index = self._rItem2Index(item)
        if index.isValid():
            self._refreshIndex(index)
        else:
            qDebug("invalid index")

    def _rItem2Index(self, item, parent=None):
        if parent is None:
            parent = QModelIndex()
        if item == self.getItem(parent):
            return parent

        if not parent.isValid() or parent.internalPointer().populated:
            for i in range(self.rowCount(parent)):
                index = self.index(i, 0, parent)
                index = self._rItem2Index(item, index)
                if index.isValid():
                    return index

        return QModelIndex()

    def _rPath2Index(self, path, parent=None, n=0):
        if parent is None:
            parent = QModelIndex()
        if path is None or len(path) == 0:
            return parent

        for i in range(self.rowCount(parent)):
            index = self.index(i, 0, parent)
            if self._getPath(index)[n] == path[0]:
                return self._rPath2Index(path[1:], index, n + 1)

        return parent

    def getItem(self, index):
        if not index.isValid():
            return None
        return index.internalPointer().getItemData()

    def _getPath(self, index):
        if not index.isValid():
            return None
        return index.internalPointer().path()

    def columnCount(self, parent):
        return 1

    def data(self, index, role):
        if not index.isValid():
            return None

        if role == Qt.DecorationRole and index.column() == 0:
            icon = index.internalPointer().icon()
            if icon:
                return icon

        if role != Qt.DisplayRole and role != Qt.EditRole:
            return None

        retval = index.internalPointer().data(index.column())
        return retval

    def flags(self, index):
        global isImportVectorAvail

        if not index.isValid():
            return Qt.NoItemFlags

        flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable

        if index.column() == 0:
            item = index.internalPointer()

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

            if isinstance(item, TableItem):
                flags |= Qt.ItemIsDragEnabled

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

                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):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole and section < len(self.header):
            return self.header[section]
        return None

    def index(self, row, column, parent):
        if not self.hasIndex(row, column, parent):
            return QModelIndex()

        parentItem = parent.internalPointer() if parent.isValid() else self.rootItem
        childItem = parentItem.child(row)
        if childItem:
            return self.createIndex(row, column, childItem)
        return QModelIndex()

    def parent(self, index):
        if not index.isValid():
            return QModelIndex()

        childItem = index.internalPointer()
        parentItem = childItem.parent()

        if parentItem == self.rootItem:
            return QModelIndex()

        return self.createIndex(parentItem.row(), 0, parentItem)

    def rowCount(self, parent):
        parentItem = parent.internalPointer() if parent.isValid() else self.rootItem
        if not parentItem.populated:
            self._refreshIndex(parent, True)
        return parentItem.childCount()

    def hasChildren(self, parent):
        parentItem = parent.internalPointer() if parent.isValid() else self.rootItem
        return parentItem.childCount() > 0 or not parentItem.populated

    def setData(self, index, value, role):
        if role != Qt.EditRole or index.column() != 0:
            return False

        item = index.internalPointer()
        new_value = unicode(value)

        if isinstance(item, SchemaItem) or isinstance(item, TableItem):
            obj = item.getItemData()

            # rename schema or table or view
            if new_value == obj.name:
                return False

            QApplication.setOverrideCursor(Qt.WaitCursor)
            try:
                obj.rename(new_value)
                self._onDataChanged(index)
            except BaseError as e:
                DlgDbError.showError(e, self.treeView)
                return False
            finally:
                QApplication.restoreOverrideCursor()

            return True

        return False

    def removeRows(self, row, count, parent):
        self.beginRemoveRows(parent, row, count + row - 1)
        item = parent.internalPointer()
        for i in range(row, count + row):
            item.removeChild(row)
        self.endRemoveRows()

    def _refreshIndex(self, index, force=False):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            item = index.internalPointer() if index.isValid() else self.rootItem
            prevPopulated = item.populated
            if prevPopulated:
                self.removeRows(0, self.rowCount(index), index)
                item.populated = False
            if prevPopulated or force:
                if item.populate():
                    for child in item.childItems:
                        child.itemChanged.connect(self.refreshItem)
                    self._onDataChanged(index)
                else:
                    self.notPopulated.emit(index)

        except BaseError:
            item.populated = False
            return

        finally:
            QApplication.restoreOverrideCursor()

    def _onDataChanged(self, indexFrom, indexTo=None):
        if indexTo is None:
            indexTo = indexFrom
        self.dataChanged.emit(indexFrom, indexTo)

    QGIS_URI_MIME = "application/x-vnd.qgis.qgis.uri"

    def mimeTypes(self):
        return ["text/uri-list", self.QGIS_URI_MIME]

    def mimeData(self, indexes):
        mimeData = QMimeData()
        encodedData = QByteArray()

        stream = QDataStream(encodedData, QIODevice.WriteOnly)

        for index in indexes:
            if not index.isValid():
                continue
            if not isinstance(index.internalPointer(), TableItem):
                continue
            table = self.getItem(index)
            stream.writeQString(table.mimeUri())

        mimeData.setData(self.QGIS_URI_MIME, encodedData)
        return mimeData

    def dropMimeData(self, data, action, row, column, parent):
        global isImportVectorAvail

        if action == Qt.IgnoreAction:
            return True

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

        added = 0

        if data.hasUrls():
            for u in data.urls():
                filename = u.toLocalFile()
                if filename == "":
                    continue

                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 = QgsDataSourceURI()
                        uri.setDatabase(filename)
                        item.getItemData().addConnection(conn_name, uri)
                        item.itemChanged.emit(item)
                        added += 1
                        continue

                if canImportLayer:
                    if 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 QgsMimeDataUtils.decodeUriList(data):
                if canImportLayer:
                    if self.importLayer(uri.layerType, uri.providerKey, uri.name, uri.uri, parent):
                        added += 1

        return added > 0

    def importLayer(self, layerType, providerKey, layerName, uriString, parent):
        global isImportVectorAvail

        if not isImportVectorAvail:
            return False

        if layerType == 'raster':
            return False  # not implemented yet
            inLayer = QgsRasterLayer(uriString, layerName, providerKey)
        else:
            inLayer = QgsVectorLayer(uriString, layerName, providerKey)

        if not inLayer.isValid():
            # invalid layer
            QMessageBox.warning(None, self.tr("Invalid layer"), self.tr("Unable to load the layer %s") % inLayer.name())
            return False

        # retrieve information about the new table's db and schema
        outItem = parent.internalPointer()
        outObj = outItem.getItemData()
        outDb = outObj.database()
        outSchema = None
        if isinstance(outItem, SchemaItem):
            outSchema = outObj
        elif isinstance(outItem, TableItem):
            outSchema = outObj.schema()

        # toIndex will point to the parent item of the new table
        toIndex = parent
        if isinstance(toIndex.internalPointer(), TableItem):
            toIndex = toIndex.parent()

        if inLayer.type() == inLayer.VectorLayer:
            # create the output uri
            schema = outSchema.name if outDb.schemas() is not None and outSchema is not None else ""
            pkCol = geomCol = ""

            # default pk and geom field name value
            if providerKey in ['postgres', 'spatialite']:
                inUri = QgsDataSourceURI(inLayer.source())
                pkCol = inUri.keyColumn()
                geomCol = inUri.geometryColumn()

            outUri = outDb.uri()
            outUri.setDataSource(schema, layerName, geomCol, "", pkCol)

            self.importVector.emit(inLayer, outDb, outUri, toIndex)
            return True

        return False

    def vectorImport(self, inLayer, outDb, outUri, parent):
        global isImportVectorAvail

        if not isImportVectorAvail:
            return False

        try:
            from .dlg_import_vector import DlgImportVector

            dlg = DlgImportVector(inLayer, outDb, outUri)
            QApplication.restoreOverrideCursor()
            if dlg.exec_():
                self._refreshIndex(parent)
        finally:
            inLayer.deleteLater()
Example #9
0
class DBPlugin(QObject):
    deleted = pyqtSignal()
    changed = pyqtSignal()
    aboutToChange = pyqtSignal()

    def __init__(self, conn_name, parent=None):
        QObject.__init__(self, parent)
        self.connName = conn_name
        self.db = None

    def __del__(self):
        pass  # print "DBPlugin.__del__", self.connName

    def connectionName(self):
        return self.connName

    def database(self):
        return self.db

    def info(self):
        from .info_model import DatabaseInfo

        return DatabaseInfo(None)

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

    def connectToUri(self, uri):
        self.db = self.databasesFactory(self, uri)
        if self.db:
            return True
        return False

    def reconnect(self):
        if self.db is not None:
            uri = self.db.uri()
            self.db.deleteLater()
            self.db = None
            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.deleted.emit()
        return True

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

    @classmethod
    def icon(self):
        return None

    @classmethod
    def typeName(self):
        # return the db typename (e.g. 'postgis')
        pass

    @classmethod
    def typeNameString(self):
        # return the db typename string (e.g. 'PostGIS')
        pass

    @classmethod
    def providerName(self):
        # return the provider's name (e.g. 'postgres')
        pass

    @classmethod
    def connectionSettingsKey(self):
        # return the key used to store the connections in settings
        pass

    @classmethod
    def connections(self):
        # get the list of connections
        conn_list = []
        settings = QSettings()
        settings.beginGroup(self.connectionSettingsKey())
        for name in settings.childGroups():
            conn_list.append(createDbPlugin(self.typeName(), name))
        settings.endGroup()
        return conn_list

    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()
Example #10
0
class RectangleMapTool(QgsMapToolEmitPoint):

    rectangleCreated = pyqtSignal()
    deactivated = pyqtSignal()

    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.rubberBand.setColor(QColor(255, 0, 0, 100))
        self.rubberBand.setWidth(2)

        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QGis.Polygon)

    def canvasPressEvent(self, e):
        self.startPoint = self.toMapCoordinates(e.pos())
        self.endPoint = self.startPoint
        self.isEmittingPoint = True

        self.showRect(self.startPoint, self.endPoint)

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False
        if self.rectangle() is not None:
            self.rectangleCreated.emit()

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = self.toMapCoordinates(e.pos())
        self.showRect(self.startPoint, self.endPoint)

    def showRect(self, startPoint, endPoint):
        self.rubberBand.reset(QGis.Polygon)
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return

        point1 = QgsPoint(startPoint.x(), startPoint.y())
        point2 = QgsPoint(startPoint.x(), endPoint.y())
        point3 = QgsPoint(endPoint.x(), endPoint.y())
        point4 = QgsPoint(endPoint.x(), startPoint.y())

        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        # True to update canvas
        self.rubberBand.addPoint(point4, True)
        self.rubberBand.show()

    def rectangle(self):
        if self.startPoint is None or self.endPoint is None:
            return None
        elif self.startPoint.x() == self.endPoint.x() or \
                self.startPoint.y() == self.endPoint.y():
            return None

        return QgsRectangle(self.startPoint, self.endPoint)

    def setRectangle(self, rect):
        if rect == self.rectangle():
            return False

        if rect is None:
            self.reset()
        else:
            self.startPoint = QgsPoint(rect.xMaximum(), rect.yMaximum())
            self.endPoint = QgsPoint(rect.xMinimum(), rect.yMinimum())
            self.showRect(self.startPoint, self.endPoint)
        return True

    def deactivate(self):
        QgsMapTool.deactivate(self)
        self.deactivated.emit()
Example #11
0
class GdalToolsExtentSelector(QWidget, Ui_ExtentSelector):
    selectionStarted = pyqtSignal()
    selectionStopped = pyqtSignal()
    selectionPaused = pyqtSignal()
    newExtentDefined = pyqtSignal()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.canvas = None
        self.tool = None
        self.previousMapTool = None
        self.isStarted = False

        self.setupUi(self)

        self.x1CoordEdit.textChanged.connect(self.coordsChanged)
        self.x2CoordEdit.textChanged.connect(self.coordsChanged)
        self.y1CoordEdit.textChanged.connect(self.coordsChanged)
        self.y2CoordEdit.textChanged.connect(self.coordsChanged)
        self.btnEnable.clicked.connect(self.start)

    def setCanvas(self, canvas):
        self.canvas = canvas
        self.tool = RectangleMapTool(self.canvas)
        self.previousMapTool = self.canvas.mapTool()
        self.tool.rectangleCreated.connect(self.fillCoords)
        self.tool.deactivated.connect(self.pause)

    def stop(self):
        if not self.isStarted:
            return
        self.isStarted = False
        self.btnEnable.setVisible(False)
        self.tool.reset()
        self.canvas.unsetMapTool(self.tool)
        if self.previousMapTool != self.tool:
            self.canvas.setMapTool(self.previousMapTool)
        #self.coordsChanged()
        self.selectionStopped.emit()

    def start(self):
        prevMapTool = self.canvas.mapTool()
        if prevMapTool != self.tool:
            self.previousMapTool = prevMapTool
        self.canvas.setMapTool(self.tool)
        self.isStarted = True
        self.btnEnable.setVisible(False)
        self.coordsChanged()
        self.selectionStarted.emit()

    def pause(self):
        if not self.isStarted:
            return

        self.btnEnable.setVisible(True)
        self.selectionPaused.emit()

    def setExtent(self, rect):
        if self.tool.setRectangle(rect):
            self.newExtentDefined.emit()

    def getExtent(self):
        return self.tool.rectangle()

    def isCoordsValid(self):
        try:
            QgsPoint(float(self.x1CoordEdit.text()),
                     float(self.y1CoordEdit.text()))
            QgsPoint(float(self.x2CoordEdit.text()),
                     float(self.y2CoordEdit.text()))
        except ValueError:
            return False

        return True

    def coordsChanged(self):
        rect = None
        if self.isCoordsValid():
            point1 = QgsPoint(float(self.x1CoordEdit.text()),
                              float(self.y1CoordEdit.text()))
            point2 = QgsPoint(float(self.x2CoordEdit.text()),
                              float(self.y2CoordEdit.text()))
            rect = QgsRectangle(point1, point2)

        self.setExtent(rect)

    def fillCoords(self):
        rect = self.getExtent()
        self.blockSignals(True)
        if rect is not None:
            self.x1CoordEdit.setText(unicode(rect.xMinimum()))
            self.x2CoordEdit.setText(unicode(rect.xMaximum()))
            self.y1CoordEdit.setText(unicode(rect.yMaximum()))
            self.y2CoordEdit.setText(unicode(rect.yMinimum()))
        else:
            self.x1CoordEdit.clear()
            self.x2CoordEdit.clear()
            self.y1CoordEdit.clear()
            self.y2CoordEdit.clear()
        self.blockSignals(False)
        self.newExtentDefined.emit()
Example #12
0
class GdalToolsInOutSelector(QWidget, Ui_GdalToolsInOutSelector):
    FILE = 0x1
    LAYER = 0x2
    MULTIFILE = 0x4  # NOT IMPLEMENTED YET

    FILE_LAYER = 0x1 | 0x2
    FILES = 0x1 | 0x4  # NOT IMPLEMENTED YET
    FILES_LAYER = 0x3 | 0x4  # NOT IMPLEMENTED YET

    selectClicked = pyqtSignal()
    filenameChanged = pyqtSignal()
    layerChanged = pyqtSignal()

    def __init__(self, parent=None, type=None):
        QWidget.__init__(self, parent)

        self.setupUi(self)
        self.setFocusPolicy(Qt.StrongFocus)
        self.combo.setInsertPolicy(QComboBox.NoInsert)

        self.clear()

        self.typ = None
        if type is None:
            self.resetType()
        else:
            self.setType(type)

        self.selectBtn.clicked.connect(self.selectClicked)
        self.fileEdit.textChanged.connect(self.textChanged)
        self.combo.editTextChanged.connect(self.textChanged)
        self.combo.currentIndexChanged.connect(self.indexChanged)

    def clear(self):
        self.filenames = []
        self.fileEdit.clear()
        self.clearComboState()
        self.combo.clear()

    def textChanged(self):
        if self.getType() & self.MULTIFILE:
            self.filenames = self.fileEdit.text().split(",")
        if self.getType() & self.LAYER:
            index = self.combo.currentIndex()
            if index >= 0:
                text = self.combo.currentText()
                if text != self.combo.itemText(index):
                    return self.setFilename(text)
        self.filenameChanged.emit()

    def indexChanged(self):
        self.layerChanged.emit()
        self.filenameChanged.emit()

    def setType(self, type):
        if type == self.typ:
            return

        if type & self.MULTIFILE:  # MULTITYPE IS NOT IMPLEMENTED YET
            type = type & ~self.MULTIFILE

        self.typ = type

        self.selectBtn.setVisible(self.getType() & self.FILE)
        self.combo.setVisible(self.getType() & self.LAYER)
        self.fileEdit.setVisible(not (self.getType() & self.LAYER))
        self.combo.setEditable(self.getType() & self.FILE)

        if self.getType() & self.FILE:
            self.setFocusProxy(self.selectBtn)
        else:
            self.setFocusProxy(self.combo)

        # send signals to refresh connected widgets
        self.filenameChanged.emit()
        self.layerChanged.emit()

    def getType(self):
        return self.typ

    def resetType(self):
        self.setType(self.FILE_LAYER)

    selectorType = pyqtProperty("int", getType, setType, resetType)

    def setFilename(self, fn=None):
        self.blockSignals(True)
        prevFn, prevLayer = self.filename(), self.layer()

        if isinstance(fn, QgsMapLayer):
            fn = fn.source()

        elif isinstance(fn, str) or isinstance(fn, unicode):
            fn = unicode(fn)

        # TODO test
        elif isinstance(fn, list):
            if len(fn) > 0:
                if self.getType() & self.MULTIFILE:
                    self.filenames = fn
                #fn = "".join( fn, "," )
                fn = ",".join(fn)
            else:
                fn = ''

        else:
            fn = ''

        if not (self.getType() & self.LAYER):
            self.fileEdit.setText(fn)
        else:
            self.combo.setCurrentIndex(-1)
            self.combo.setEditText(fn)

        self.blockSignals(False)
        if self.filename() != prevFn:
            self.filenameChanged.emit()
        if self.layer() != prevLayer:
            self.layerChanged.emit()

    def setLayer(self, layer=None):
        if not (self.getType() & self.LAYER):
            return self.setFilename(layer)

        self.blockSignals(True)
        prevFn, prevLayer = self.filename(), self.layer()

        if isinstance(layer, QgsMapLayer):
            if self.combo.findData(layer.id()) >= 0:
                index = self.combo.findData(layer.id())
                self.combo.setCurrentIndex(index)
            else:
                self.combo.setCurrentIndex(-1)
                self.combo.setEditText(layer.source())

        elif isinstance(layer,
                        int) and layer >= 0 and layer < self.combo.count():
            self.combo.setCurrentIndex(layer)

        else:
            self.combo.clearEditText()
            self.combo.setCurrentIndex(-1)

        self.blockSignals(False)
        if self.filename() != prevFn:
            self.filenameChanged.emit()
        if self.layer() != prevLayer:
            self.layerChanged.emit()

    def setLayers(self, layers=None):
        if layers is None or not hasattr(layers,
                                         '__iter__') or len(layers) <= 0:
            self.combo.clear()
            return

        self.blockSignals(True)
        prevFn, prevLayer = self.filename(), self.layer()
        self.saveComboState()

        self.combo.clear()
        for l in layers:
            self.combo.addItem(l.name(), l.id())

        self.restoreComboState()
        self.blockSignals(False)
        if self.filename() != prevFn:
            self.filenameChanged.emit()
        if self.layer() != prevLayer:
            self.layerChanged.emit()

    def clearComboState(self):
        self.prevState = None

    def saveComboState(self):
        index = self.combo.currentIndex()
        text = self.combo.currentText()
        layerID = self.combo.itemData(index) if index >= 0 else ""
        self.prevState = (index, text, layerID)

    def restoreComboState(self):
        if self.prevState is None:
            return
        index, text, layerID = self.prevState

        if index < 0:
            if text == '' and self.combo.count() > 0:
                index = 0

        elif self.combo.findData(layerID) < 0:
            index = -1
            text = ""

        else:
            index = self.combo.findData(layerID)

        self.combo.setCurrentIndex(index)
        if index >= 0:
            text = self.combo.itemText(index)
        self.combo.setEditText(text)

    def layer(self):
        if self.getType() != self.FILE and self.combo.currentIndex() >= 0:
            layerID = self.combo.itemData(self.combo.currentIndex())
            return QgsMapLayerRegistry.instance().mapLayer(layerID)
        return None

    def filename(self):
        if not (self.getType() & self.LAYER):
            if self.getType() & self.MULTIFILE:
                return self.filenames
            return self.fileEdit.text()

        if self.combo.currentIndex() < 0:
            if self.getType() & self.MULTIFILE:
                return self.filenames
            return self.combo.currentText()
        layer = self.layer()
        if layer is not None:
            return layer.source()

        return ''
Example #13
0
class GdalToolsBaseDialog(QDialog, Ui_Dialog):
    refreshArgs = pyqtSignal()
    okClicked = pyqtSignal()
    closeClicked = pyqtSignal()
    helpClicked = pyqtSignal()
    processError = pyqtSignal(QProcess.ProcessError)
    processFinished = pyqtSignal(int, QProcess.ExitStatus)
    finished = pyqtSignal(bool)
    valuesChanged = pyqtSignal(list)

    def __init__(self, parent, iface, pluginBase, pluginName, pluginCommand):
        QDialog.__init__(self, parent)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.iface = iface

        self.process = QProcess(self)
        Utils.setProcessEnvironment(self.process)
        self.process.error.connect(self.processError)
        self.process.finished.connect(self.processFinished)

        self.setupUi(self)
        self.arguments = []

        self.editCmdBtn.setIcon(QIcon(":/icons/edit.png"))
        self.editCmdBtn.toggled.connect(self.editCommand)
        self.resetCmdBtn.setIcon(QIcon(":/icons/reset.png"))
        self.resetCmdBtn.clicked.connect(self.resetCommand)
        self.editCommand(False)

        self.buttonBox.rejected.connect(self.reject)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.helpRequested.connect(self.help)

        self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True)

        self.plugin = pluginBase
        self.valuesChanged.connect(self.handleRefreshArgs)

        self.pluginLayout.addWidget(self.plugin)
        self.plugin.setFocus()

        self.setWindowTitle(pluginName)
        self.setPluginCommand(pluginCommand)

    def setPluginCommand(self, cmd):
        # on Windows replace the .py with .bat extension
        if platform.system() == "Windows" and cmd[-3:] == ".py":
            self.command = cmd[:-3] + ".bat"
        else:
            self.command = cmd

        if cmd[-3:] == ".py":
            self.helpFileName = cmd[:-3] + ".html"
        else:
            self.helpFileName = cmd + ".html"

    def editCommand(self, enabled):
        if not self.commandIsEnabled():
            return
        self.editCmdBtn.setChecked(enabled)
        self.resetCmdBtn.setEnabled(enabled)
        self.textEditCommand.setReadOnly(not enabled)
        self.controlsWidget.setEnabled(not enabled)
        self.refreshArgs.emit()

    def resetCommand(self):
        if not self.commandIsEditable():
            return
        self.refreshArgs.emit()

    def commandIsEditable(self):
        return self.commandIsEnabled() and self.editCmdBtn.isChecked()

    def setCommandViewerEnabled(self, enable):
        if not enable:
            self.editCommand(False)
        self.commandWidget.setEnabled(enable)

    def commandIsEnabled(self):
        return self.commandWidget.isEnabled()

    def reject(self):
        if self.process.state() != QProcess.NotRunning:
            ret = QMessageBox.warning(
                self, self.tr("Warning"),
                self.
                tr("The command is still running. \nDo you want terminate it anyway?"
                   ), QMessageBox.Yes | QMessageBox.No)
            if ret == QMessageBox.No:
                return

            self.process.error.disconnect(self.processError)
            self.process.finished.disconnect(self.processFinished)

        self.closeClicked.emit()

    def accept(self):
        self.okClicked.emit()

    def help(self):
        self.helpClicked.emit()

    # show the online tool documentation in the default browser
    def onHelp(self):
        helpPath = Utils.getHelpPath()
        if helpPath == '':
            url = QUrl("http://www.gdal.org/" + self.helpFileName)
        else:
            url = QUrl.fromLocalFile(helpPath + '/' + self.helpFileName)
        QDesktopServices.openUrl(url)

    # called when a value in the plugin widget interface changed
    def handleRefreshArgs(self, args):
        self.arguments = [unicode(a) for a in args]

        if not self.commandIsEnabled():
            self.textEditCommand.setPlainText(self.command)
        else:
            self.textEditCommand.setPlainText(
                self.command + " " + Utils.escapeAndJoin(self.arguments))

    # enables the OK button
    def enableRun(self, enable=True):
        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable)

    # start the command execution
    def onRun(self):
        self.enableRun(False)
        self.setCursor(Qt.WaitCursor)
        if not self.commandIsEditable():
            #print(self.command+' '+unicode(self.arguments))
            self.process.start(self.command, self.arguments,
                               QIODevice.ReadOnly)
        else:
            self.process.start(self.textEditCommand.toPlainText(),
                               QIODevice.ReadOnly)

    # stop the command execution
    def stop(self):
        self.enableRun(True)
        self.setCursor(Qt.ArrowCursor)
        self.process.kill()

    # called on closing the dialog, stop the process if it's running
    def onClosing(self):
        self.stop()
        QDialog.reject(self)

    # called if an error occurs when the command has not already finished, shows the occurred error message
    def onError(self, error):
        if error == QProcess.FailedToStart:
            msg = QCoreApplication.translate(
                "GdalTools",
                "The process failed to start. Either the invoked program is missing, or you may have insufficient permissions to invoke the program."
            )
        elif error == QProcess.Crashed:
            msg = QCoreApplication.translate(
                "GdalTools",
                "The process crashed some time after starting successfully.")
        else:
            msg = QCoreApplication.translate("GdalTools",
                                             "An unknown error occurred.")

        QErrorMessage(self).showMessage(msg)
        QApplication.processEvents()  # give the user chance to see the message

        self.stop()

    # called when the command finished its execution, shows an error message if there's one
    # and, if required, load the output file in canvas
    def onFinished(self, exitCode, status):
        if status == QProcess.CrashExit:
            self.stop()
            return

        if self.command.find("gdalinfo") != -1 and exitCode == 0:
            self.finished.emit(self.loadCheckBox.isChecked())
            self.stop()
            return

        # show the error message if there's one, otherwise show the process output message
        msg = unicode(self.process.readAllStandardError())
        if msg == '':
            outMessages = unicode(
                self.process.readAllStandardOutput()).splitlines()

            # make sure to not show the help
            for m in outMessages:
                m = string.strip(m)
                if m == '':
                    continue
                # TODO fix this
                #if m.contains( QRegExp( "^(?:[Uu]sage:\\s)?" + QRegExp.escape(self.command) + "\\s" ) ):
                #  if msg.isEmpty():
                #    msg = self.tr ( "Invalid parameters." )
                #  break
                #if m.contains( QRegExp( "0(?:\\.+[1-9]0{1,2})+" ) ):
                #  continue

                if msg:
                    msg += "\n"
                msg += m

        QErrorMessage(self).showMessage(msg.replace("\n", "<br>"))

        if exitCode == 0:
            self.finished.emit(self.loadCheckBox.isChecked())

        self.stop()
Example #14
0
class DlgSqlWindow(QWidget, Ui_Dialog):
    nameChanged = pyqtSignal(str)

    def __init__(self, iface, db, parent=None):
        QWidget.__init__(self, parent)
        self.iface = iface
        self.db = db
        self.filter = ""
        self.allowMultiColumnPk = isinstance(
            db, PGDatabase
        )  # at the moment only PostgreSQL allows a primary key to span multiple columns, spatialite doesn't
        self.aliasSubQuery = isinstance(
            db,
            PGDatabase)  # only PostgreSQL requires subqueries to be aliases
        self.setupUi(self)
        self.setWindowTitle(
            u"%s - %s [%s]" %
            (self.windowTitle(), db.connection().connectionName(),
             db.connection().typeNameString()))

        self.defaultLayerName = 'QueryLayer'

        if self.allowMultiColumnPk:
            self.uniqueColumnCheck.setText(
                self.trUtf8("Column(s) with unique values"))
        else:
            self.uniqueColumnCheck.setText(
                self.trUtf8("Column with unique values"))

        self.editSql.setFocus()
        self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.initCompleter()

        # allow copying results
        copyAction = QAction("copy", self)
        self.viewResult.addAction(copyAction)
        copyAction.setShortcuts(QKeySequence.Copy)

        copyAction.triggered.connect(self.copySelectedResults)

        self.btnExecute.clicked.connect(self.executeSql)
        self.btnSetFilter.clicked.connect(self.setFilter)
        self.btnClear.clicked.connect(self.clearSql)

        self.presetStore.clicked.connect(self.storePreset)
        self.presetDelete.clicked.connect(self.deletePreset)
        self.presetCombo.activated[str].connect(self.loadPreset)
        self.presetCombo.activated[str].connect(self.presetName.setText)

        self.updatePresetsCombobox()

        self.geomCombo.setEditable(True)
        self.geomCombo.lineEdit().setReadOnly(True)

        self.uniqueCombo.setEditable(True)
        self.uniqueCombo.lineEdit().setReadOnly(True)
        self.uniqueModel = QStandardItemModel(self.uniqueCombo)
        self.uniqueCombo.setModel(self.uniqueModel)
        if self.allowMultiColumnPk:
            self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
            self.uniqueModel.itemChanged.connect(
                self.uniqueChanged)  # react to the (un)checking of an item
            self.uniqueCombo.lineEdit().textChanged.connect(
                self.uniqueTextChanged
            )  # there are other events that change the displayed text and some of them can not be caught directly

        # hide the load query as layer if feature is not supported
        self._loadAsLayerAvailable = self.db.connector.hasCustomQuerySupport()
        self.loadAsLayerGroup.setVisible(self._loadAsLayerAvailable)
        if self._loadAsLayerAvailable:
            self.layerTypeWidget.hide()  # show if load as raster is supported
            self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
            self.getColumnsBtn.clicked.connect(self.fillColumnCombos)
            self.loadAsLayerGroup.toggled.connect(self.loadAsLayerToggled)
            self.loadAsLayerToggled(False)

        self._createViewAvailable = self.db.connector.hasCreateSpatialViewSupport(
        )
        self.btnCreateView.setVisible(self._createViewAvailable)
        if self._createViewAvailable:
            self.btnCreateView.clicked.connect(self.createView)

        self.queryBuilderFirst = True
        self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif"))
        self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder)

        self.presetName.textChanged.connect(self.nameChanged)

    def updatePresetsCombobox(self):
        self.presetCombo.clear()

        names = []
        entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
        for entry in entries:
            name = QgsProject.instance().readEntry(
                'DBManager', 'savedQueries/' + entry + '/name')[0]
            names.append(name)

        for name in sorted(names):
            self.presetCombo.addItem(name)
        self.presetCombo.setCurrentIndex(-1)

    def storePreset(self):
        query = self._getSqlQuery()
        if query == "":
            return
        name = self.presetName.text()
        QgsProject.instance().writeEntry(
            'DBManager', 'savedQueries/q' + unicode(name.__hash__()) + '/name',
            name)
        QgsProject.instance().writeEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/query', query)
        index = self.presetCombo.findText(name)
        if index == -1:
            self.presetCombo.addItem(name)
            self.presetCombo.setCurrentIndex(self.presetCombo.count() - 1)
        else:
            self.presetCombo.setCurrentIndex(index)

    def deletePreset(self):
        name = self.presetCombo.currentText()
        QgsProject.instance().removeEntry(
            'DBManager', 'savedQueries/q' + unicode(name.__hash__()))
        self.presetCombo.removeItem(self.presetCombo.findText(name))
        self.presetCombo.setCurrentIndex(-1)

    def loadPreset(self, name):
        query = QgsProject.instance().readEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/query')[0]
        name = QgsProject.instance().readEntry(
            'DBManager',
            'savedQueries/q' + unicode(name.__hash__()) + '/name')[0]
        self.editSql.setText(query)

    def loadAsLayerToggled(self, checked):
        self.loadAsLayerGroup.setChecked(checked)
        self.loadAsLayerWidget.setVisible(checked)
        if checked:
            self.fillColumnCombos()

    def clearSql(self):
        self.editSql.clear()
        self.editSql.setFocus()
        self.filter = ""

    def executeSql(self):

        sql = self._getSqlQuery()
        if sql == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # delete the old model
        old_model = self.viewResult.model()
        self.viewResult.setModel(None)
        if old_model:
            old_model.deleteLater()

        cols = []
        quotedCols = []

        try:
            # set the new model
            model = self.db.sqlResultModel(sql, self)
            self.viewResult.setModel(model)
            self.lblResult.setText(
                self.tr("%d rows, %.1f seconds") %
                (model.affectedRows(), model.secs()))
            cols = self.viewResult.model().columnNames()
            for col in cols:
                quotedCols.append(self.db.connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        self.setColumnCombos(cols, quotedCols)

        self.update()
        QApplication.restoreOverrideCursor()

    def _getSqlLayer(self, _filter):
        hasUniqueField = self.uniqueColumnCheck.checkState() == Qt.Checked
        if hasUniqueField:
            if self.allowMultiColumnPk:
                checkedCols = []
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.checkState() == Qt.Checked:
                        checkedCols.append(item.data())
                uniqueFieldName = ",".join(checkedCols)
            elif self.uniqueCombo.currentIndex() >= 0:
                uniqueFieldName = self.uniqueModel.item(
                    self.uniqueCombo.currentIndex()).data()
            else:
                uniqueFieldName = None
        else:
            uniqueFieldName = None
        hasGeomCol = self.hasGeometryCol.checkState() == Qt.Checked
        if hasGeomCol:
            geomFieldName = self.geomCombo.currentText()
        else:
            geomFieldName = None

        query = self._getSqlQuery()
        if query == "":
            return None

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        from qgis.core import QgsMapLayer, QgsMapLayerRegistry

        layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked(
        ) else QgsMapLayer.RasterLayer

        # get a new layer name
        names = []
        for layer in list(QgsMapLayerRegistry.instance().mapLayers().values()):
            names.append(layer.name())

        layerName = self.layerNameEdit.text()
        if layerName == "":
            layerName = self.defaultLayerName
        newLayerName = layerName
        index = 1
        while newLayerName in names:
            index += 1
            newLayerName = u"%s_%d" % (layerName, index)

        # create the layer
        layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName,
                                   newLayerName, layerType,
                                   self.avoidSelectById.isChecked(), _filter)
        if layer.isValid():
            return layer
        else:
            return None

    def loadSqlLayer(self):
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            layer = self._getSqlLayer(self.filter)
            if layer is None:
                return

            from qgis.core import QgsMapLayerRegistry
            QgsMapLayerRegistry.instance().addMapLayers([layer], True)
        finally:
            QApplication.restoreOverrideCursor()

    def fillColumnCombos(self):
        query = self._getSqlQuery()
        if query == "":
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        # get all the columns
        cols = []
        quotedCols = []
        connector = self.db.connector
        if self.aliasSubQuery:
            # get a new alias
            aliasIndex = 0
            while True:
                alias = "_subQuery__%d" % aliasIndex
                escaped = re.compile('\\b("?)' + re.escape(alias) + '\\1\\b')
                if not escaped.search(query):
                    break
                aliasIndex += 1

            sql = u"SELECT * FROM (%s\n) AS %s LIMIT 0" % (
                unicode(query), connector.quoteId(alias))
        else:
            sql = u"SELECT * FROM (%s\n) WHERE 1=0" % unicode(query)

        c = None
        try:
            c = connector._execute(None, sql)
            cols = connector._get_cursor_columns(c)
            for col in cols:
                quotedCols.append(connector.quoteId(col))

        except BaseError as e:
            QApplication.restoreOverrideCursor()
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

        finally:
            if c:
                c.close()
                del c

        self.setColumnCombos(cols, quotedCols)

        QApplication.restoreOverrideCursor()

    def setColumnCombos(self, cols, quotedCols):
        # get sensible default columns. do this before sorting in case there's hints in the column order (eg, id is more likely to be first)
        try:
            defaultGeomCol = next(
                col for col in cols
                if col in ['geom', 'geometry', 'the_geom', 'way'])
        except:
            defaultGeomCol = None
        try:
            defaultUniqueCol = [col for col in cols if 'id' in col][0]
        except:
            defaultUniqueCol = None

        colNames = sorted(zip(cols, quotedCols))
        newItems = []
        uniqueIsFilled = False
        for (col, quotedCol) in colNames:
            item = QStandardItem(col)
            item.setData(quotedCol)
            item.setEnabled(True)
            item.setCheckable(self.allowMultiColumnPk)
            item.setSelectable(not self.allowMultiColumnPk)
            if self.allowMultiColumnPk:
                matchingItems = self.uniqueModel.findItems(col)
                if matchingItems:
                    item.setCheckState(matchingItems[0].checkState())
                    uniqueIsFilled = uniqueIsFilled or matchingItems[
                        0].checkState() == Qt.Checked
                else:
                    item.setCheckState(Qt.Unchecked)
            newItems.append(item)
        if self.allowMultiColumnPk:
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            self.uniqueChanged()
        else:
            previousUniqueColumn = self.uniqueCombo.currentText()
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            if self.uniqueModel.findItems(previousUniqueColumn):
                self.uniqueCombo.setEditText(previousUniqueColumn)
                uniqueIsFilled = True

        oldGeometryColumn = self.geomCombo.currentText()
        self.geomCombo.clear()
        self.geomCombo.addItems(cols)
        self.geomCombo.setCurrentIndex(
            self.geomCombo.findText(oldGeometryColumn, Qt.MatchExactly))

        # set sensible default columns if the columns are not already set
        try:
            if self.geomCombo.currentIndex() == -1:
                self.geomCombo.setCurrentIndex(cols.index(defaultGeomCol))
        except:
            pass
        items = self.uniqueModel.findItems(defaultUniqueCol)
        if items and not uniqueIsFilled:
            if self.allowMultiColumnPk:
                items[0].setCheckState(Qt.Checked)
            else:
                self.uniqueCombo.setEditText(defaultUniqueCol)
        try:
            pass
        except:
            pass

    def copySelectedResults(self):
        if len(self.viewResult.selectedIndexes()) <= 0:
            return
        model = self.viewResult.model()

        # convert to string using tab as separator
        text = model.headerToString("\t")
        for idx in self.viewResult.selectionModel().selectedRows():
            text += "\n" + model.rowToString(idx.row(), "\t")

        QApplication.clipboard().setText(text, QClipboard.Selection)
        QApplication.clipboard().setText(text, QClipboard.Clipboard)

    def initCompleter(self):
        dictionary = None
        if self.db:
            dictionary = self.db.connector.getSqlDictionary()
        if not dictionary:
            # use the generic sql dictionary
            from .sql_dictionary import getSqlDictionary

            dictionary = getSqlDictionary()

        wordlist = []
        for name, value in dictionary.items():
            wordlist += value  # concat lists
        wordlist = list(set(wordlist))  # remove duplicates

        api = QsciAPIs(self.editSql.lexer())
        for word in wordlist:
            api.add(word)

        api.prepare()
        self.editSql.lexer().setAPIs(api)

    def displayQueryBuilder(self):
        dlg = QueryBuilderDlg(self.iface,
                              self.db,
                              self,
                              reset=self.queryBuilderFirst)
        self.queryBuilderFirst = False
        r = dlg.exec_()
        if r == QDialog.Accepted:
            self.editSql.setText(dlg.query)

    def createView(self):
        name, ok = QInputDialog.getText(None, "View name", "View name")
        if ok:
            try:
                self.db.connector.createSpatialView(name, self._getSqlQuery())
            except BaseError as e:
                DlgDbError.showError(e, self)

    def _getSqlQuery(self):
        sql = self.editSql.selectedText()
        if len(sql) == 0:
            sql = self.editSql.text()
        return sql

    def uniqueChanged(self):
        # when an item is (un)checked, simply trigger an update of the combobox text
        self.uniqueTextChanged(None)

    def uniqueTextChanged(self, text):
        # Whenever there is new text displayed in the combobox, check if it is the correct one and if not, display the correct one.
        checkedItems = []
        for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
            if item.checkState() == Qt.Checked:
                checkedItems.append(item.text())
        label = ", ".join(checkedItems)
        if text != label:
            self.uniqueCombo.setEditText(label)

    def setFilter(self):
        from qgis.gui import QgsQueryBuilder
        layer = self._getSqlLayer("")
        if not layer:
            return

        dlg = QgsQueryBuilder(layer)
        dlg.setSql(self.filter)
        if dlg.exec_():
            self.filter = dlg.sql()
        layer.deleteLater()
Example #15
0
class LayerRegistry(QObject):
    layersChanged = pyqtSignal()

    _instance = None
    _iface = None

    @staticmethod
    def instance():
        if LayerRegistry._instance is None:
            LayerRegistry._instance = LayerRegistry()
        return LayerRegistry._instance

    @staticmethod
    def setIface(iface):
        LayerRegistry._iface = iface

    layers = []

    def __init__(self):
        QObject.__init__(self)
        if LayerRegistry._instance is not None:
            return

        LayerRegistry.layers = self.getAllLayers()
        LayerRegistry._instance = self
        QgsMapLayerRegistry.instance().removeAll.connect(self.removeAllLayers)
        QgsMapLayerRegistry.instance().layerWasAdded.connect(self.layerAdded)
        QgsMapLayerRegistry.instance().layerWillBeRemoved.connect(
            self.removeLayer)

    def getAllLayers(self):
        if LayerRegistry._iface and hasattr(LayerRegistry._iface,
                                            'legendInterface'):
            return LayerRegistry._iface.legendInterface().layers()
        return QgsMapLayerRegistry.instance().mapLayers().values()

    def layerAdded(self, layer):
        LayerRegistry.layers.append(layer)
        self.layersChanged.emit()

    def removeLayer(self, layerId):
        LayerRegistry.layers = [
            x for x in LayerRegistry.layers if x.id() != layerId
        ]
        self.layersChanged.emit()

    def removeAllLayers(self):
        LayerRegistry.layers = []
        self.layersChanged.emit()

    @classmethod
    def isRaster(self, layer):
        # only gdal raster layers
        if layer.type() != layer.RasterLayer:
            return False
        if layer.providerType() != 'gdal':
            return False
        return True

    def getRasterLayers(self):
        return filter(self.isRaster, LayerRegistry.layers)

    @classmethod
    def isVector(self, layer):
        if layer.type() != layer.VectorLayer:
            return False
        if layer.providerType() != 'ogr':
            return False
        return True

    def getVectorLayers(self):
        return filter(self.isVector, LayerRegistry.layers)
Example #16
0
class Repositories(QObject):
    """ A dict-like class for handling repositories data """
    # ----------------------------------------- #

    anythingChanged = pyqtSignal(unicode, int, int)
    repositoryFetched = pyqtSignal(unicode)
    checkingDone = pyqtSignal()

    def __init__(self):
        QObject.__init__(self)
        self.mRepositories = {}
        self.httpId = {}  # {httpId : repoName}
        self.mInspectionFilter = None

    # ----------------------------------------- #
    def all(self):
        """ return dict of all repositories """
        return self.mRepositories

    # ----------------------------------------- #
    def allEnabled(self):
        """ return dict of all enabled and valid repositories """
        if self.mInspectionFilter:
            return {
                self.mInspectionFilter:
                self.mRepositories[self.mInspectionFilter]
            }

        repos = {}
        for i in self.mRepositories:
            if self.mRepositories[i]["enabled"] and self.mRepositories[i][
                    "valid"]:
                repos[i] = self.mRepositories[i]
        return repos

    # ----------------------------------------- #
    def allUnavailable(self):
        """ return dict of all unavailable repositories """
        repos = {}

        if self.mInspectionFilter:
            # return the inspected repo if unavailable, otherwise empty dict
            if self.mRepositories[self.mInspectionFilter]["state"] == 3:
                repos[self.mInspectionFilter] = self.mRepositories[
                    self.mInspectionFilter]
            return repos

        for i in self.mRepositories:
            if self.mRepositories[i]["enabled"] and self.mRepositories[i][
                    "valid"] and self.mRepositories[i]["state"] == 3:
                repos[i] = self.mRepositories[i]
        return repos

    # ----------------------------------------- #
    def urlParams(self):
        """ return GET parameters to be added to every request """
        v = str(QGis.QGIS_VERSION_INT)
        return "?qgis=%d.%d" % (int(v[0]), int(v[1:3]))

    # ----------------------------------------- #
    def setRepositoryData(self, reposName, key, value):
        """ write data to the mRepositories dict """
        self.mRepositories[reposName][key] = value

    # ----------------------------------------- #
    def remove(self, reposName):
        """ remove given item from the mRepositories dict """
        del self.mRepositories[reposName]

    # ----------------------------------------- #
    def rename(self, oldName, newName):
        """ rename repository key """
        if oldName == newName:
            return
        self.mRepositories[newName] = self.mRepositories[oldName]
        del self.mRepositories[oldName]

    # ----------------------------------------- #
    def checkingOnStart(self):
        """ return true if checking for news and updates is enabled """
        settings = QSettings()
        return settings.value(settingsGroup + "/checkOnStart",
                              False,
                              type=bool)

    # ----------------------------------------- #
    def setCheckingOnStart(self, state):
        """ set state of checking for news and updates """
        settings = QSettings()
        settings.setValue(settingsGroup + "/checkOnStart", state)

    # ----------------------------------------- #
    def checkingOnStartInterval(self):
        """ return checking for news and updates interval """
        settings = QSettings()
        try:
            # QSettings may contain non-int value...
            i = settings.value(settingsGroup + "/checkOnStartInterval",
                               1,
                               type=int)
        except:
            # fallback do 1 day by default
            i = 1
        if i < 0:
            i = 1
        # allowed values: 0,1,3,7,14,30 days
        interval = 0
        for j in [1, 3, 7, 14, 30]:
            if i >= j:
                interval = j
        return interval

    # ----------------------------------------- #
    def setCheckingOnStartInterval(self, interval):
        """ set checking for news and updates interval """
        settings = QSettings()
        settings.setValue(settingsGroup + "/checkOnStartInterval", interval)

    # ----------------------------------------- #
    def saveCheckingOnStartLastDate(self):
        """ set today's date as the day of last checking  """
        settings = QSettings()
        settings.setValue(settingsGroup + "/checkOnStartLastDate",
                          QDate.currentDate())

    # ----------------------------------------- #
    def timeForChecking(self):
        """ determine whether it's the time for checking for news and updates now """
        if self.checkingOnStartInterval() == 0:
            return True
        settings = QSettings()
        try:
            # QSettings may contain ivalid value...
            interval = settings.value(settingsGroup + "/checkOnStartLastDate",
                                      type=QDate).daysTo(QDate.currentDate())
        except:
            interval = 0
        if interval >= self.checkingOnStartInterval():
            return True
        else:
            return False

    # ----------------------------------------- #
    def load(self):
        """ populate the mRepositories dict"""
        self.mRepositories = {}
        settings = QSettings()
        settings.beginGroup(reposGroup)
        # first, update repositories in QSettings if needed
        officialRepoPresent = False
        for key in settings.childGroups():
            url = settings.value(key + "/url", "", type=unicode)
            if url == officialRepo[1]:
                officialRepoPresent = True
            if url == officialRepo[2]:
                settings.setValue(key + "/url",
                                  officialRepo[1])  # correct a depreciated url
                officialRepoPresent = True
        if not officialRepoPresent:
            settings.setValue(officialRepo[0] + "/url", officialRepo[1])

        for key in settings.childGroups():
            self.mRepositories[key] = {}
            self.mRepositories[key]["url"] = settings.value(key + "/url",
                                                            "",
                                                            type=unicode)
            self.mRepositories[key]["authcfg"] = settings.value(key +
                                                                "/authcfg",
                                                                "",
                                                                type=unicode)
            self.mRepositories[key]["enabled"] = settings.value(key +
                                                                "/enabled",
                                                                True,
                                                                type=bool)
            self.mRepositories[key]["valid"] = settings.value(key + "/valid",
                                                              True,
                                                              type=bool)
            self.mRepositories[key]["Relay"] = Relay(key)
            self.mRepositories[key]["xmlData"] = None
            self.mRepositories[key]["state"] = 0
            self.mRepositories[key]["error"] = ""
        settings.endGroup()

    # ----------------------------------------- #
    def requestFetching(self, key):
        """ start fetching the repository given by key """
        self.mRepositories[key]["state"] = 1
        url = QUrl(self.mRepositories[key]["url"] + self.urlParams())
        #v=str(QGis.QGIS_VERSION_INT)
        #url.addQueryItem('qgis', '.'.join([str(int(s)) for s in [v[0], v[1:3]]]) ) # don't include the bugfix version!

        self.mRepositories[key]["QRequest"] = QNetworkRequest(url)
        authcfg = self.mRepositories[key]["authcfg"]
        if authcfg and isinstance(authcfg, basestring):
            if not QgsAuthManager.instance().updateNetworkRequest(
                    self.mRepositories[key]["QRequest"], authcfg.strip()):
                msg = QCoreApplication.translate(
                    "QgsPluginInstaller",
                    "Update of network request with authentication "
                    "credentials FAILED for configuration '{0}'").format(
                        authcfg)
                iface.pluginManagerInterface().pushMessage(
                    msg, QgsMessageBar.WARNING)
                self.mRepositories[key]["QRequest"] = None
                return
        self.mRepositories[key]["QRequest"].setAttribute(
            QNetworkRequest.User, key)
        self.mRepositories[key]["xmlData"] = QgsNetworkAccessManager.instance(
        ).get(self.mRepositories[key]["QRequest"])
        self.mRepositories[key]["xmlData"].setProperty('reposName', key)
        self.mRepositories[key]["xmlData"].downloadProgress.connect(
            self.mRepositories[key]["Relay"].dataReadProgress)
        self.mRepositories[key]["xmlData"].finished.connect(self.xmlDownloaded)

    # ----------------------------------------- #
    def fetchingInProgress(self):
        """ return true if fetching repositories is still in progress """
        for key in self.mRepositories:
            if self.mRepositories[key]["state"] == 1:
                return True
        return False

    # ----------------------------------------- #
    def killConnection(self, key):
        """ kill the fetching on demand """
        if self.mRepositories[key]["state"] == 1 and self.mRepositories[key][
                "xmlData"] and self.mRepositories[key]["xmlData"].isRunning():
            self.mRepositories[key]["xmlData"].finished.disconnect()
            self.mRepositories[key]["xmlData"].abort()

    # ----------------------------------------- #
    def xmlDownloaded(self):
        """ populate the plugins object with the fetched data """
        reply = self.sender()
        reposName = reply.property('reposName')
        if reply.error() != QNetworkReply.NoError:  # fetching failed
            self.mRepositories[reposName]["state"] = 3
            self.mRepositories[reposName]["error"] = reply.errorString()
            if reply.error() == QNetworkReply.OperationCanceledError:
                self.mRepositories[reposName][
                    "error"] += "\n\n" + QCoreApplication.translate(
                        "QgsPluginInstaller",
                        "If you haven't cancelled the download manually, it was most likely caused by a timeout. In this case consider increasing the connection timeout value in QGIS options window."
                    )
        else:
            reposXML = QDomDocument()
            content = reply.readAll()
            # Fix lonely ampersands in metadata
            a = QByteArray()
            a.append("& ")
            b = QByteArray()
            b.append("&amp; ")
            content = content.replace(a, b)
            reposXML.setContent(content)
            pluginNodes = reposXML.elementsByTagName("pyqgis_plugin")
            if pluginNodes.size():
                for i in range(pluginNodes.size()):
                    fileName = pluginNodes.item(i).firstChildElement(
                        "file_name").text().strip()
                    if not fileName:
                        fileName = QFileInfo(
                            pluginNodes.item(i).firstChildElement(
                                "download_url").text().strip().split("?")
                            [0]).fileName()
                    name = fileName.partition(".")[0]
                    experimental = False
                    if pluginNodes.item(i).firstChildElement(
                            "experimental").text().strip().upper() in [
                                "TRUE", "YES"
                            ]:
                        experimental = True
                    deprecated = False
                    if pluginNodes.item(i).firstChildElement(
                            "deprecated").text().strip().upper() in [
                                "TRUE", "YES"
                            ]:
                        deprecated = True
                    icon = pluginNodes.item(i).firstChildElement(
                        "icon").text().strip()
                    if icon and not icon.startswith("http"):
                        icon = "http://%s/%s" % (QUrl(
                            self.mRepositories[reposName]["url"]).host(), icon)

                    if pluginNodes.item(i).toElement().hasAttribute(
                            "plugin_id"):
                        plugin_id = pluginNodes.item(i).toElement().attribute(
                            "plugin_id")
                    else:
                        plugin_id = None

                    plugin = {
                        "id":
                        name,
                        "plugin_id":
                        plugin_id,
                        "name":
                        pluginNodes.item(i).toElement().attribute("name"),
                        "version_available":
                        pluginNodes.item(i).toElement().attribute("version"),
                        "description":
                        pluginNodes.item(i).firstChildElement(
                            "description").text().strip(),
                        "about":
                        pluginNodes.item(i).firstChildElement(
                            "about").text().strip(),
                        "author_name":
                        pluginNodes.item(i).firstChildElement(
                            "author_name").text().strip(),
                        "homepage":
                        pluginNodes.item(i).firstChildElement(
                            "homepage").text().strip(),
                        "download_url":
                        pluginNodes.item(i).firstChildElement(
                            "download_url").text().strip(),
                        "category":
                        pluginNodes.item(i).firstChildElement(
                            "category").text().strip(),
                        "tags":
                        pluginNodes.item(i).firstChildElement(
                            "tags").text().strip(),
                        "changelog":
                        pluginNodes.item(i).firstChildElement(
                            "changelog").text().strip(),
                        "author_email":
                        pluginNodes.item(i).firstChildElement(
                            "author_email").text().strip(),
                        "tracker":
                        pluginNodes.item(i).firstChildElement(
                            "tracker").text().strip(),
                        "code_repository":
                        pluginNodes.item(i).firstChildElement(
                            "repository").text().strip(),
                        "downloads":
                        pluginNodes.item(i).firstChildElement(
                            "downloads").text().strip(),
                        "average_vote":
                        pluginNodes.item(i).firstChildElement(
                            "average_vote").text().strip(),
                        "rating_votes":
                        pluginNodes.item(i).firstChildElement(
                            "rating_votes").text().strip(),
                        "icon":
                        icon,
                        "experimental":
                        experimental,
                        "deprecated":
                        deprecated,
                        "filename":
                        fileName,
                        "installed":
                        False,
                        "available":
                        True,
                        "status":
                        "not installed",
                        "error":
                        "",
                        "error_details":
                        "",
                        "version_installed":
                        "",
                        "zip_repository":
                        reposName,
                        "library":
                        "",
                        "readonly":
                        False
                    }
                    qgisMinimumVersion = pluginNodes.item(i).firstChildElement(
                        "qgis_minimum_version").text().strip()
                    if not qgisMinimumVersion:
                        qgisMinimumVersion = "2"
                    qgisMaximumVersion = pluginNodes.item(i).firstChildElement(
                        "qgis_maximum_version").text().strip()
                    if not qgisMaximumVersion:
                        qgisMaximumVersion = qgisMinimumVersion[0] + ".99"
                    #if compatible, add the plugin to the list
                    if not pluginNodes.item(i).firstChildElement(
                            "disabled").text().strip().upper() in [
                                "TRUE", "YES"
                            ]:
                        if isCompatible(QGis.QGIS_VERSION, qgisMinimumVersion,
                                        qgisMaximumVersion):
                            #add the plugin to the cache
                            plugins.addFromRepository(plugin)
                self.mRepositories[reposName]["state"] = 2
            else:
                # no plugin metadata found
                self.mRepositories[reposName]["state"] = 3
                if reply.attribute(
                        QNetworkRequest.HttpStatusCodeAttribute) == 200:
                    self.mRepositories[reposName][
                        "error"] = QCoreApplication.translate(
                            "QgsPluginInstaller",
                            "Server response is 200 OK, but doesn't contain plugin metatada. This is most likely caused by a proxy or a wrong repository URL. You can configure proxy settings in QGIS options."
                        )
                else:
                    self.mRepositories[reposName][
                        "error"] = QCoreApplication.translate(
                            "QgsPluginInstaller",
                            "Status code:") + " %d %s" % (
                                reply.attribute(
                                    QNetworkRequest.HttpStatusCodeAttribute),
                                reply.attribute(
                                    QNetworkRequest.HttpReasonPhraseAttribute))

        self.repositoryFetched.emit(reposName)

        # is the checking done?
        if not self.fetchingInProgress():
            self.checkingDone.emit()

        reply.deleteLater()

    # ----------------------------------------- #
    def inspectionFilter(self):
        """ return inspection filter (only one repository to be fetched) """
        return self.mInspectionFilter

    # ----------------------------------------- #
    def setInspectionFilter(self, key=None):
        """ temporarily disable all repositories but this for inspection """
        self.mInspectionFilter = key
Example #17
0
class DBTree(QTreeView):
    selectedItemChanged = pyqtSignal(object)

    def __init__(self, mainWindow):
        QTreeView.__init__(self, mainWindow)
        self.mainWindow = mainWindow

        self.setModel(DBModel(self))
        self.setHeaderHidden(True)
        self.setEditTriggers(QTreeView.EditKeyPressed
                             | QTreeView.SelectedClicked)

        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.setDropIndicatorShown(True)

        self.doubleClicked.connect(self.addLayer)
        self.selectionModel().currentChanged.connect(self.currentItemChanged)
        self.expanded.connect(self.itemChanged)
        self.collapsed.connect(self.itemChanged)
        self.model().dataChanged.connect(self.modelDataChanged)
        self.model().notPopulated.connect(self.collapse)

    def refreshItem(self, item=None):
        if item is None:
            item = self.currentItem()
            if item is None:
                return
        self.model().refreshItem(item)

    def showSystemTables(self, show):
        pass

    def currentItem(self):
        indexes = self.selectedIndexes()
        if len(indexes) <= 0:
            return
        return self.model().getItem(indexes[0])

    def currentDatabase(self):
        item = self.currentItem()
        if item is None:
            return

        if isinstance(item, (DBPlugin, Schema, Table)):
            return item.database()
        return None

    def currentSchema(self):
        item = self.currentItem()
        if item is None:
            return

        if isinstance(item, (Schema, Table)):
            return item.schema()
        return None

    def currentTable(self):
        item = self.currentItem()
        if item is None:
            return

        if isinstance(item, Table):
            return item
        return None

    def newConnection(self):
        index = self.currentIndex()
        if not index.isValid() or not isinstance(index.internalPointer(),
                                                 PluginItem):
            return
        item = self.currentItem()
        self.mainWindow.invokeCallback(item.addConnectionActionSlot, index)

    def itemChanged(self, index):
        self.setCurrentIndex(index)
        self.selectedItemChanged.emit(self.currentItem())

    def modelDataChanged(self, indexFrom, indexTo):
        self.itemChanged(indexTo)

    def currentItemChanged(self, current, previous):
        self.itemChanged(current)

    def contextMenuEvent(self, ev):
        index = self.indexAt(ev.pos())
        if not index.isValid():
            return

        if index != self.currentIndex():
            self.itemChanged(index)

        item = self.currentItem()

        menu = QMenu(self)

        if isinstance(item, (Table, Schema)):
            menu.addAction(self.tr("Rename"), self.rename)
            menu.addAction(self.tr("Delete"), self.delete)

            if isinstance(item, Table) and item.canBeAddedToCanvas():
                menu.addSeparator()
                menu.addAction(self.tr("Add to canvas"), self.addLayer)

        elif isinstance(item, DBPlugin):
            if item.database() is not None:
                menu.addAction(self.tr("Re-connect"), self.reconnect)
            menu.addAction(self.tr("Remove"), self.delete)

        elif not index.parent().isValid() and item.typeName() == "spatialite":
            menu.addAction(self.tr("New Connection..."), self.newConnection)

        if not menu.isEmpty():
            menu.exec_(ev.globalPos())

        menu.deleteLater()

    def rename(self):
        item = self.currentItem()
        if isinstance(item, (Table, Schema)):
            self.edit(self.currentIndex())

    def delete(self):
        item = self.currentItem()
        if isinstance(item, (Table, Schema)):
            self.mainWindow.invokeCallback(item.database().deleteActionSlot)
        elif isinstance(item, DBPlugin):
            self.mainWindow.invokeCallback(item.removeActionSlot)

    def addLayer(self):
        table = self.currentTable()
        if table is not None:
            layer = table.toMapLayer()
            layers = QgsMapLayerRegistry.instance().addMapLayers([layer])
            if len(layers) != 1:
                QgsMessageLog.logMessage(
                    self.tr("%1 is an invalid layer - not loaded").replace(
                        "%1", layer.publicSource()))
                msgLabel = QLabel(
                    self.
                    tr("%1 is an invalid layer and cannot be loaded. Please check the <a href=\"#messageLog\">message log</a> for further info."
                       ).replace("%1", layer.publicSource()),
                    self.mainWindow.infoBar)
                msgLabel.setWordWrap(True)
                msgLabel.linkActivated.connect(
                    self.mainWindow.iface.mainWindow().findChild(
                        QWidget, "MessageLog").show)
                msgLabel.linkActivated.connect(
                    self.mainWindow.iface.mainWindow().raise_)
                self.mainWindow.infoBar.pushItem(
                    QgsMessageBarItem(msgLabel, QgsMessageBar.WARNING))

    def reconnect(self):
        db = self.currentDatabase()
        if db is not None:
            self.mainWindow.invokeCallback(db.reconnectActionSlot)
Example #18
0
class DlgTableProperties(QDialog, Ui_Dialog):
    aboutToChangeTable = pyqtSignal()

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

        self.db = self.table.database()

        m = TableFieldsModel(self)
        self.viewFields.setModel(m)

        m = TableConstraintsModel(self)
        self.viewConstraints.setModel(m)

        m = TableIndexesModel(self)
        self.viewIndexes.setModel(m)

        self.btnAddColumn.clicked.connect(self.addColumn)
        self.btnAddGeometryColumn.clicked.connect(self.addGeometryColumn)
        self.btnEditColumn.clicked.connect(self.editColumn)
        self.btnDeleteColumn.clicked.connect(self.deleteColumn)

        self.btnAddConstraint.clicked.connect(self.addConstraint)
        self.btnDeleteConstraint.clicked.connect(self.deleteConstraint)

        self.btnAddIndex.clicked.connect(self.createIndex)
        self.btnAddSpatialIndex.clicked.connect(self.createSpatialIndex)
        self.btnDeleteIndex.clicked.connect(self.deleteIndex)

        self.populateViews()
        self.checkSupports()

    def checkSupports(self):
        allowEditColumns = self.db.connector.hasTableColumnEditingSupport()
        self.btnEditColumn.setEnabled(allowEditColumns)
        self.btnDeleteColumn.setEnabled(allowEditColumns)

        allowSpatial = self.db.connector.hasSpatialSupport()
        self.btnAddGeometryColumn.setEnabled(allowSpatial)
        self.btnAddSpatialIndex.setEnabled(allowSpatial)

    def populateViews(self):
        self.populateFields()
        self.populateConstraints()
        self.populateIndexes()

    def populateFields(self):
        """ load field information from database """

        m = self.viewFields.model()
        m.clear()

        for fld in self.table.fields():
            m.append(fld)

        for col in range(4):
            self.viewFields.resizeColumnToContents(col)

    def currentColumn(self):
        """ returns row index of selected column """
        sel = self.viewFields.selectionModel()
        indexes = sel.selectedRows()
        if len(indexes) == 0:
            QMessageBox.information(self, self.tr("DB Manager"),
                                    self.tr("nothing selected"))
            return -1
        return indexes[0].row()

    def addColumn(self):
        """ open dialog to set column info and add column to table """
        dlg = DlgFieldProperties(self, None, self.table)
        if not dlg.exec_():
            return
        fld = dlg.getField()

        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.aboutToChangeTable.emit()
        try:
            # add column to table
            self.table.addField(fld)
            self.populateViews()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def addGeometryColumn(self):
        """ open dialog to add geometry column """
        dlg = DlgAddGeometryColumn(self, self.table)
        if not dlg.exec_():
            return
        self.populateViews()

    def editColumn(self):
        """ open dialog to change column info and alter table appropriately """
        index = self.currentColumn()
        if index == -1:
            return

        m = self.viewFields.model()
        # get column in table
        # (there can be missing number if someone deleted a column)
        fld = m.getObject(index)

        dlg = DlgFieldProperties(self, fld, self.table)
        if not dlg.exec_():
            return
        new_fld = dlg.getField(True)

        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.aboutToChangeTable.emit()
        try:
            fld.update(new_fld.name, new_fld.type2String(), new_fld.notNull,
                       new_fld.default2String())
            self.populateViews()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def deleteColumn(self):
        """ delete currently selected column """
        index = self.currentColumn()
        if index == -1:
            return

        m = self.viewFields.model()
        fld = m.getObject(index)

        res = QMessageBox.question(
            self, self.tr("Are you sure"),
            self.tr("really delete column '%s'?") % fld.name,
            QMessageBox.Yes | QMessageBox.No)
        if res != QMessageBox.Yes:
            return

        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.aboutToChangeTable.emit()
        try:
            fld.delete()
            self.populateViews()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def populateConstraints(self):
        constraints = self.table.constraints()
        if constraints is None:
            self.hideConstraints()  # not supported
            return

        m = self.viewConstraints.model()
        m.clear()

        for constr in constraints:
            m.append(constr)

        for col in range(3):
            self.viewConstraints.resizeColumnToContents(col)

    def hideConstraints(self):
        index = self.tabs.indexOf(self.tabConstraints)
        if index >= 0:
            self.tabs.setTabEnabled(index, False)

    def addConstraint(self):
        """ add primary key or unique constraint """

        dlg = DlgCreateConstraint(self, self.table)
        if not dlg.exec_():
            return
        self.populateViews()

    def deleteConstraint(self):
        """ delete a constraint """

        index = self.currentConstraint()
        if index == -1:
            return

        m = self.viewConstraints.model()
        constr = m.getObject(index)

        res = QMessageBox.question(
            self, self.tr("Are you sure"),
            self.tr("really delete constraint '%s'?") % constr.name,
            QMessageBox.Yes | QMessageBox.No)
        if res != QMessageBox.Yes:
            return

        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.aboutToChangeTable.emit()
        try:
            constr.delete()
            self.populateViews()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def currentConstraint(self):
        """ returns row index of selected index """
        sel = self.viewConstraints.selectionModel()
        indexes = sel.selectedRows()
        if len(indexes) == 0:
            QMessageBox.information(self, self.tr("DB Manager"),
                                    self.tr("nothing selected"))
            return -1
        return indexes[0].row()

    def populateIndexes(self):
        indexes = self.table.indexes()
        if indexes is None:
            self.hideIndexes()
            return

        m = self.viewIndexes.model()
        m.clear()

        for idx in indexes:
            m.append(idx)

        for col in range(2):
            self.viewIndexes.resizeColumnToContents(col)

    def hideIndexes(self):
        index = self.tabs.indexOf(self.tabIndexes)
        if index >= 0:
            self.tabs.setTabEnabled(index, False)

    def createIndex(self):
        """ create an index """
        dlg = DlgCreateIndex(self, self.table)
        if not dlg.exec_():
            return
        self.populateViews()

    def createSpatialIndex(self):
        """ create spatial index for the geometry column """
        if self.table.type != self.table.VectorType:
            QMessageBox.information(
                self, self.tr("DB Manager"),
                self.tr("The selected table has no geometry"))
            return

        res = QMessageBox.question(
            self, self.tr("Create?"),
            self.tr("Create spatial index for field %s?") %
            self.table.geomColumn, QMessageBox.Yes | QMessageBox.No)
        if res != QMessageBox.Yes:
            return

        # TODO: first check whether the index doesn't exist already
        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.aboutToChangeTable.emit()

        try:
            self.table.createSpatialIndex()
            self.populateViews()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def currentIndex(self):
        """ returns row index of selected index """
        sel = self.viewIndexes.selectionModel()
        indexes = sel.selectedRows()
        if len(indexes) == 0:
            QMessageBox.information(self, self.tr("DB Manager"),
                                    self.tr("Nothing selected"))
            return -1
        return indexes[0].row()

    def deleteIndex(self):
        """ delete currently selected index """
        index = self.currentIndex()
        if index == -1:
            return

        m = self.viewIndexes.model()
        idx = m.getObject(index)

        res = QMessageBox.question(
            self, self.tr("Are you sure"),
            self.tr("really delete index '%s'?") % idx.name,
            QMessageBox.Yes | QMessageBox.No)
        if res != QMessageBox.Yes:
            return

        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.aboutToChangeTable.emit()
        try:
            idx.delete()
            self.populateViews()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()