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
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
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
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)
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)
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()
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
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()
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()
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()
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()
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 ''
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()
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()
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)
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("& ") 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
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)
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()