def populateList(self): model = QStandardItemModel() for option in self.selectedoptions: item = QStandardItem(option) model.appendRow(item) self.lstLayers.setModel(model)
def populateList(self): model = QStandardItemModel() for i, option in enumerate(self.options): item = QStandardItem(option) item.setCheckState(Qt.Checked if i in self.selectedoptions else Qt.Unchecked) item.setCheckable(True) model.appendRow(item) self.lstLayers.setModel(model)
def populateList(self): model = QStandardItemModel() for value, text in self.options: item = QStandardItem(text) item.setData(value, Qt.UserRole) item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked) item.setCheckable(True) model.appendRow(item) self.lstLayers.setModel(model)
def setValue(self, table): cols = len(table[0]) rows = len(table) model = QStandardItemModel(rows, cols) for i in range(rows): for j in range(cols): item = QStandardItem(str(table[i][j])) model.setItem(i, j, item) self.tblView.setModel(model)
def setValue(self, headers, table): model = self.tblView.model() model.setHorizontalHeaderLabels(headers) cols = len(headers) rows = len(table) // cols model = QStandardItemModel(rows, cols) for row in range(rows): for col in range(cols): item = QStandardItem(str(table[row * cols + col])) model.setItem(row, col, item) self.tblView.setModel(model)
def populateTable(self, table): cols = len(self.param.cols) rows = len(table) model = QStandardItemModel(rows, cols) # Set headers model.setHorizontalHeaderLabels(self.param.cols) # Populate table for i in range(rows): for j in range(cols): item = QStandardItem(table[i][j]) model.setItem(i, j, item) self.tblView.setModel(model)
def populateTable(self, table): cols = len(self.param.headers()) rows = len(table) // cols model = QStandardItemModel(rows, cols) # Set headers model.setHorizontalHeaderLabels(self.param.headers()) # Populate table for row in range(rows): for col in range(cols): item = QStandardItem(str(table[row * cols + col])) model.setItem(row, col, item) self.tblView.setModel(model)
def __init__(self, showSearch=True): super(ConfigDialog, self).__init__(None) self.setupUi(self) self.groupIcon = QIcon() self.groupIcon.addPixmap(self.style().standardPixmap( QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off) self.groupIcon.addPixmap(self.style().standardPixmap( QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On) self.model = QStandardItemModel() self.tree.setModel(self.model) self.delegate = SettingDelegate() self.tree.setItemDelegateForColumn(1, self.delegate) if showSearch: if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(QApplication.translate('ConfigDialog', 'Search…')) self.searchBox.textChanged.connect(self.textChanged) else: self.searchBox.hide() self.fillTree() self.saveMenus = False self.tree.expanded.connect(self.itemExpanded) self.auto_adjust_columns = True
def __init__(self, toolbox): super(ConfigDialog, self).__init__(None) self.setupUi(self) self.toolbox = toolbox self.groupIcon = QIcon() self.groupIcon.addPixmap(self.style().standardPixmap( QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off) self.groupIcon.addPixmap(self.style().standardPixmap( QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) self.model = QStandardItemModel() self.tree.setModel(self.model) self.delegate = SettingDelegate() self.tree.setItemDelegateForColumn(1, self.delegate) self.searchBox.textChanged.connect(self.textChanged) self.fillTree() self.tree.expanded.connect(self.adjustColumns)
def __init__(self, parent=None): super(MatrixModelerWidget, self).__init__(parent) self.setupUi(self) self.btnAddColumn.setIcon(QgsApplication.getThemeIcon('/mActionNewAttribute.svg')) self.btnRemoveColumn.setIcon(QgsApplication.getThemeIcon('/mActionDeleteAttribute.svg')) self.btnAddRow.setIcon(QgsApplication.getThemeIcon('/symbologyAdd.svg')) self.btnRemoveRow.setIcon(QgsApplication.getThemeIcon('/symbologyRemove.svg')) self.btnClear.setIcon(QgsApplication.getThemeIcon('console/iconClearConsole.svg')) self.btnAddColumn.clicked.connect(self.addColumn) self.btnRemoveColumn.clicked.connect(self.removeColumns) self.btnAddRow.clicked.connect(self.addRow) self.btnRemoveRow.clicked.connect(self.removeRows) self.btnClear.clicked.connect(self.clearTable) items = [QStandardItem('0')] model = QStandardItemModel() model.appendColumn(items) self.tblView.setModel(model) self.tblView.horizontalHeader().sectionDoubleClicked.connect(self.changeHeader)
def __init__(self, parent): QDialog.__init__(self, parent) self.setupUi(self) self.parent = parent self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console - Command History")) self.listView.setToolTip(QCoreApplication.translate("PythonConsole", "Double click on item to execute")) self.model = QStandardItemModel(self.listView) self._reloadHistory() self.deleteScut = QShortcut(QKeySequence(Qt.Key_Delete), self) self.deleteScut.activated.connect(self._deleteItem) self.listView.doubleClicked.connect(self._runHistory) self.reloadHistory.clicked.connect(self._reloadHistory) self.saveHistory.clicked.connect(self._saveHistory)
def populateList(self): self.model = QStandardItemModel() for value, text in self.options: item = QStandardItem(text) item.setData(value, Qt.UserRole) item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked) item.setCheckable(True) self.model.appendRow(item) # add extra options (e.g. manually added layers) for t in [o for o in self.selectedoptions if not isinstance(o, int)]: if isinstance(t, QgsProcessingModelChildParameterSource): item = QStandardItem(t.staticValue()) else: item = QStandardItem(t) item.setData(item.text(), Qt.UserRole) item.setCheckState(Qt.Checked) item.setCheckable(True) self.model.appendRow(item) self.lstLayers.setModel(self.model)
class HistoryDialog(QDialog, Ui_HistoryDialogPythonConsole): def __init__(self, parent): QDialog.__init__(self, parent) self.setupUi(self) self.parent = parent self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console - Command History")) self.listView.setToolTip(QCoreApplication.translate("PythonConsole", "Double click on item to execute")) self.model = QStandardItemModel(self.listView) self._reloadHistory() self.deleteScut = QShortcut(QKeySequence(Qt.Key_Delete), self) self.deleteScut.activated.connect(self._deleteItem) self.listView.doubleClicked.connect(self._runHistory) self.reloadHistory.clicked.connect(self._reloadHistory) self.saveHistory.clicked.connect(self._saveHistory) def _runHistory(self, item): cmd = item.data(Qt.DisplayRole) self.parent.runCommand(cmd) def _saveHistory(self): self.parent.writeHistoryFile(True) def _reloadHistory(self): self.model.clear() for i in self.parent.history: item = QStandardItem(i) if sys.platform.startswith('win'): item.setSizeHint(QSize(18, 18)) self.model.appendRow(item) self.listView.setModel(self.model) self.listView.scrollToBottom() def _deleteItem(self): itemsSelected = self.listView.selectionModel().selectedIndexes() if itemsSelected: item = itemsSelected[0].row() ## Remove item from the command history (just for the current session) self.parent.history.pop(item) self.parent.historyIndex -= 1 ## Remove row from the command history dialog self.model.removeRow(item)
def __init__(self, showSearch=True): super(ConfigDialog, self).__init__(None) self.setupUi(self) self.groupIcon = QgsApplication.getThemeIcon('mIconFolder.svg') self.model = QStandardItemModel() self.tree.setModel(self.model) self.delegate = SettingDelegate() self.tree.setItemDelegateForColumn(1, self.delegate) if showSearch: if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(QApplication.translate('ConfigDialog', 'Search…')) self.searchBox.textChanged.connect(self.textChanged) else: self.searchBox.hide() self.fillTree() self.saveMenus = False self.tree.expanded.connect(self.itemExpanded) self.auto_adjust_columns = True
def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value("/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget(int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(os.path.dirname(__file__), ".", "pdok.json") f = open(pdokjson, 'r', encoding='utf-8') self.pdok = json.load(f) f.close() self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers(QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str) or isinstance(service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText("woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect(self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable(False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel().selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True; self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth(0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer()
class AbstractSTREnityListView(QListView): """ A widget for listing and selecting one or more STR entities. .. versionadded:: 1.7 """ def __init__(self, parent=None, **kwargs): super(AbstractSTREnityListView, self).__init__(parent) self._model = QStandardItemModel(self) self._model.setColumnCount(1) self.setModel(self._model) self.setEditTriggers(QAbstractItemView.NoEditTriggers) self._model.itemChanged.connect(self._on_item_changed) self._profile = kwargs.get('profile', None) self._social_tenure = kwargs.get('social_tenure', None) # Load appropriate entities to the view if not self._profile is None: self._load_profile_entities() # Load entities in the STR definition if not self._social_tenure is None: self._select_str_entities() def _on_item_changed(self, item): # Emit signals when an item has been (de)selected. To be # implemented by subclasses. pass @property def profile(self): """ :return: Returns the current profile object in the configuration. :rtype: Profile """ return self._profile @profile.setter def profile(self, profile): """ Sets the current profile object in the configuration. :param profile: Profile object. :type profile: Profile """ self._profile = profile self._load_profile_entities() @property def social_tenure(self): """ :return: Returns the profile's social tenure entity. :rtype: SocialTenure """ return self._social_tenure @social_tenure.setter def social_tenure(self, social_tenure): """ Set the social_tenure entity. :param social_tenure: A profile's social tenure entity. :type social_tenure: SocialTenure """ self._social_tenure = social_tenure self._select_str_entities() def _select_str_entities(self): """ Select the entities defined in the STR. E.g. parties for party entity and spatial units for spatial unit entity. Default implementation does nothing, to be implemented by subclasses. """ pass def _load_profile_entities(self): # Reset view self.clear() # Populate entity items in the view for e in self._profile.user_entities(): self._add_entity(e) def _add_entity(self, entity): # Add entity item to view item = QStandardItem( GuiUtils.get_icon('table.png'), entity.short_name ) item.setCheckable(True) item.setCheckState(Qt.Unchecked) self._model.appendRow(item) def select_entities(self, entities): """ Checks STR entities in the view and emit the entity_selected signal for each item selected. :param entities: Collection of STR entities. :type entities: list """ # Clear selection self.clear_selection() for e in entities: name = e.short_name self.select_entity(name) def selected_entities(self): """ :return: Returns a list of selected entity short names. :rtype: list """ selected_items = [] for i in range(self._model.rowCount()): item = self._model.item(i) if item.checkState() == Qt.Checked: selected_items.append(item.text()) return selected_items def clear(self): """ Remove all party items in the view. """ self._model.clear() self._model.setColumnCount(1) def clear_selection(self): """ Uncheck all items in the view. """ for i in range(self._model.rowCount()): item = self._model.item(i) if item.checkState() == Qt.Checked: item.setCheckState(Qt.Unchecked) def select_entity(self, name): """ Selects a party entity with the given short name. :param name: Entity short name :type name: str """ items = self._model.findItems(name) if len(items) > 0: item = items[0] if item.checkState() == Qt.Unchecked: item.setCheckState(Qt.Checked) def deselect_entity(self, name): """ Deselects an entity with the given short name. :param name: Entity short name :type name: str """ items = self._model.findItems(name) if len(items) > 0: item = items[0] if item.checkState() == Qt.Checked: item.setCheckState(Qt.Unchecked)
def headerData(self, section, orientation, role): if orientation == Qt.Vertical and role == Qt.DisplayRole: return "↑ ↓" return QStandardItemModel.headerData(self, section, orientation, role)
def __init__(self, header, editable=False, parent=None): self.header = header self.editable = editable QStandardItemModel.__init__(self, 0, len(self.header), parent)
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( self.tr(u"{0} - {1} [{2}]").format(self.windowTitle(), db.connection().connectionName(), db.connection().typeNameString())) self.defaultLayerName = 'QueryLayer' if self.allowMultiColumnPk: self.uniqueColumnCheck.setText(self.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText(self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) 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' + str(name.__hash__()) + '/name', name) QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + str(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' + str(name.__hash__())) self.presetCombo.removeItem(self.presetCombo.findText(name)) self.presetCombo.setCurrentIndex(-1) def loadPreset(self, name): query = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + str(name.__hash__()) + '/query')[0] name = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + str(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 with OverrideCursor(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("{0} rows, {1:.1f} seconds").format(model.affectedRows(), model.secs())) cols = self.viewResult.model().columnNames() for col in cols: quotedCols.append(self.db.connector.quoteId(col)) except BaseError as e: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return self.setColumnCombos(cols, quotedCols) self.update() 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 layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayer.RasterLayer # get a new layer name names = [] for layer in list(QgsProject.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): with OverrideCursor(Qt.WaitCursor): layer = self._getSqlLayer(self.filter) if layer is None: return QgsProject.instance().addMapLayers([layer], True) def fillColumnCombos(self): query = self._getSqlQuery() if query == "": return with OverrideCursor(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" % (str(query), connector.quoteId(alias)) else: sql = u"SELECT * FROM (%s\n) WHERE 1=0" % str(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: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return finally: if c: c.close() del c self.setColumnCombos(cols, quotedCols) def setColumnCombos(self, cols, quotedCols): # get sensible default columns. do this before sorting in case there's hints in the column order (e.g., 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 list(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 ConfigDialog(BASE, WIDGET): def __init__(self, showSearch=True): super(ConfigDialog, self).__init__(None) self.setupUi(self) self.groupIcon = QIcon() self.groupIcon.addPixmap(self.style().standardPixmap( QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off) self.groupIcon.addPixmap(self.style().standardPixmap( QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On) self.model = QStandardItemModel() self.tree.setModel(self.model) self.delegate = SettingDelegate() self.tree.setItemDelegateForColumn(1, self.delegate) if showSearch: if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(QApplication.translate('ConfigDialog', 'Search…')) self.searchBox.textChanged.connect(self.textChanged) else: self.searchBox.hide() self.fillTree() self.saveMenus = False self.tree.expanded.connect(self.itemExpanded) self.auto_adjust_columns = True def textChanged(self, text=None): if text is not None: text = str(text.lower()) else: text = str(self.searchBox.text().lower()) found = self._filterItem(self.model.invisibleRootItem(), text) self.auto_adjust_columns = False if text: self.tree.expandAll() else: self.tree.collapseAll() self.adjustColumns() self.auto_adjust_columns = True if text: return found else: self.tree.collapseAll() return False def _filterItem(self, item, text, forceShow=False): if item.hasChildren(): show = forceShow or isinstance(item, QStandardItem) and bool(text) and (text in item.text().lower()) for i in range(item.rowCount()): child = item.child(i) show = self._filterItem(child, text, forceShow) or show self.tree.setRowHidden(item.row(), item.index().parent(), not show) return show elif isinstance(item, QStandardItem): show = forceShow or bool(text) and (text in item.text().lower()) self.tree.setRowHidden(item.row(), item.index().parent(), not show) return show def fillTree(self): self.fillTreeUsingProviders() def fillTreeUsingProviders(self): self.items = {} self.model.clear() self.model.setHorizontalHeaderLabels([self.tr('Setting'), self.tr('Value')]) settings = ProcessingConfig.getSettings() rootItem = self.model.invisibleRootItem() """ Filter 'General', 'Models' and 'Scripts' items """ priorityKeys = [self.tr('General'), self.tr('Models'), self.tr('Scripts')] for group in priorityKeys: groupItem = QStandardItem(group) icon = ProcessingConfig.getGroupIcon(group) groupItem.setIcon(icon) groupItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [groupItem, emptyItem]) if not group in settings: continue # add menu item only if it has any search matches for setting in settings[group]: if setting.hidden or setting.name.startswith("MENU_"): continue labelItem = QStandardItem(setting.description) labelItem.setIcon(icon) labelItem.setEditable(False) self.items[setting] = SettingItem(setting) groupItem.insertRow(0, [labelItem, self.items[setting]]) """ Filter 'Providers' items """ providersItem = QStandardItem(self.tr('Providers')) icon = QgsApplication.getThemeIcon("/processingAlgorithm.svg") providersItem.setIcon(icon) providersItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [providersItem, emptyItem]) for group in list(settings.keys()): if group in priorityKeys or group == menusSettingsGroup: continue groupItem = QStandardItem(group) icon = ProcessingConfig.getGroupIcon(group) groupItem.setIcon(icon) groupItem.setEditable(False) for setting in settings[group]: if setting.hidden: continue labelItem = QStandardItem(setting.description) labelItem.setIcon(icon) labelItem.setEditable(False) self.items[setting] = SettingItem(setting) groupItem.insertRow(0, [labelItem, self.items[setting]]) emptyItem = QStandardItem() emptyItem.setEditable(False) providersItem.appendRow([groupItem, emptyItem]) """ Filter 'Menus' items """ self.menusItem = QStandardItem(self.tr('Menus')) icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png')) self.menusItem.setIcon(icon) self.menusItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [self.menusItem, emptyItem]) button = QPushButton(self.tr('Reset to defaults')) button.clicked.connect(self.resetMenusToDefaults) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(button) layout.addStretch() widget = QWidget() widget.setLayout(layout) self.tree.setIndexWidget(emptyItem.index(), widget) for provider in QgsApplication.processingRegistry().providers(): providerDescription = provider.name() groupItem = QStandardItem(providerDescription) icon = provider.icon() groupItem.setIcon(icon) groupItem.setEditable(False) for alg in provider.algorithms(): algItem = QStandardItem(alg.displayName()) algItem.setIcon(icon) algItem.setEditable(False) try: settingMenu = ProcessingConfig.settings["MENU_" + alg.id()] settingButton = ProcessingConfig.settings["BUTTON_" + alg.id()] settingIcon = ProcessingConfig.settings["ICON_" + alg.id()] except: continue self.items[settingMenu] = SettingItem(settingMenu) self.items[settingButton] = SettingItem(settingButton) self.items[settingIcon] = SettingItem(settingIcon) menuLabelItem = QStandardItem("Menu path") menuLabelItem.setEditable(False) buttonLabelItem = QStandardItem("Add button in toolbar") buttonLabelItem.setEditable(False) iconLabelItem = QStandardItem("Icon") iconLabelItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) algItem.insertRow(0, [menuLabelItem, self.items[settingMenu]]) algItem.insertRow(0, [buttonLabelItem, self.items[settingButton]]) algItem.insertRow(0, [iconLabelItem, self.items[settingIcon]]) groupItem.insertRow(0, [algItem, emptyItem]) emptyItem = QStandardItem() emptyItem.setEditable(False) self.menusItem.appendRow([groupItem, emptyItem]) self.tree.sortByColumn(0, Qt.AscendingOrder) self.adjustColumns() def resetMenusToDefaults(self): for provider in QgsApplication.processingRegistry().providers(): for alg in provider.algorithms(): d = defaultMenuEntries.get(alg.id(), "") setting = ProcessingConfig.settings["MENU_" + alg.id()] item = self.items[setting] item.setData(d, Qt.EditRole) self.saveMenus = True def accept(self): qsettings = QgsSettings() for setting in list(self.items.keys()): if setting.group != menusSettingsGroup or self.saveMenus: if isinstance(setting.value, bool): setting.setValue(self.items[setting].checkState() == Qt.Checked) else: try: setting.setValue(str(self.items[setting].text())) except ValueError as e: QMessageBox.warning(self, self.tr('Wrong value'), self.tr('Wrong value for parameter "{0}":\n\n{1}').format(setting.description, str(e))) return setting.save(qsettings) with OverrideCursor(Qt.WaitCursor): for p in QgsApplication.processingRegistry().providers(): p.refreshAlgorithms() settingsWatcher.settingsChanged.emit() def itemExpanded(self, idx): if idx == self.menusItem.index(): self.saveMenus = True if self.auto_adjust_columns: self.adjustColumns() def adjustColumns(self): self.tree.resizeColumnToContents(0) self.tree.resizeColumnToContents(1)
class DlgSqlLayerWindow(QWidget, Ui_Dialog): nameChanged = pyqtSignal(str) def __init__(self, iface, layer, parent=None): QWidget.__init__(self, parent) self.iface = iface self.layer = layer uri = QgsDataSourceUri(layer.source()) dbplugin = None db = None if layer.dataProvider().name() == 'postgres': dbplugin = createDbPlugin('postgis', 'postgres') elif layer.dataProvider().name() == 'spatialite': dbplugin = createDbPlugin('spatialite', 'spatialite') elif layer.dataProvider().name() == 'oracle': dbplugin = createDbPlugin('oracle', 'oracle') elif layer.dataProvider().name() == 'virtual': dbplugin = createDbPlugin('vlayers', 'virtual') elif layer.dataProvider().name() == 'ogr': dbplugin = createDbPlugin('gpkg', 'gpkg') if dbplugin: dbplugin.connectToUri(uri) db = dbplugin.db self.dbplugin = dbplugin 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 = self.tr('QueryLayer') if self.allowMultiColumnPk: self.uniqueColumnCheck.setText( self.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText( self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) 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.editSql.textChanged.connect(self.updatePresetButtonsState) self.presetName.textChanged.connect(self.updatePresetButtonsState) self.presetCombo.currentIndexChanged.connect( self.updatePresetButtonsState) 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 self.layerTypeWidget.hide() # show if load as raster is supported # self.loadLayerBtn.clicked.connect(self.loadSqlLayer) self.updateLayerBtn.clicked.connect(self.updateSqlLayer) self.getColumnsBtn.clicked.connect(self.fillColumnCombos) self.queryBuilderFirst = True self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif")) self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder) self.presetName.textChanged.connect(self.nameChanged) # Update from layer # First the SQL from QgsDataSourceUri table sql = uri.table().replace('\n', ' ').strip() if uri.keyColumn() == '_uid_': match = re.search( r'^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$', sql, re.S | re.X | re.IGNORECASE) if match: sql = match.group(1) else: match = re.search(r'^\((SELECT .+ FROM .+)\)$', sql, re.S | re.X | re.IGNORECASE) if match: sql = match.group(1) # Need to check on table() since the parentheses were removed by the regexp if not uri.table().startswith('(') and not uri.table().endswith(')'): schema = uri.schema() if schema and schema.upper() != 'PUBLIC': sql = 'SELECT * FROM {0}.{1}'.format( self.db.connector.quoteId(schema), self.db.connector.quoteId(sql)) else: sql = 'SELECT * FROM {0}'.format( self.db.connector.quoteId(sql)) self.editSql.setText(sql) self.executeSql() # Then the columns self.geomCombo.setCurrentIndex( self.geomCombo.findText(uri.geometryColumn(), Qt.MatchExactly)) if uri.keyColumn() != '_uid_': self.uniqueColumnCheck.setCheckState(Qt.Checked) if self.allowMultiColumnPk: itemsData = uri.keyColumn().split(',') for item in self.uniqueModel.findItems("*", Qt.MatchWildcard): if item.data() in itemsData: item.setCheckState(Qt.Checked) else: keyColumn = uri.keyColumn() if self.uniqueModel.findItems(keyColumn): self.uniqueCombo.setEditText(keyColumn) # Finally layer name, filter and selectAtId self.layerNameEdit.setText(layer.name()) self.filter = uri.sql() if uri.selectAtIdDisabled(): self.avoidSelectById.setCheckState(Qt.Checked) def getQueryHash(self, name): return 'q%s' % md5(name.encode('utf8')).hexdigest() def updatePresetButtonsState(self, *args): """Slot called when the combo box or the sql or the query name have changed: sets store button state""" self.presetStore.setEnabled( bool(self._getSqlQuery() and self.presetName.text())) self.presetDelete.setEnabled( bool(self.presetCombo.currentIndex() != -1)) 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/' + self.getQueryHash(name) + '/name', name) QgsProject.instance().writeEntry( 'DBManager', 'savedQueries/' + self.getQueryHash(name) + '/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' + self.getQueryHash(name)) self.presetCombo.removeItem(self.presetCombo.findText(name)) self.presetCombo.setCurrentIndex(-1) def loadPreset(self, name): query = QgsProject.instance().readEntry( 'DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query')[0] self.editSql.setText(query) def clearSql(self): self.editSql.clear() self.editSql.setFocus() self.filter = "" def executeSql(self): sql = self._getSqlQuery() if sql == "": return with OverrideCursor(Qt.WaitCursor): # delete the old model old_model = self.viewResult.model() self.viewResult.setModel(None) if old_model: old_model.deleteLater() quotedCols = [] try: # set the new model model = self.db.sqlResultModel(sql, self) self.viewResult.setModel(model) self.lblResult.setText( self.tr("{0} rows, {1:.3f} seconds").format( model.affectedRows(), model.secs())) cols = self.viewResult.model().columnNames() for col in cols: quotedCols.append(self.db.connector.quoteId(col)) except BaseError as e: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return self.setColumnCombos(cols, quotedCols) self.update() 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] layerType = QgsMapLayerType.VectorLayer if self.vectorRadio.isChecked( ) else QgsMapLayerType.RasterLayer # get a new layer name names = [] for layer in list(QgsProject.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): with OverrideCursor(Qt.WaitCursor): layer = self._getSqlLayer(self.filter) if layer is None: return QgsProject.instance().addMapLayers([layer], True) def updateSqlLayer(self): with OverrideCursor(Qt.WaitCursor): layer = self._getSqlLayer(self.filter) if layer is None: return # self.layer.dataProvider().setDataSourceUri(layer.dataProvider().dataSourceUri()) # self.layer.dataProvider().reloadData() XMLDocument = QDomDocument("style") XMLMapLayers = XMLDocument.createElement("maplayers") XMLMapLayer = XMLDocument.createElement("maplayer") self.layer.writeLayerXml(XMLMapLayer, XMLDocument, QgsReadWriteContext()) XMLMapLayer.firstChildElement( "datasource").firstChild().setNodeValue(layer.source()) XMLMapLayers.appendChild(XMLMapLayer) XMLDocument.appendChild(XMLMapLayers) self.layer.readLayerXml(XMLMapLayer, QgsReadWriteContext()) self.layer.reload() self.iface.actionDraw().trigger() self.iface.mapCanvas().refresh() def fillColumnCombos(self): query = self._getSqlQuery() if query == "": return with OverrideCursor(Qt.WaitCursor): # remove a trailing ';' from query if present if query.strip().endswith(';'): query = query.strip()[:-1] # get all the columns 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" % ( str(query), connector.quoteId(alias)) else: sql = u"SELECT * FROM (%s\n) WHERE 1=0" % str(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: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return finally: if c: c.close() del c self.setColumnCombos(cols, quotedCols) def setColumnCombos(self, cols, quotedCols): # get sensible default columns. do this before sorting in case there's hints in the column order (e.g., 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) 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 _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 SimulationOverview(uicls, basecls): """Dialog with methods for handling running simulations.""" PROGRESS_COLUMN_IDX = 2 def __init__(self, plugin_dock, parent=None): super().__init__(parent) self.setupUi(self) self.plugin_dock = plugin_dock self.threedi_api = self.plugin_dock.threedi_api self.user = self.plugin_dock.current_user self.model_selection_dlg = ModelSelectionDialog(self.plugin_dock, parent=self) self.simulation_init_wizard = None self.simulation_wizard = None self.simulations_keys = {} self.last_progresses = {} self.simulations_without_progress = set() self.tv_model = None self.setup_view_model() self.plugin_dock.simulations_progresses_sentinel.progresses_fetched.connect( self.update_progress) self.pb_new_sim.clicked.connect(self.new_wizard_init) self.pb_stop_sim.clicked.connect(self.stop_simulation) def setup_view_model(self): """Setting up model and columns for TreeView.""" delegate = SimulationProgressDelegate(self.tv_sim_tree) self.tv_sim_tree.setItemDelegateForColumn(self.PROGRESS_COLUMN_IDX, delegate) self.tv_model = QStandardItemModel(0, 3) self.tv_model.setHorizontalHeaderLabels( ["Simulation name", "User", "Progress"]) self.tv_sim_tree.setModel(self.tv_model) def add_simulation_to_model(self, simulation, status, progress): """Method for adding simulation to the model.""" sim_id = simulation.id sim_name_item = QStandardItem(f"{simulation.name} ({sim_id})") sim_name_item.setData(sim_id, Qt.UserRole) user_item = QStandardItem(simulation.user) progress_item = QStandardItem() progress_item.setData((status, progress), PROGRESS_ROLE) self.tv_model.appendRow([sim_name_item, user_item, progress_item]) self.simulations_keys[sim_id] = simulation for i in range(self.PROGRESS_COLUMN_IDX): self.tv_sim_tree.resizeColumnToContents(i) def update_progress(self, progresses): """Updating progress bars in the running simulations list.""" for sim_id, (sim, status, progress) in progresses.items(): status_name = status.name if status_name not in [ "queued", "starting", "initialized", "postprocessing" ]: continue if sim_id not in self.simulations_keys: self.add_simulation_to_model(sim, status, progress) row_count = self.tv_model.rowCount() for row_idx in range(row_count): name_item = self.tv_model.item(row_idx, 0) sim_id = name_item.data(Qt.UserRole) if sim_id in self.simulations_without_progress or sim_id not in progresses: continue progress_item = self.tv_model.item(row_idx, self.PROGRESS_COLUMN_IDX) sim, new_status, new_progress = progresses[sim_id] status_name = new_status.name if status_name == "stopped" or status_name == "crashed": old_status, old_progress = progress_item.data(PROGRESS_ROLE) progress_item.setData((new_status, old_progress), PROGRESS_ROLE) self.simulations_without_progress.add(sim_id) else: progress_item.setData((new_status, new_progress), PROGRESS_ROLE) if status_name == "finished": self.simulations_without_progress.add(sim_id) msg = f"Simulation {sim.name} finished!" self.plugin_dock.communication.bar_info(msg, log_text_color=QColor( Qt.darkGreen)) def new_wizard_init(self): """Open new simulation initiation options dialog.""" if self.plugin_dock.current_local_schematisation is not None: self.model_selection_dlg.search_le.setText( self.plugin_dock.current_local_schematisation.name) self.model_selection_dlg.fetch_3di_models() self.model_selection_dlg.refresh_templates_list() self.model_selection_dlg.exec_() if self.model_selection_dlg.model_is_loaded: simulation_template = self.model_selection_dlg.current_simulation_template simulation, settings_overview, events = self.get_simulation_data_from_template( simulation_template) self.simulation_init_wizard = SimulationInit(simulation_template, settings_overview, events, parent=self) self.simulation_init_wizard.exec_() if self.simulation_init_wizard.open_wizard: self.new_simulation(simulation, settings_overview, events) def get_simulation_data_from_template(self, template): """Fetching simulation, settings and events data from the simulation template.""" simulation, settings_overview, events = None, None, None try: tc = ThreediCalls(self.threedi_api) simulation = template.simulation settings_overview = tc.fetch_simulation_settings_overview( str(simulation.id)) events = tc.fetch_simulation_events(simulation.id) except ApiException as e: error_msg = extract_error_message(e) self.plugin_dock.communication.bar_error(error_msg) except Exception as e: error_msg = f"Error: {e}" self.plugin_dock.communication.bar_error(error_msg) return simulation, settings_overview, events def new_simulation(self, simulation, settings_overview, events): """Opening a wizard which allows defining and running new simulations.""" self.simulation_wizard = SimulationWizard(self.plugin_dock, self.model_selection_dlg, self.simulation_init_wizard) if simulation: self.simulation_wizard.load_template_parameters( simulation, settings_overview, events) self.close() self.simulation_wizard.exec_() new_simulations = self.simulation_wizard.new_simulations if new_simulations is not None: for sim in new_simulations: initial_status = self.simulation_wizard.new_simulation_statuses.get( sim.id) initial_progress = Progress(percentage=0, time=sim.duration) self.add_simulation_to_model(sim, initial_status, initial_progress) def stop_simulation(self): """Sending request to shut down currently selected simulation.""" index = self.tv_sim_tree.currentIndex() if not index.isValid(): return title = "Warning" question = "This simulation is now running.\nAre you sure you want to stop it?" answer = self.plugin_dock.communication.ask(self, title, question, QMessageBox.Warning) if answer is True: try: name_item = self.tv_model.item(index.row(), 0) sim_id = name_item.data(Qt.UserRole) tc = ThreediCalls(self.plugin_dock.threedi_api) tc.create_simulation_action(sim_id, name="shutdown") msg = f"Simulation {name_item.text()} stopped!" self.plugin_dock.communication.bar_info(msg) except ApiException as e: error_msg = extract_error_message(e) self.plugin_dock.communication.show_error(error_msg) except Exception as e: error_msg = f"Error: {e}" self.plugin_dock.communication.show_error(error_msg)
class ListWidget(EditorWidget): widgettype = 'List' def __init__(self, *args, **kwargs): super(ListWidget, self).__init__(*args, **kwargs) self.listmodel = QStandardItemModel() self._bindvalue = None def createWidget(self, parent): return QComboBox(parent) def _buildfromlist(self, widget, listconfig): items = listconfig['items'] for item in items: parts = item.split(';') data = parts[0] try: desc = parts[1] except IndexError: desc = data try: path = parts[2] path = path.strip() icon = QIcon(path) except: icon = QIcon() item = QStandardItem(desc) item.setData(data, Qt.UserRole) item.setIcon(icon) self.listmodel.appendRow(item) def _buildfromlayer(self, widget, layerconfig): layername = layerconfig['layer'] keyfield = layerconfig['key'] valuefield = layerconfig['value'] filterexp = layerconfig.get('filter', None) try: layer = utils.layer_by_name(layername) except IndexError: roam.utils.warning( "Can't find layer {} in project".format(layername)) return keyfieldindex = layer.fields().lookupField(keyfield) valuefieldindex = layer.fields().lookupField(valuefield) if keyfieldindex == -1 or valuefieldindex == -1: roam.utils.warning(f"Can't find key or value column for widget " f"Id: {self.id} " f"Layer: {layername} " f"Key: {keyfield} - {keyfieldindex} " f"Value: {valuefield} - {valuefieldindex} ") return if self.allownulls: item = QStandardItem('(no selection)') item.setData(None, Qt.UserRole) self.listmodel.appendRow(item) fields = [keyfieldindex, valuefieldindex] iconfieldindex = layer.fields().lookupField('icon') if iconfieldindex > -1: fields.append("icon") if not filterexp and valuefieldindex == keyfieldindex and iconfieldindex == -1: values = layer.uniqueValues(keyfieldindex) values = sorted(values) for value in values: value = nullconvert(value) item = QStandardItem(value) item.setData(value, Qt.UserRole) self.listmodel.appendRow(item) return features = roam.api.utils.search_layer(layer, filterexp, fields, with_geometry=False) # Sort the fields based on value field features = sorted(features, key=lambda f: f[keyfield]) for feature in features: keyvalue = nullconvert(feature[keyfieldindex]) valuvalue = nullconvert(feature[valuefield]) try: path = feature["icon"] icon = QIcon(path) except KeyError: icon = QIcon() item = QStandardItem(keyvalue) item.setData(str(valuvalue), Qt.UserRole) item.setIcon(icon) self.listmodel.appendRow(item) def initWidget(self, widget, config): if widget.isEditable(): widget.editTextChanged.connect(self.emitvaluechanged) widget.currentIndexChanged.connect(self.emitvaluechanged) widget.setModel(self.listmodel) widget.showPopup = self.showpopup widget.setIconSize(QSize(24, 24)) widget.setStyleSheet( "QComboBox::drop-down {border-width: 0px;} QComboBox::down-arrow {image: url(noimg); border-width: 0px;}" ) widget.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) def showpopup(self): if self.listmodel.rowCount() == 0: return self.largewidgetrequest.emit( BigListWidget, self.widget.currentIndex(), self._biglistitem, dict(model=self.listmodel, label=self.labeltext)) def updatefromconfig(self): super(ListWidget, self).updatefromconfig() self.listmodel.clear() if 'list' in self.config: listconfig = self.config['list'] self._buildfromlist(self.widget, listconfig) elif 'layer' in self.config: layerconfig = self.config['layer'] self._buildfromlayer(self.widget, layerconfig) super(ListWidget, self).endupdatefromconfig() @property def allownulls(self): return self.config.get('allownull', False) def validate(self, *args): if (not self.widget.currentText() == '' and not self.widget.currentText() == "(no selection)"): return True else: return False def _biglistitem(self, index): self.widget.setCurrentIndex(index.row()) def setvalue(self, value): self._bindvalue = value index = self.widget.findData(value) self.widget.setCurrentIndex(index) if index == -1 and self.widget.isEditable(): if value is None and not self.config['allownull']: return self.widget.addItem(str(value)) index = self.widget.count() - 1 self.widget.setCurrentIndex(index) def value(self): index = self.widget.currentIndex() value = self.widget.itemData(index) text = self.widget.currentText() if value is None and self.widget.isEditable( ) and not text == '(no selection)': return self.widget.currentText() return value
def __init__(self, iface, parent=None): # pylint:disable=too-many-statements """Constructor.""" super().__init__(parent) self.setupUi(self) self.setObjectName('QQuakeDialog') QgsGui.enableAutoGeometryRestore(self) self.scrollArea.setStyleSheet(""" QScrollArea { background: transparent; } QScrollArea > QWidget > QWidget { background: transparent; } QScrollArea > QWidget > QScrollBar { background: 1; } """) self.scrollArea_2.setStyleSheet(self.scrollArea.styleSheet()) self.scrollArea_3.setStyleSheet(self.scrollArea.styleSheet()) self.scrollArea_4.setStyleSheet(self.scrollArea.styleSheet()) self.splitter.setStretchFactor(0, 0) self.splitter_2.setStretchFactor(0, 0) self.splitter_3.setStretchFactor(0, 0) self.splitter_4.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.splitter_2.setStretchFactor(1, 1) self.splitter_3.setStretchFactor(1, 1) self.splitter_4.setStretchFactor(1, 1) self.fdsn_event_filter = FilterParameterWidget( iface, SERVICE_MANAGER.FDSNEVENT) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.fdsn_event_filter) self.fdsn_event_filter_container.setLayout(vl) self.earthquake_service_info_widget = ServiceInformationWidget(iface) self.fdsn_by_id_filter = FilterByIdWidget(iface, SERVICE_MANAGER.FDSNEVENT) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.fdsn_by_id_filter) self.fdsn_by_id_container.setLayout(vl) self.fdsn_by_url_widget = FetchByUrlWidget(iface, SERVICE_MANAGER.FDSNEVENT) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.fdsn_by_url_widget) self.fdsn_by_url_container.setLayout(vl) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.earthquake_service_info_widget) self.earthquake_service_info_container.setLayout(vl) self.macro_filter = FilterParameterWidget(iface, SERVICE_MANAGER.MACROSEISMIC) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.macro_filter) self.macro_filter_container.setLayout(vl) self.macro_by_id_filter = FilterByIdWidget( iface, SERVICE_MANAGER.MACROSEISMIC) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.macro_by_id_filter) self.macro_by_id_container.setLayout(vl) self.macro_by_url_widget = FetchByUrlWidget( iface, SERVICE_MANAGER.MACROSEISMIC) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.macro_by_url_widget) self.macro_by_url_container.setLayout(vl) self.macro_service_info_widget = ServiceInformationWidget(iface) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.macro_service_info_widget) self.macro_service_info_container.setLayout(vl) self.station_filter = FilterParameterWidget( iface, SERVICE_MANAGER.FDSNSTATION) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.station_filter) self.station_filter_container.setLayout(vl) self.station_by_id_filter = FilterStationByIdWidget( iface, SERVICE_MANAGER.FDSNSTATION) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.station_by_id_filter) self.station_by_id_container.setLayout(vl) self.station_service_info_widget = ServiceInformationWidget(iface) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.station_service_info_widget) self.station_service_info_container.setLayout(vl) self.station_by_url_widget = FetchByUrlWidget( iface, SERVICE_MANAGER.FDSNSTATION) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.station_by_url_widget) self.station_by_url_container.setLayout(vl) self.ogc_service_widget = OgcServiceWidget(iface) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.ogc_service_widget) self.ogc_widget_container.setLayout(vl) self.ogc_service_info_widget = ServiceInformationWidget(iface) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.ogc_service_info_widget) self.ogc_service_info_container.setLayout(vl) self.message_bar = QgsMessageBar() self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.insertWidget(0, self.message_bar) self.fdsn_event_url_text_browser.viewport().setAutoFillBackground( False) self.fdsn_macro_url_text_browser.viewport().setAutoFillBackground( False) self.fdsn_station_url_text_browser.viewport().setAutoFillBackground( False) self.button_box.button(QDialogButtonBox.Ok).setText( self.tr('Fetch Data')) self.button_box.rejected.connect(self._save_settings) self.iface = iface # OGC self.ogc_combo.addItem(self.tr('Web Map Services (WMS)'), SERVICE_MANAGER.WMS) self.ogc_combo.addItem(self.tr('Web Feature Services (WFS)'), SERVICE_MANAGER.WFS) self.ogc_combo.currentIndexChanged.connect(self.refreshOgcWidgets) self.ogc_list_model = QStandardItemModel(self.ogc_list) self.ogc_list.setModel(self.ogc_list_model) self.ogc_list.selectionModel().selectionChanged.connect( self._ogc_service_changed) self._refresh_services() SERVICE_MANAGER.refreshed.connect(self._refresh_services) # connect to refreshing function to refresh the UI depending on the WS self._refresh_fdsnevent_widgets() self.refreshFdsnMacroseismicWidgets() self.refreshFdsnStationWidgets() # change the UI parameter according to the web service chosen self.fdsn_event_list.currentRowChanged.connect( self._refresh_fdsnevent_widgets) self.fdsn_macro_list.currentRowChanged.connect( self.refreshFdsnMacroseismicWidgets) self.fdsn_station_list.currentRowChanged.connect( self.refreshFdsnStationWidgets) self.fdsn_event_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.fdsn_by_id_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.fdsn_by_url_widget.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.fdsn_event_list.currentRowChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.macro_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.macro_by_id_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.macro_by_url_widget.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.fdsn_macro_list.currentRowChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.station_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) self.station_by_id_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) self.fdsn_station_list.currentRowChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) self.station_by_url_widget.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) self.button_box.accepted.connect(self._getEventList) self.service_tab_widget.currentChanged.connect( lambda: self._refresh_url(None)) self.fetcher = None QgsGui.enableAutoGeometryRestore(self) self.fdsn_tab_widget.currentChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.macro_tab_widget.currentChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.fdsnstation_tab_widget.currentChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) for b in [ self.button_fdsn_new_service, self.button_macro_new_service, self.button_station_new_service, self.button_ogc_new_service ]: self._build_add_service_menu(b) for b in [ self.button_fdsn_edit_service, self.button_macro_edit_service, self.button_station_edit_service, self.button_ogc_edit_service ]: b.clicked.connect(self._edit_service) for b in [ self.button_fdsn_rename_service, self.button_macro_rename_service, self.button_station_rename_service, self.button_ogc_rename_service ]: b.clicked.connect(self._rename_service) for b in [ self.button_fdsn_remove_service, self.button_macro_remove_service, self.button_station_remove_service, self.button_ogc_remove_service ]: b.clicked.connect(self._remove_service) for b in [ self.button_fdsn_export_service, self.button_macro_export_service, self.button_station_export_service, self.button_ogc_export_service ]: b.clicked.connect(self._export_service) self._restore_settings() self._refresh_url(SERVICE_MANAGER.FDSNEVENT) self._refresh_url(SERVICE_MANAGER.MACROSEISMIC) self._refresh_url(SERVICE_MANAGER.FDSNSTATION)
class TemporalSpectralProfilePlugin(object): POINT_SELECTION = 0 SELECTED_POLYGON = 1 def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() self.wdg = None self.pointTool = None def initGui(self): # create action self.action = QAction( QIcon( ":/plugins/temporalprofiletool/icons/temporalProfileIcon.png"), "Temporal/Spectral Profile", self.iface.mainWindow()) self.action.setWhatsThis("Plots temporal/spectral profiles") self.action.triggered.connect(self.run) self.aboutAction = QAction("About", self.iface.mainWindow()) self.aboutAction.triggered.connect(self.about) # add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu("&Profile Tool", self.action) self.iface.addPluginToMenu("&Profile Tool", self.aboutAction) #Init class variables self.pointTool = ProfiletoolMapTool(self.iface.mapCanvas(), self.action) #the mouselistener self.dockOpened = False #remember for not reopening dock if there's already one opened self.mdl = None #the model whitch in are saved layers analysed caracteristics self.selectionmethod = 0 #The selection method defined in option self.saveTool = self.canvas.mapTool( ) #Save the standard mapttool for restoring it at the end self.plotlibrary = None #The plotting library to use self.pointSelectionInstructions = "Click on a raster for temporal/spectral profile (right click to cancel then quit)" self.selectedPolygonInstructions = 'Use "Select Features" tool to select polygon(s) designating AOI for which temporal/spectral profile should be calculated' def unload(self): if not self.wdg is None: self.wdg.close() self.iface.removeToolBarIcon(self.action) self.iface.removePluginMenu("&Profile Tool", self.action) self.iface.removePluginMenu("&Profile Tool", self.aboutAction) def run(self): # first, check posibility if self.checkIfOpening() == False: return #if dock not already opened, open the dock and all the necessary thing (model,doProfile...) if self.dockOpened == False: self.mdl = QStandardItemModel(0, 6) self.wdg = PTDockWidget(self.iface.mainWindow(), self.iface, self.mdl) self.wdg.showIt() self.doprofile = DoProfile(self.iface, self.wdg, self.pointTool, self) self.tableViewTool = TableViewTool() self.wdg.closed.connect(self.cleaning2) self.wdg.tableView.clicked.connect(self._onClick) self.wdg.pushButton_2.clicked.connect(self.addLayer) self.wdg.pushButton.clicked.connect(self.removeLayer) self.wdg.comboBox.currentIndexChanged.connect(self.selectionMethod) self.wdg.cboLibrary.currentIndexChanged.connect( self.changePlotLibrary) self.wdg.cboXAxis.currentIndexChanged.connect( self.changeXAxisLabeling) self.wdg.leXAxisSteps.editingFinished.connect( self.changeXAxisLabeling) self.wdg.dateTimeEditCurrentTime.editingFinished.connect( self.changeXAxisLabeling) self.wdg.spinBoxTimeExtent.editingFinished.connect( self.changeXAxisLabeling) self.wdg.cboTimeExtent.currentIndexChanged.connect( self.changeXAxisLabeling) self.wdg.cbTimeDimension.stateChanged.connect( self.changeXAxisLabeling) self.wdg.addOptionComboboxItems() self.addLayer() self.dockOpened = True #Listeners of mouse self.connectPointMapTool() #init the mouse listener comportement and save the classic to restore it on quit self.canvas.setMapTool(self.pointTool) #Help about what doing if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION: self.iface.mainWindow().statusBar().showMessage( self.pointSelectionInstructions) elif self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON: self.iface.mainWindow().statusBar().showMessage( self.selectedPolygonInstructions) QgsProject.instance().layersWillBeRemoved.connect( self.onLayersWillBeRemoved) #************************************* Canvas listener actions ********************************************** # Used when layer is about to be removed from QGIS Map Layer Registry def onLayersWillBeRemoved(self, layersIds): if self.mdl is not None: for layerId in layersIds: for row in range(self.mdl.rowCount()): if layerId == self.mdl.index(row, 3).data().id(): self.removeLayer(row) self.onLayersWillBeRemoved(layersIds) break # Use for selected polygon option def selectionChanged(self, layer): if not layer.geometryType() == QgsWkbTypes.PolygonGeometry: return fullGeometry = QgsGeometry() for feature in layer.selectedFeatures(): if fullGeometry.isEmpty(): fullGeometry = QgsGeometry(feature.geometry()) else: fullGeometry = fullGeometry.combine(feature.geometry()) if not fullGeometry.isEmpty(): crs = osr.SpatialReference() crs.ImportFromProj4(str(layer.crs().toProj4())) self.doprofile.calculatePolygonProfile(fullGeometry, crs, self.mdl, self.plotlibrary) #************************************* Mouse listener actions *********************************************** # Used for point selection option def moved(self, point): if self.wdg and not self.wdg.cbPlotWhenClick.isChecked(): if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION: self.doubleClicked(point) if self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON: pass def rightClicked(self, point): #used to quit the current action self.cleaning() def leftClicked(self, point): self.doubleClicked(point) def doubleClicked(self, point): if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION: self.iface.mainWindow().statusBar().showMessage( str(point.x()) + ", " + str(point.y())) self.doprofile.calculatePointProfile(point, self.mdl, self.plotlibrary) if self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON: return #***************************** open and quit options ******************************************* def checkIfOpening(self): if self.iface.mapCanvas().layerCount() == 0: #Check a layer is opened QMessageBox.warning(self.iface.mainWindow(), "Profile", "First open a raster layer, please") return False layer = self.iface.activeLayer() if layer == None or not isProfilable( layer): #Check if a raster layer is opened and selected if self.mdl == None or self.mdl.rowCount() == 0: QMessageBox.warning(self.iface.mainWindow(), "Profile Tool", "Please select a raster layer") return False return True def connectPointMapTool(self): self.pointTool.moved.connect(self.moved) self.pointTool.rightClicked.connect(self.rightClicked) self.pointTool.leftClicked.connect(self.leftClicked) self.pointTool.doubleClicked.connect(self.doubleClicked) def deactivatePointMapTool(self): #enable clean exit of the plugin self.pointTool.moved.disconnect(self.moved) self.pointTool.leftClicked.disconnect(self.leftClicked) self.pointTool.rightClicked.disconnect(self.rightClicked) self.pointTool.doubleClicked.disconnect(self.doubleClicked) self.iface.mainWindow().statusBar().showMessage("") def connectSelectedPolygonsTool(self): self.iface.mapCanvas().selectionChanged.connect(self.selectionChanged) def deactivateSelectedPolygonsTools(self): self.iface.mapCanvas().selectionChanged.disconnect( self.selectionChanged) def cleaning(self): #used on right click self.canvas.unsetMapTool(self.pointTool) self.canvas.setMapTool(self.saveTool) self.iface.mainWindow().statusBar().showMessage("") def cleaning2(self): #used when Dock dialog is closed self.wdg.tableView.clicked.disconnect(self._onClick) if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION: self.deactivatePointMapTool() else: self.deactivateSelectedPolygonsTools() self.selectionmethod = TemporalSpectralProfilePlugin.POINT_SELECTION self.wdg.comboBox.currentIndexChanged.disconnect(self.selectionMethod) QgsProject.instance().layersWillBeRemoved.disconnect( self.onLayersWillBeRemoved) self.mdl = None self.dockOpened = False self.cleaning() self.wdg = None #***************************** Options ******************************************* def selectionMethod(self, item): if item == TemporalSpectralProfilePlugin.POINT_SELECTION: self.selectionmethod = TemporalSpectralProfilePlugin.POINT_SELECTION self.pointTool.setCursor(Qt.CrossCursor) self.deactivateSelectedPolygonsTools() self.connectPointMapTool() if not self.canvas.mapTool() == self.pointTool: self.canvas.setMapTool(self.pointTool) self.iface.mainWindow().statusBar().showMessage( self.pointSelectionInstructions) self.wdg.changeStatComboBoxItems( self.doprofile.getPointProfileStatNames()) elif item == TemporalSpectralProfilePlugin.SELECTED_POLYGON: self.selectionmethod = TemporalSpectralProfilePlugin.SELECTED_POLYGON self.deactivatePointMapTool() self.connectSelectedPolygonsTool() self.iface.actionSelectRectangle().trigger() self.iface.mainWindow().statusBar().showMessage( self.selectedPolygonInstructions) self.wdg.changeStatComboBoxItems( self.doprofile.getPolygonProfileStatNames(), "mean") def changePlotLibrary(self, item): self.plotlibrary = self.wdg.cboLibrary.itemText(item) self.wdg.addPlotWidget(self.plotlibrary) self.changeXAxisLabeling() def changeXAxisLabeling(self): self.xAxisSteps = {} # default band number labeling if self.wdg.cboXAxis.currentIndex() == 0: self.doprofile.xAxisSteps = None # Labels from string elif self.wdg.cboXAxis.currentIndex() == 1: self.doprofile.xAxisSteps = self.wdg.leXAxisSteps.text().split(';') try: self.doprofile.xAxisSteps = [ float(x) for x in self.doprofile.xAxisSteps ] except ValueError: self.doprofile.xAxisSteps = None text = "Temporal/Spectral Profile Tool: The X-axis steps' string " + \ "is invalid. Using band numbers instead." self.iface.messageBar().pushWidget( self.iface.messageBar().createMessage(text), QgsMessageBar.WARNING, 5) # Labels based on time elif self.wdg.cboXAxis.currentIndex() == 2: self.doprofile.xAxisSteps = [ "Timesteps", self.wdg.dateTimeEditCurrentTime.dateTime().toPyDateTime(), int(self.wdg.spinBoxTimeExtent.cleanText()), self.wdg.cboTimeExtent.currentText(), self.wdg.cbTimeDimension.isChecked() ] if self.plotlibrary == "Qwt5": text = "Temporal/Spectral Profile Tool: There is currently no support using " + \ "Time steps while using the Qwt plotlibrary" self.iface.messageBar().pushWidget( self.iface.messageBar().createMessage(text), QgsMessageBar.WARNING, 5) self.doprofile.xAxisSteps = None #************************* tableview function ****************************************** def addLayer(self): layer = self.iface.activeLayer() if isProfilable(layer): self.tableViewTool.addLayer(self.iface, self.mdl, layer) def _onClick(self, index1): #action when clicking the tableview self.tableViewTool.onClick(self.iface, self.wdg, self.mdl, self.plotlibrary, index1) def removeLayer(self, index=None): if index is None or index is False: index = self.tableViewTool.chooseLayerForRemoval( self.iface, self.mdl) if index is not None: self.tableViewTool.removeLayer(self.mdl, index) PlottingTool().clearData(self.wdg, self.mdl, self.plotlibrary) def about(self): from .ui.dlgabout import DlgAbout DlgAbout(self.iface.mainWindow()).exec_()
class QQuakeDialog(QDialog, FORM_CLASS): """ The main plugin dialog """ def __init__(self, iface, parent=None): # pylint:disable=too-many-statements """Constructor.""" super().__init__(parent) self.setupUi(self) self.setObjectName('QQuakeDialog') QgsGui.enableAutoGeometryRestore(self) self.scrollArea.setStyleSheet(""" QScrollArea { background: transparent; } QScrollArea > QWidget > QWidget { background: transparent; } QScrollArea > QWidget > QScrollBar { background: 1; } """) self.scrollArea_2.setStyleSheet(self.scrollArea.styleSheet()) self.scrollArea_3.setStyleSheet(self.scrollArea.styleSheet()) self.scrollArea_4.setStyleSheet(self.scrollArea.styleSheet()) self.splitter.setStretchFactor(0, 0) self.splitter_2.setStretchFactor(0, 0) self.splitter_3.setStretchFactor(0, 0) self.splitter_4.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.splitter_2.setStretchFactor(1, 1) self.splitter_3.setStretchFactor(1, 1) self.splitter_4.setStretchFactor(1, 1) self.fdsn_event_filter = FilterParameterWidget( iface, SERVICE_MANAGER.FDSNEVENT) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.fdsn_event_filter) self.fdsn_event_filter_container.setLayout(vl) self.earthquake_service_info_widget = ServiceInformationWidget(iface) self.fdsn_by_id_filter = FilterByIdWidget(iface, SERVICE_MANAGER.FDSNEVENT) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.fdsn_by_id_filter) self.fdsn_by_id_container.setLayout(vl) self.fdsn_by_url_widget = FetchByUrlWidget(iface, SERVICE_MANAGER.FDSNEVENT) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.fdsn_by_url_widget) self.fdsn_by_url_container.setLayout(vl) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.earthquake_service_info_widget) self.earthquake_service_info_container.setLayout(vl) self.macro_filter = FilterParameterWidget(iface, SERVICE_MANAGER.MACROSEISMIC) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.macro_filter) self.macro_filter_container.setLayout(vl) self.macro_by_id_filter = FilterByIdWidget( iface, SERVICE_MANAGER.MACROSEISMIC) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.macro_by_id_filter) self.macro_by_id_container.setLayout(vl) self.macro_by_url_widget = FetchByUrlWidget( iface, SERVICE_MANAGER.MACROSEISMIC) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.macro_by_url_widget) self.macro_by_url_container.setLayout(vl) self.macro_service_info_widget = ServiceInformationWidget(iface) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.macro_service_info_widget) self.macro_service_info_container.setLayout(vl) self.station_filter = FilterParameterWidget( iface, SERVICE_MANAGER.FDSNSTATION) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.station_filter) self.station_filter_container.setLayout(vl) self.station_by_id_filter = FilterStationByIdWidget( iface, SERVICE_MANAGER.FDSNSTATION) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.station_by_id_filter) self.station_by_id_container.setLayout(vl) self.station_service_info_widget = ServiceInformationWidget(iface) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.station_service_info_widget) self.station_service_info_container.setLayout(vl) self.station_by_url_widget = FetchByUrlWidget( iface, SERVICE_MANAGER.FDSNSTATION) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.station_by_url_widget) self.station_by_url_container.setLayout(vl) self.ogc_service_widget = OgcServiceWidget(iface) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.ogc_service_widget) self.ogc_widget_container.setLayout(vl) self.ogc_service_info_widget = ServiceInformationWidget(iface) vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.ogc_service_info_widget) self.ogc_service_info_container.setLayout(vl) self.message_bar = QgsMessageBar() self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.insertWidget(0, self.message_bar) self.fdsn_event_url_text_browser.viewport().setAutoFillBackground( False) self.fdsn_macro_url_text_browser.viewport().setAutoFillBackground( False) self.fdsn_station_url_text_browser.viewport().setAutoFillBackground( False) self.button_box.button(QDialogButtonBox.Ok).setText( self.tr('Fetch Data')) self.button_box.rejected.connect(self._save_settings) self.iface = iface # OGC self.ogc_combo.addItem(self.tr('Web Map Services (WMS)'), SERVICE_MANAGER.WMS) self.ogc_combo.addItem(self.tr('Web Feature Services (WFS)'), SERVICE_MANAGER.WFS) self.ogc_combo.currentIndexChanged.connect(self.refreshOgcWidgets) self.ogc_list_model = QStandardItemModel(self.ogc_list) self.ogc_list.setModel(self.ogc_list_model) self.ogc_list.selectionModel().selectionChanged.connect( self._ogc_service_changed) self._refresh_services() SERVICE_MANAGER.refreshed.connect(self._refresh_services) # connect to refreshing function to refresh the UI depending on the WS self._refresh_fdsnevent_widgets() self.refreshFdsnMacroseismicWidgets() self.refreshFdsnStationWidgets() # change the UI parameter according to the web service chosen self.fdsn_event_list.currentRowChanged.connect( self._refresh_fdsnevent_widgets) self.fdsn_macro_list.currentRowChanged.connect( self.refreshFdsnMacroseismicWidgets) self.fdsn_station_list.currentRowChanged.connect( self.refreshFdsnStationWidgets) self.fdsn_event_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.fdsn_by_id_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.fdsn_by_url_widget.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.fdsn_event_list.currentRowChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.macro_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.macro_by_id_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.macro_by_url_widget.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.fdsn_macro_list.currentRowChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.station_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) self.station_by_id_filter.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) self.fdsn_station_list.currentRowChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) self.station_by_url_widget.changed.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) self.button_box.accepted.connect(self._getEventList) self.service_tab_widget.currentChanged.connect( lambda: self._refresh_url(None)) self.fetcher = None QgsGui.enableAutoGeometryRestore(self) self.fdsn_tab_widget.currentChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNEVENT)) self.macro_tab_widget.currentChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.MACROSEISMIC)) self.fdsnstation_tab_widget.currentChanged.connect( lambda: self._refresh_url(SERVICE_MANAGER.FDSNSTATION)) for b in [ self.button_fdsn_new_service, self.button_macro_new_service, self.button_station_new_service, self.button_ogc_new_service ]: self._build_add_service_menu(b) for b in [ self.button_fdsn_edit_service, self.button_macro_edit_service, self.button_station_edit_service, self.button_ogc_edit_service ]: b.clicked.connect(self._edit_service) for b in [ self.button_fdsn_rename_service, self.button_macro_rename_service, self.button_station_rename_service, self.button_ogc_rename_service ]: b.clicked.connect(self._rename_service) for b in [ self.button_fdsn_remove_service, self.button_macro_remove_service, self.button_station_remove_service, self.button_ogc_remove_service ]: b.clicked.connect(self._remove_service) for b in [ self.button_fdsn_export_service, self.button_macro_export_service, self.button_station_export_service, self.button_ogc_export_service ]: b.clicked.connect(self._export_service) self._restore_settings() self._refresh_url(SERVICE_MANAGER.FDSNEVENT) self._refresh_url(SERVICE_MANAGER.MACROSEISMIC) self._refresh_url(SERVICE_MANAGER.FDSNSTATION) def closeEvent(self, e): # pylint: disable=missing-function-docstring self._save_settings() super().closeEvent(e) def _build_add_service_menu(self, widget): """ Builds the add service menu for a specific widget """ menu = QMenu() save_action = QAction(self.tr('Save Current Configuration As…'), parent=menu) save_action.setObjectName('save_action') menu.addAction(save_action) save_action.triggered.connect(self._save_configuration) import_action = QAction(self.tr('Import from File…'), parent=menu) menu.addAction(import_action) import_action.triggered.connect(self._import_configuration) create_new_action = QAction(self.tr('Create New Service…'), parent=menu) menu.addAction(create_new_action) create_new_action.triggered.connect(self._create_configuration) menu.aboutToShow.connect(lambda: self._menu_about_to_show(menu)) widget.setMenu(menu) def _menu_about_to_show(self, menu): """ Triggered when the Add Service menu is about to show """ save_current_action = menu.findChild(QAction, 'save_action') service_type = self.get_current_service_type() filter_widget = self.get_service_filter_widget(service_type) save_current_action.setEnabled( hasattr(filter_widget, 'to_service_definition')) def _refresh_services(self): """ Refreshes the list of available services """ # fill the FDSN listWidget with the dictionary keys self.fdsn_event_list.clear() self.fdsn_event_list.addItems( SERVICE_MANAGER.available_services(SERVICE_MANAGER.FDSNEVENT)) self.fdsn_event_list.setCurrentRow(0) # fill the FDSN listWidget with the dictionary keys self.fdsn_macro_list.clear() self.fdsn_macro_list.addItems( SERVICE_MANAGER.available_services(SERVICE_MANAGER.MACROSEISMIC)) self.fdsn_macro_list.setCurrentRow(0) # fill the FDSN listWidget with the dictionary keys self.fdsn_station_list.clear() self.fdsn_station_list.addItems( SERVICE_MANAGER.available_services(SERVICE_MANAGER.FDSNSTATION)) self.fdsn_station_list.setCurrentRow(0) self.refreshOgcWidgets() def _save_configuration(self): """ Triggers saving the current service configuration """ service_type = self.get_current_service_type() name, ok = QInputDialog.getText( self, self.tr('Save Service Configuration'), self.tr('Save the current service configuration as')) if not name or not ok: return filter_widget = self.get_service_filter_widget(service_type) SERVICE_MANAGER.save_service(service_type, name, filter_widget.to_service_definition()) self.set_current_service(service_type, name) def _save_settings(self): """ Saves all settings currently defined in the dialog """ s = QgsSettings() if self.service_tab_widget.currentIndex( ) != self.service_tab_widget.count() - 1: s.setValue('/plugins/qquake/last_tab', self.service_tab_widget.currentIndex()) s.setValue('/plugins/qquake/fdsn_event_last_event_service', self.fdsn_event_list.currentItem().text()) s.setValue('/plugins/qquake/macro_last_event_service', self.fdsn_macro_list.currentItem().text()) s.setValue('/plugins/qquake/fdsnevent_last_tab', self.fdsn_tab_widget.currentIndex()) s.setValue('/plugins/qquake/macro_last_tab', self.macro_tab_widget.currentIndex()) s.setValue('/plugins/qquake/station_last_tab', self.fdsnstation_tab_widget.currentIndex()) self.fdsn_event_filter.save_settings('fdsn_event') self.fdsn_by_id_filter.save_settings('fdsn_event') self.fdsn_by_url_widget.save_settings('fdsn_event') self.macro_filter.save_settings('macro') self.macro_by_id_filter.save_settings('macro') self.macro_by_url_widget.save_settings('macro') self.station_filter.save_settings('stations') self.station_by_id_filter.save_settings('stations') self.station_by_url_widget.save_settings('stations') def _restore_settings(self): """ Restores dialog settings """ s = QgsSettings() last_tab = s.value('/plugins/qquake/last_tab') if last_tab is not None: self.service_tab_widget.setCurrentIndex(int(last_tab)) last_service = s.value('/plugins/qquake/fdsn_event_last_event_service') if last_service is not None: try: self.fdsn_event_list.setCurrentItem( self.fdsn_event_list.findItems(last_service, Qt.MatchContains)[0]) except IndexError: pass last_service = s.value('/plugins/qquake/macro_last_event_service') if last_service is not None: self.fdsn_macro_list.setCurrentItem( self.fdsn_macro_list.findItems(last_service, Qt.MatchContains)[0]) self.fdsn_event_filter.restore_settings('fdsn_event') self.fdsn_by_id_filter.restore_settings('fdsn_event') self.fdsn_by_url_widget.restore_settings('fdsn_event') self.macro_filter.restore_settings('macro') self.macro_by_id_filter.restore_settings('macro') self.macro_by_url_widget.restore_settings('fdsn_event') self.station_filter.restore_settings('stations') self.station_by_id_filter.restore_settings('stations') self.station_by_url_widget.restore_settings('stations') self.fdsn_tab_widget.setCurrentIndex( s.value('/plugins/qquake/fdsnevent_last_tab', 0, int)) self.macro_tab_widget.setCurrentIndex( s.value('/plugins/qquake/macro_last_tab', 0, int)) self.fdsnstation_tab_widget.setCurrentIndex( s.value('/plugins/qquake/station_last_tab', 0, int)) def get_current_service_id(self, service_type: str) -> Optional[str]: """ Returns the current selected service id """ if service_type == SERVICE_MANAGER.FDSNEVENT: service_id = self.fdsn_event_list.currentItem().text( ) if self.fdsn_event_list.currentItem() else None elif service_type == SERVICE_MANAGER.MACROSEISMIC: service_id = self.fdsn_macro_list.currentItem().text( ) if self.fdsn_macro_list.currentItem() else None elif service_type == SERVICE_MANAGER.FDSNSTATION: service_id = self.fdsn_station_list.currentItem().text( ) if self.fdsn_station_list.currentItem() else None elif service_type in (SERVICE_MANAGER.WMS, SERVICE_MANAGER.WFS): service_id = self.ogc_list.selectionModel().selectedIndexes( )[0].data() if self.ogc_list.selectionModel().selectedIndexes( ) else None else: service_id = None return service_id def get_current_service_type(self) -> Optional[str]: """ Returns the current service type """ if self.service_tab_widget.currentIndex() == 0: service_type = SERVICE_MANAGER.FDSNEVENT elif self.service_tab_widget.currentIndex() == 1: service_type = SERVICE_MANAGER.MACROSEISMIC elif self.service_tab_widget.currentIndex() == 2: service_type = SERVICE_MANAGER.FDSNSTATION elif self.service_tab_widget.currentIndex() == 3: return self.ogc_combo.currentData() else: service_type = None return service_type def get_service_filter_widget( self, # pylint:disable=too-many-branches service_type: str ) -> Optional[ Union[FilterParameterWidget, FilterByIdWidget, FetchByUrlWidget, FilterStationByIdWidget, OgcServiceWidget]]: """ Returns the service filter widget for a specific service type """ widget = None if service_type == SERVICE_MANAGER.FDSNEVENT: if self.fdsn_tab_widget.currentIndex() in (0, 3): widget = self.fdsn_event_filter elif self.fdsn_tab_widget.currentIndex() == 1: widget = self.fdsn_by_id_filter elif self.fdsn_tab_widget.currentIndex() == 2: widget = self.fdsn_by_url_widget elif service_type == SERVICE_MANAGER.MACROSEISMIC: if self.macro_tab_widget.currentIndex() in (0, 3): widget = self.macro_filter elif self.macro_tab_widget.currentIndex() == 1: widget = self.macro_by_id_filter elif self.macro_tab_widget.currentIndex() == 2: widget = self.macro_by_url_widget elif service_type == SERVICE_MANAGER.FDSNSTATION: if self.fdsnstation_tab_widget.currentIndex() in (0, 3): widget = self.station_filter elif self.fdsnstation_tab_widget.currentIndex() == 1: widget = self.station_by_id_filter elif self.fdsnstation_tab_widget.currentIndex() == 2: widget = self.station_by_url_widget elif service_type in (SERVICE_MANAGER.WMS, SERVICE_MANAGER.WFS): widget = self.ogc_service_widget return widget def get_fetcher(self, service_type: Optional[str] = None): """ Returns a quake fetcher corresponding to the current dialog settings """ if service_type is None: service_type = self.get_current_service_type() service = self.get_current_service_id(service_type) if not service: return None filter_widget = self.get_service_filter_widget(service_type) service_config = SERVICE_MANAGER.service_details(service_type, service) if isinstance(filter_widget, FilterParameterWidget): fetcher = Fetcher( service_type=service_type, event_service=service, event_start_date=filter_widget.start_date(), event_end_date=filter_widget.end_date(), event_min_magnitude=filter_widget.min_magnitude(), event_max_magnitude=filter_widget.max_magnitude(), limit_extent_rect=filter_widget.extent_rect(), min_latitude=filter_widget.min_latitude(), max_latitude=filter_widget.max_latitude(), min_longitude=filter_widget.min_longitude(), max_longitude=filter_widget.max_longitude(), limit_extent_circle=filter_widget.limit_extent_circle(), circle_latitude=filter_widget.circle_latitude(), circle_longitude=filter_widget.circle_longitude(), circle_min_radius=filter_widget.circle_min_radius(), circle_max_radius=filter_widget.circle_max_radius(), circle_radius_unit=filter_widget.circle_radius_unit(), earthquake_number_mdps_greater=filter_widget. earthquake_number_mdps_greater(), earthquake_max_intensity_greater=filter_widget. earthquake_max_intensity_greater(), output_fields=filter_widget.output_fields, output_type=filter_widget.output_type(), convert_negative_depths=filter_widget.convert_negative_depths( ), depth_unit=filter_widget.depth_unit(), event_type=filter_widget.event_type(), updated_after=filter_widget.updated_after()) elif isinstance(filter_widget, FilterByIdWidget): if not service_config['settings'].get('queryeventid'): fetcher = None else: fetcher = Fetcher( service_type=service_type, event_service=service, event_ids=filter_widget.ids(), contributor_id=filter_widget.contributor_id(), output_fields=filter_widget.output_fields, output_type=filter_widget.output_type(), convert_negative_depths=filter_widget. convert_negative_depths(), depth_unit=filter_widget.depth_unit()) elif isinstance(filter_widget, FetchByUrlWidget): fetcher = Fetcher(service_type=service_type, event_service=service, url=filter_widget.url(), output_fields=filter_widget.output_fields, output_type=filter_widget.output_type(), convert_negative_depths=filter_widget. convert_negative_depths(), depth_unit=filter_widget.depth_unit()) elif isinstance(filter_widget, FilterStationByIdWidget): fetcher = Fetcher(service_type=service_type, event_service=service, network_codes=filter_widget.network_codes(), station_codes=filter_widget.station_codes(), locations=filter_widget.locations(), output_fields=filter_widget.output_fields, output_type=filter_widget.output_type(), convert_negative_depths=filter_widget. convert_negative_depths(), depth_unit=filter_widget.depth_unit()) return fetcher def _refresh_url(self, service_type: Optional[str] = None): """ Updates the service URL """ if not service_type: service_type = self.get_current_service_type() if service_type is None: self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) return if service_type not in (SERVICE_MANAGER.FDSNEVENT, SERVICE_MANAGER.MACROSEISMIC, SERVICE_MANAGER.FDSNSTATION): self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) return fetcher = self.get_fetcher(service_type) if not fetcher: self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) return self._valid_changed() if service_type == SERVICE_MANAGER.FDSNEVENT: self.fdsn_event_url_text_browser.setText( '<a href="{0}">{0}</a>'.format(fetcher.generate_url())) elif service_type == SERVICE_MANAGER.MACROSEISMIC: self.fdsn_macro_url_text_browser.setText( '<a href="{0}">{0}</a>'.format(fetcher.generate_url())) elif service_type == SERVICE_MANAGER.FDSNSTATION: self.fdsn_station_url_text_browser.setText( '<a href="{0}">{0}</a>'.format(fetcher.generate_url())) def _valid_changed(self): """ Called when dialog entry validation should occur """ service_type = self.get_current_service_type() if service_type not in (SERVICE_MANAGER.FDSNEVENT, SERVICE_MANAGER.MACROSEISMIC, SERVICE_MANAGER.FDSNSTATION): self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) return filter_widget = self.get_service_filter_widget(service_type) self.button_box.button(QDialogButtonBox.Ok).setEnabled( filter_widget.is_valid()) def _update_service_widgets( self, # pylint: disable=too-many-locals,too-many-branches service_type, service_id, filter_widget, filter_by_id_widget, fetch_by_url_widget, info_widget, remove_service_button, edit_service_button, rename_service_button, tab_widget): """ Updates all widgets to reflect the current service details """ service_config = SERVICE_MANAGER.service_details( service_type, service_id) date_start = QDateTime.fromString(service_config['datestart'], Qt.ISODate) default_date_start = QDateTime.fromString( service_config['default']['datestart'], Qt.ISODate) if service_config['default'].get('datestart') else None # if the dateend is not set in the config.json set the date to NOW date_end = QDateTime.fromString( service_config['dateend'], Qt.ISODate) if 'dateend' in service_config and service_config[ 'dateend'] else None default_date_end = QDateTime.fromString( service_config['default']['dateend'], Qt.ISODate) if service_config['default'].get('dateend') else None filter_widget.set_date_range_limits(date_start, date_end) filter_widget.set_current_date_range(default_date_start, default_date_end) if service_config['default'].get('boundingboxpredefined'): filter_widget.set_predefined_bounding_box( service_config['default'].get('boundingboxpredefined')) if service_config['default'].get('minimumlatitude'): filter_widget.set_min_latitude( service_config['default'].get('minimumlatitude')) if service_config['default'].get('maximumlatitude'): filter_widget.set_max_latitude( service_config['default'].get('maximumlatitude')) if service_config['default'].get('minimumlongitude'): filter_widget.set_min_longitude( service_config['default'].get('minimumlongitude')) if service_config['default'].get('maximumlongitude'): filter_widget.set_max_longitude( service_config['default'].get('maximumlongitude')) if service_config['default'].get('circlelatitude'): filter_widget.set_circle_latitude( service_config['default'].get('circlelatitude')) if service_config['default'].get('circlelongitude'): filter_widget.set_circle_longitude( service_config['default'].get('circlelongitude')) if service_config['default'].get('minimumcircleradius'): filter_widget.set_min_circle_radius( service_config['default'].get('minimumcircleradius')) if service_config['default'].get('maximumcircleradius'): filter_widget.set_max_circle_radius( service_config['default'].get('maximumcircleradius')) if service_config['default'].get('minimummagnitude'): filter_widget.set_min_magnitude( service_config['default'].get('minimummagnitude')) if service_config['default'].get('maximummagnitude'): filter_widget.set_max_magnitude( service_config['default'].get('maximummagnitude')) if service_config['default'].get('macromaxintensitygreater'): filter_widget.set_max_intensity_greater( service_config['default'].get('macromaxintensitygreater')) if service_config['default'].get('macromdpsgreaterthan'): filter_widget.set_mdps_greater_than( service_config['default'].get('macromdpsgreaterthan')) if service_config['default'].get('eventtype'): filter_widget.set_event_type( service_config['default'].get('eventtype')) updated_after = QDateTime.fromString( service_config['default']['updatedafter'], Qt.ISODate ) if service_config['default'].get('updatedafter') else None if updated_after: filter_widget.set_updated_after(updated_after) filter_widget.set_extent_limit( service_config.get('boundingbox', [-180, -90, 180, 90])) if service_type in [ SERVICE_MANAGER.FDSNEVENT, SERVICE_MANAGER.MACROSEISMIC ]: tab_widget.widget(1).setEnabled(service_config['settings'].get( 'queryeventid', False)) info_widget.set_service(service_type=service_type, service_id=service_id) filter_widget.set_service_id(service_id) filter_by_id_widget.set_service_id(service_id) if fetch_by_url_widget is not None: fetch_by_url_widget.set_service_id(service_id) remove_service_button.setEnabled(not service_config['read_only']) edit_service_button.setEnabled(not service_config['read_only']) rename_service_button.setEnabled(not service_config['read_only']) def _refresh_fdsnevent_widgets(self): """ Refreshing the FDSN-Event UI depending on the WS chosen """ if not self.fdsn_event_list.currentItem(): return service_id = self.fdsn_event_list.currentItem().text() self._update_service_widgets( service_type=SERVICE_MANAGER.FDSNEVENT, service_id=service_id, filter_widget=self.fdsn_event_filter, filter_by_id_widget=self.fdsn_by_id_filter, fetch_by_url_widget=self.fdsn_by_url_widget, info_widget=self.earthquake_service_info_widget, remove_service_button=self.button_fdsn_remove_service, edit_service_button=self.button_fdsn_edit_service, rename_service_button=self.button_fdsn_rename_service, tab_widget=self.fdsn_tab_widget) def refreshFdsnMacroseismicWidgets(self): """ Refreshing the FDSN-Macroseismic UI depending on the WS chosen """ if not self.fdsn_macro_list.currentItem(): return service_id = self.fdsn_macro_list.currentItem().text() self._update_service_widgets( service_type=SERVICE_MANAGER.MACROSEISMIC, service_id=service_id, filter_widget=self.macro_filter, filter_by_id_widget=self.macro_by_id_filter, fetch_by_url_widget=self.macro_by_url_widget, info_widget=self.macro_service_info_widget, remove_service_button=self.button_macro_remove_service, edit_service_button=self.button_macro_edit_service, rename_service_button=self.button_macro_rename_service, tab_widget=self.macro_tab_widget) def refreshFdsnStationWidgets(self): """ Refreshing the FDSN-Macroseismic UI depending on the WS chosen """ if not self.fdsn_station_list.currentItem(): return service_id = self.fdsn_station_list.currentItem().text() self._update_service_widgets( service_type=SERVICE_MANAGER.FDSNSTATION, service_id=service_id, filter_by_id_widget=self.station_by_id_filter, fetch_by_url_widget=self.station_by_url_widget, filter_widget=self.station_filter, info_widget=self.station_service_info_widget, remove_service_button=self.button_station_remove_service, edit_service_button=self.button_station_edit_service, rename_service_button=self.button_station_rename_service, tab_widget=self.fdsnstation_tab_widget) def refreshOgcWidgets(self): """ read the ogc_combo and fill it with the services """ self.ogc_list_model.clear() ogc_selection = self.ogc_combo.currentData() services = SERVICE_MANAGER.available_services(ogc_selection) group_items = {} for service in services: service_config = SERVICE_MANAGER.service_details( ogc_selection, service) group = service_config.get('group') if not group or group in group_items: continue group_item = QStandardItem(group) group_item.setFlags(Qt.ItemIsEnabled) self.ogc_list_model.appendRow([group_item]) group_items[group] = group_item first_item = None for service in services: item = QStandardItem(service) item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) item.setData(service, role=Qt.UserRole) if not first_item: first_item = item service_config = SERVICE_MANAGER.service_details( ogc_selection, service) group = service_config.get('group') if group: parent = group_items[group] parent.appendRow([item]) else: self.ogc_list_model.appendRow([item]) self.ogc_list.expandAll() first_item_index = self.ogc_list_model.indexFromItem(first_item) self.ogc_list.selectionModel().select( first_item_index, QItemSelectionModel.ClearAndSelect) service_config = SERVICE_MANAGER.service_details( ogc_selection, self.get_current_service_id(ogc_selection)) self.button_ogc_edit_service.setEnabled( not service_config['read_only']) self.button_ogc_rename_service.setEnabled( not service_config['read_only']) self.button_ogc_remove_service.setEnabled( not service_config['read_only']) def _ogc_service_changed(self, _, __): """ Triggered when the current OGC service changes """ if not self.ogc_list.selectionModel().selectedIndexes(): return current_service = self.ogc_list.selectionModel().selectedIndexes( )[0].data(Qt.UserRole) if not current_service: return self.ogc_service_widget.set_service( service_type=self.ogc_combo.currentData(), service_id=current_service) self.ogc_service_info_widget.set_service( service_type=self.ogc_combo.currentData(), service_id=current_service) service_config = SERVICE_MANAGER.service_details( self.ogc_combo.currentData(), current_service) self.button_ogc_edit_service.setEnabled( not service_config['read_only']) self.button_ogc_rename_service.setEnabled( not service_config['read_only']) self.button_ogc_remove_service.setEnabled( not service_config['read_only']) def _remove_service(self): """ Removes the current service """ service_type = self.get_current_service_type() service_id = self.get_current_service_id(service_type) if QMessageBox.question( self, self.tr('Remove Service'), self.tr('Are you sure you want to remove "{}"?'.format( service_id))) != QMessageBox.Yes: return SERVICE_MANAGER.remove_service(service_type, service_id) def _edit_service(self): """ Edits the current service """ service_type = self.get_current_service_type() service_id = self.get_current_service_id(service_type) config_dialog = ServiceConfigurationDialog(self.iface, service_type, service_id, self) if config_dialog.exec_(): self.set_current_service(service_type, service_id) def _rename_service(self): """ Renames the current service """ service_type = self.get_current_service_type() service_id = self.get_current_service_id(service_type) dlg = QgsNewNameDialog( service_id, service_id, [], existing=SERVICE_MANAGER.available_services(service_type)) dlg.setHintString(self.tr('Rename service configuration to')) dlg.setWindowTitle(self.tr('Rename Service Configuration')) dlg.setOverwriteEnabled(False) dlg.setConflictingNameWarning( self.tr('A configuration with this name already exists')) if not dlg.exec_(): return new_name = dlg.name() SERVICE_MANAGER.rename_service(service_type, service_id, new_name) self.set_current_service(service_type, new_name) def set_current_service(self, service_type: str, service_id: str): """ Sets the current service """ if service_type == SERVICE_MANAGER.FDSNEVENT: self.fdsn_event_list.setCurrentItem( self.fdsn_event_list.findItems(service_id, Qt.MatchContains)[0]) elif service_type == SERVICE_MANAGER.MACROSEISMIC: self.fdsn_macro_list.setCurrentItem( self.fdsn_macro_list.findItems(service_id, Qt.MatchContains)[0]) elif service_type == SERVICE_MANAGER.FDSNSTATION: self.fdsn_station_list.setCurrentItem( self.fdsn_station_list.findItems(service_id, Qt.MatchContains)[0]) elif service_type in (SERVICE_MANAGER.WMS, SERVICE_MANAGER.WFS): self.ogc_combo.setCurrentIndex( self.ogc_combo.findData(service_type)) indexes = self.ogc_list_model.match( self.ogc_list_model.index(0, 0), Qt.UserRole, service_id, flags=Qt.MatchExactly | Qt.MatchRecursive) if len(indexes) > 0: self.ogc_list.selectionModel().select( indexes[0], QItemSelectionModel.ClearAndSelect) def _create_configuration(self): """ Creates a new service configuration """ service_type = self.get_current_service_type() dlg = QgsNewNameDialog( '', '', [], existing=SERVICE_MANAGER.available_services(service_type)) dlg.setHintString(self.tr('Create a new service configuration named')) dlg.setWindowTitle(self.tr('New Service Configuration')) dlg.setOverwriteEnabled(False) dlg.setConflictingNameWarning( self.tr('A configuration with this name already exists')) if not dlg.exec_(): return name = dlg.name() config_dialog = ServiceConfigurationDialog(self.iface, service_type, name, self) if config_dialog.exec_(): self.set_current_service(service_type, name) def _export_service(self): """ Triggers exporting a service configuration """ service_type = self.get_current_service_type() service_id = self.get_current_service_id(service_type) file, _ = QFileDialog.getSaveFileName( self, self.tr('Export Service'), QDir.homePath() + '/{}.json'.format(service_id), 'JSON Files (*.json)') if not file: return file = QgsFileUtils.ensureFileNameHasExtension(file, ['json']) if SERVICE_MANAGER.export_service(service_type, service_id, file): self.message_bar.pushMessage(self.tr("Service exported"), Qgis.Success, 5) else: self.message_bar.pushMessage( self.tr("An error occurred while exporting service"), Qgis.Critical, 5) def _import_configuration(self): """ Triggers importing a configuration """ file, _ = QFileDialog.getOpenFileName(self, self.tr('Import Service'), QDir.homePath(), 'JSON Files (*.json)') if not file: return res, err = SERVICE_MANAGER.import_service(file) if res: self.message_bar.pushMessage(self.tr("Service imported"), Qgis.Success, 5) else: self.message_bar.pushMessage(err, Qgis.Critical, 5) def _getEventList(self): """ read the event URL and convert the response in a list """ if self.get_current_service_type() in (SERVICE_MANAGER.WMS, SERVICE_MANAGER.WFS): self.ogc_service_widget.add_selected_layers() return if self.fetcher: # TODO - cancel current request return self.fetcher = self.get_fetcher() def on_started(): self.progressBar.setValue(0) self.progressBar.setRange(0, 0) def on_progress(progress: float): self.progressBar.setRange(0, 100) self.progressBar.setValue(progress) self.fetcher.started.connect(on_started) self.fetcher.progress.connect(on_progress) self.fetcher.finished.connect(self._fetcher_finished) self.fetcher.message.connect(self._fetcher_message) self.button_box.button(QDialogButtonBox.Ok).setText( self.tr('Fetching')) self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.fetcher.fetch_data() def _fetcher_message(self, message, level): """ Handles message feedback from a fetcher """ self.message_bar.clearWidgets() self.message_bar.pushMessage(message, level, 0) def _fetcher_finished(self, res): # pylint: disable=too-many-branches """ Triggered when a fetcher is finished """ self.progressBar.setRange(0, 100) self.progressBar.reset() self.button_box.button(QDialogButtonBox.Ok).setText( self.tr('Fetch Data')) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) if not res: self.fetcher.deleteLater() self.fetcher = None return found_results = False layers = [] if self.fetcher.service_type in (SERVICE_MANAGER.FDSNEVENT, SERVICE_MANAGER.MACROSEISMIC): layer = self.fetcher.create_event_layer() if layer: layers.append(layer) if self.fetcher.service_type == SERVICE_MANAGER.MACROSEISMIC: layer = self.fetcher.create_mdp_layer() if layer: layers.append(layer) if layers: events_count = layers[0].featureCount() found_results = bool(events_count) service_limit = self.fetcher.service_config['settings'].get( 'querylimitmaxentries', None) self.message_bar.clearWidgets() if service_limit is not None and events_count >= service_limit: self.message_bar.pushMessage( self.tr("Query exceeded the service's result limit"), Qgis.Critical, 0) elif events_count > 500: self.message_bar.pushMessage( self.tr( "Query returned a large number of results ({})". format(events_count)), Qgis.Warning, 0) elif events_count == 0: self.message_bar.pushMessage( self. tr("Query returned no results - possibly parameters are invalid for this service" ), Qgis.Critical, 0) else: self.message_bar.pushMessage( self.tr("Query returned {} records").format( events_count), Qgis.Success, 0) elif self.fetcher.service_type == SERVICE_MANAGER.FDSNSTATION: layers.append(self.fetcher.create_stations_layer()) stations_count = layers[0].featureCount() found_results = bool(stations_count) if stations_count == 0: self.message_bar.pushMessage( self. tr("Query returned no results - possibly parameters are invalid for this service" ), Qgis.Critical, 0) else: self.message_bar.pushMessage( self.tr("Query returned {} stations").format( stations_count), Qgis.Info, 0) else: assert False self.fetcher.deleteLater() self.fetcher = None if found_results: QgsProject.instance().addMapLayers(layers)
class checkableMapLayerList(QWidget): # create a checkable list of the map layers def __init__(self, parent=None): QWidget.__init__(self, parent) layerList = QgsProject.instance().layerTreeRoot().findLayers() self.iface = iface """for layer in layerList: print(layer.name())""" self.selectedLayers = [] layout = QVBoxLayout() self.model = QStandardItemModel() self.select_all_cb = QCheckBox('Check All') self.select_all_cb.setChecked(True) self.select_all_cb.setStyleSheet('margin-left: 5px; font: bold') #self.select_all_cb.stateChanged.connect(lambda: selectAllCheckChanged(select_all_cb, model)) layout.addWidget(self.select_all_cb) self.view = QListView() self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.view.setSelectionMode(QAbstractItemView.NoSelection) self.view.setSelectionRectVisible(False) for layer in layerList: item = QStandardItem(layer.name()) # item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) # item.setData(QVariant(Qt.Checked), Qt.CheckStateRole) item.setCheckable(True) item.setSelectable(False) item.setCheckState(QtCore.Qt.Checked) self.model.appendRow(item) self.selectedLayers.append(item) self.view.setModel(self.model) #view.clicked.connect(lambda: listviewCheckChanged(item, model, select_all_cb)) layout.addWidget(self.view) self.setLayout(layout) """if parent: parent.setLayout(layout) else: window = QWidget() window.setLayout(layout)""" #window.show() def selectAllCheckChanged(self, select_all_cb, model): TOMsMessageLog.logMessage("IN selectAllCheckChanged", level=Qgis.Info) for index in range(model.rowCount()): item = model.item(index) if item.isCheckable(): if select_all_cb.isChecked(): item.setCheckState(QtCore.Qt.Checked) self.selectedLayers.append(item) else: item.setCheckState(QtCore.Qt.Unchecked) self.selectedLayers.remove(item) TOMsMessageLog.logMessage( "IN selectAllCheckChanged: len list {}".format( len(self.selectedLayers)), level=Qgis.Info) def listviewCheckChanged(self, model, select_all_cb): ''' updates the select all checkbox based on the listview ''' # model = self.listview.model() TOMsMessageLog.logMessage("IN listviewCheckChanged", level=Qgis.Info) items = [model.item(index) for index in range(model.rowCount())] if all(item.checkState() == QtCore.Qt.Checked for item in items): select_all_cb.setTristate(False) select_all_cb.setCheckState(QtCore.Qt.Checked) elif any(item.checkState() == QtCore.Qt.Checked for item in items): select_all_cb.setTristate(True) select_all_cb.setCheckState(QtCore.Qt.PartiallyChecked) else: select_all_cb.setTristate(False) select_all_cb.setCheckState(QtCore.Qt.Unchecked) def updateSelectedLayers(self, index): #QMessageBox.information(self.iface.mainWindow(), "debug", "IN updateSelectedLayers: {}".format(self.model.itemFromIndex(index))) TOMsMessageLog.logMessage("IN updateSelectedLayers: {}".format(index), level=Qgis.Info) item = self.model.itemFromIndex(index) if item.checkState() == QtCore.Qt.Checked: self.selectedLayers.append(item) else: self.selectedLayers.remove(item) def getSelectedLayers(self): return self.selectedLayers
class ConfigDialog(BASE, WIDGET): def __init__(self, toolbox): super(ConfigDialog, self).__init__(None) self.setupUi(self) self.toolbox = toolbox self.groupIcon = QIcon() self.groupIcon.addPixmap( self.style().standardPixmap(QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off) self.groupIcon.addPixmap( self.style().standardPixmap(QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) self.model = QStandardItemModel() self.tree.setModel(self.model) self.delegate = SettingDelegate() self.tree.setItemDelegateForColumn(1, self.delegate) self.searchBox.textChanged.connect(self.textChanged) self.fillTree() self.saveMenus = False self.tree.expanded.connect(self.itemExpanded) def textChanged(self): text = str(self.searchBox.text().lower()) self._filterItem(self.model.invisibleRootItem(), text) if text: self.tree.expandAll() else: self.tree.collapseAll() def _filterItem(self, item, text): if item.hasChildren(): show = False for i in range(item.rowCount()): child = item.child(i) showChild = self._filterItem(child, text) show = (showChild or show) self.tree.setRowHidden(item.row(), item.index().parent(), not show) return show elif isinstance(item, QStandardItem): hide = bool(text) and (text not in item.text().lower()) self.tree.setRowHidden(item.row(), item.index().parent(), hide) return not hide def fillTree(self): self.fillTreeUsingProviders() def fillTreeUsingProviders(self): self.items = {} self.model.clear() self.model.setHorizontalHeaderLabels( [self.tr('Setting'), self.tr('Value')]) settings = ProcessingConfig.getSettings() rootItem = self.model.invisibleRootItem() """ Filter 'General', 'Models' and 'Scripts' items """ priorityKeys = [ self.tr('General'), self.tr('Models'), self.tr('Scripts') ] for group in priorityKeys: groupItem = QStandardItem(group) icon = ProcessingConfig.getGroupIcon(group) groupItem.setIcon(icon) groupItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [groupItem, emptyItem]) # add menu item only if it has any search matches for setting in settings[group]: if setting.hidden or setting.name.startswith("MENU_"): continue labelItem = QStandardItem(setting.description) labelItem.setIcon(icon) labelItem.setEditable(False) self.items[setting] = SettingItem(setting) groupItem.insertRow(0, [labelItem, self.items[setting]]) """ Filter 'Providers' items """ providersItem = QStandardItem(self.tr('Providers')) icon = QIcon(os.path.join(pluginPath, 'images', 'alg.svg')) providersItem.setIcon(icon) providersItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [providersItem, emptyItem]) for group in list(settings.keys()): if group in priorityKeys or group == menusSettingsGroup: continue groupItem = QStandardItem(group) icon = ProcessingConfig.getGroupIcon(group) groupItem.setIcon(icon) groupItem.setEditable(False) for setting in settings[group]: if setting.hidden: continue labelItem = QStandardItem(setting.description) labelItem.setIcon(icon) labelItem.setEditable(False) self.items[setting] = SettingItem(setting) groupItem.insertRow(0, [labelItem, self.items[setting]]) emptyItem = QStandardItem() emptyItem.setEditable(False) providersItem.appendRow([groupItem, emptyItem]) """ Filter 'Menus' items """ self.menusItem = QStandardItem(self.tr('Menus')) icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png')) self.menusItem.setIcon(icon) self.menusItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [self.menusItem, emptyItem]) button = QPushButton(self.tr('Reset to defaults')) button.clicked.connect(self.resetMenusToDefaults) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(button) layout.addStretch() widget = QWidget() widget.setLayout(layout) self.tree.setIndexWidget(emptyItem.index(), widget) providers = Processing.providers for provider in providers: providerDescription = provider.getDescription() groupItem = QStandardItem(providerDescription) icon = provider.getIcon() groupItem.setIcon(icon) groupItem.setEditable(False) for alg in provider.algs: algItem = QStandardItem(alg.i18n_name or alg.name) algItem.setIcon(icon) algItem.setEditable(False) try: settingMenu = ProcessingConfig.settings[ "MENU_" + alg.commandLineName()] settingButton = ProcessingConfig.settings[ "BUTTON_" + alg.commandLineName()] settingIcon = ProcessingConfig.settings[ "ICON_" + alg.commandLineName()] except: continue self.items[settingMenu] = SettingItem(settingMenu) self.items[settingButton] = SettingItem(settingButton) self.items[settingIcon] = SettingItem(settingIcon) menuLabelItem = QStandardItem("Menu path") menuLabelItem.setEditable(False) buttonLabelItem = QStandardItem("Add button in toolbar") buttonLabelItem.setEditable(False) iconLabelItem = QStandardItem("Icon") iconLabelItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) algItem.insertRow(0, [menuLabelItem, self.items[settingMenu]]) algItem.insertRow(0, [buttonLabelItem, self.items[settingButton]]) algItem.insertRow(0, [iconLabelItem, self.items[settingIcon]]) groupItem.insertRow(0, [algItem, emptyItem]) emptyItem = QStandardItem() emptyItem.setEditable(False) self.menusItem.appendRow([groupItem, emptyItem]) self.tree.sortByColumn(0, Qt.AscendingOrder) self.adjustColumns() def resetMenusToDefaults(self): providers = Processing.providers for provider in providers: for alg in provider.algs: d = defaultMenuEntries.get(alg.commandLineName(), "") setting = ProcessingConfig.settings["MENU_" + alg.commandLineName()] item = self.items[setting] item.setData(d, Qt.EditRole) self.saveMenus = True def accept(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) qsettings = QSettings() for setting in list(self.items.keys()): if setting.group != menusSettingsGroup or self.saveMenus: if isinstance(setting.value, bool): setting.setValue( self.items[setting].checkState() == Qt.Checked) else: try: setting.setValue(str(self.items[setting].text())) except ValueError as e: QMessageBox.warning( self, self.tr('Wrong value'), self.tr('Wrong value for parameter "%s":\n\n%s' % (setting.description, str(e)))) return setting.save(qsettings) Processing.updateAlgsList() settingsWatcher.settingsChanged.emit() QApplication.restoreOverrideCursor() QDialog.accept(self) def itemExpanded(self, idx): if idx == self.menusItem.index(): self.saveMenus = True self.adjustColumns() def adjustColumns(self): self.tree.resizeColumnToContents(0) self.tree.resizeColumnToContents(1)
class DialListCheckBox(object): """ Dialog with list check box example : 1,2,4,6,8 """ def __init__(self, values, checked_values=[]): self.dial = QDialog() self.result = "" # Layout Principal m_vbl = QVBoxLayout(self.dial) # List item checkable view = QListView() m_vbl.addWidget(view) self.m_sim = QStandardItemModel() view.setModel(self.m_sim) self._load_item(values, checked_values) # List buttons bt_all = QPushButton(self.tr("All")) bt_all.clicked.connect(lambda: self._check_all()) bt_nothing = QPushButton(self.tr("Nothing")) bt_nothing.clicked.connect(lambda: self._check_nothing()) bt_print = QPushButton(self.tr("Ok")) bt_print.clicked.connect(lambda: self._check_ok()) # Sub layout for buttons m_vbh = QHBoxLayout() m_vbl.addLayout(m_vbh) m_vbh.addWidget(bt_all) m_vbh.addWidget(bt_nothing) m_vbh.addWidget(bt_print) def _load_item(self, values, checked_values): """Load item list""" for v in values: item = QStandardItem(str(v)) if v in checked_values: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) item.setCheckable(True) self.m_sim.appendRow(item) def _check_all(self): """Check all item""" for index in range(self.m_sim.rowCount( )): ## Iteration sur le model (contient la list des items) item = self.m_sim.item(index) if item.isCheckable() and item.checkState( ) == Qt.Unchecked: # Si Unchecked item.setCheckState(Qt.Checked) def _check_nothing(self): """Uncheck all item""" for index in range(self.m_sim.rowCount( )): ## Iteration sur le model (contient la list des items) item = self.m_sim.item(index) if item.isCheckable() and item.checkState( ) == Qt.Checked: # Si Checked item.setCheckState(Qt.Unchecked) def _check_ok(self): """Get value of checked items and exit""" l_value = [] for index in range(self.m_sim.rowCount( )): ## Iteration sur le model (contient la list des items) item = self.m_sim.item(index) if item.isCheckable() and item.checkState( ) == Qt.Checked: # Si Checked l_value.append(str(item.text())) if l_value: s_value = ";".join(l_value) else: s_value = "" self.result = s_value self.dial.close() def run(self): self.dial.setWindowTitle(self.tr("Select values")) self.dial.exec_() return self.result def tr(self, string, context=''): if context == '' or context == None: context = self.__class__.__name__ return QCoreApplication.translate(context, string)
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.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText( self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) 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)
class MultipleInputDialog(BASE, WIDGET): def __init__(self, options, selectedoptions=None, datatype=None): super(MultipleInputDialog, self).__init__(None) self.setupUi(self) self.datatype = datatype self.model = None self.options = [] for i, option in enumerate(options): if option is None or isinstance(option, str): self.options.append((i, option)) else: self.options.append((option[0], option[1])) self.selectedoptions = selectedoptions or [] # Additional buttons self.btnSelectAll = QPushButton(self.tr('Select All')) self.buttonBox.addButton(self.btnSelectAll, QDialogButtonBox.ActionRole) self.btnClearSelection = QPushButton(self.tr('Clear Selection')) self.buttonBox.addButton(self.btnClearSelection, QDialogButtonBox.ActionRole) self.btnToggleSelection = QPushButton(self.tr('Toggle Selection')) self.buttonBox.addButton(self.btnToggleSelection, QDialogButtonBox.ActionRole) if self.datatype is not None: btnAddFile = QPushButton(QCoreApplication.translate("MultipleInputDialog", 'Add File(s)…')) btnAddFile.clicked.connect(self.addFiles) self.buttonBox.addButton(btnAddFile, QDialogButtonBox.ActionRole) self.btnSelectAll.clicked.connect(lambda: self.selectAll(True)) self.btnClearSelection.clicked.connect(lambda: self.selectAll(False)) self.btnToggleSelection.clicked.connect(self.toggleSelection) self.settings = QgsSettings() self.restoreGeometry(self.settings.value("/Processing/multipleInputDialogGeometry", QByteArray())) self.lstLayers.setSelectionMode(QAbstractItemView.ExtendedSelection) self.lstLayers.setDragDropMode(QAbstractItemView.InternalMove) self.populateList() self.finished.connect(self.saveWindowGeometry) def saveWindowGeometry(self): self.settings.setValue("/Processing/multipleInputDialogGeometry", self.saveGeometry()) def populateList(self): self.model = QStandardItemModel() for value, text in self.options: item = QStandardItem(text) item.setData(value, Qt.UserRole) item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked) item.setCheckable(True) item.setDropEnabled(False) self.model.appendRow(item) # add extra options (e.g. manually added layers) for t in [o for o in self.selectedoptions if not isinstance(o, int)]: if isinstance(t, QgsProcessingModelChildParameterSource): item = QStandardItem(t.staticValue()) else: item = QStandardItem(t) item.setData(item.text(), Qt.UserRole) item.setCheckState(Qt.Checked) item.setCheckable(True) item.setDropEnabled(False) self.model.appendRow(item) self.lstLayers.setModel(self.model) def accept(self): self.selectedoptions = [] model = self.lstLayers.model() for i in range(model.rowCount()): item = model.item(i) if item.checkState() == Qt.Checked: self.selectedoptions.append(item.data(Qt.UserRole)) QDialog.accept(self) def reject(self): self.selectedoptions = None QDialog.reject(self) def getItemsToModify(self): items = [] if len(self.lstLayers.selectedIndexes()) > 1: for i in self.lstLayers.selectedIndexes(): items.append(self.model.itemFromIndex(i)) else: for i in range(self.model.rowCount()): items.append(self.model.item(i)) return items def selectAll(self, value): for item in self.getItemsToModify(): item.setCheckState(Qt.Checked if value else Qt.Unchecked) def toggleSelection(self): for item in self.getItemsToModify(): checked = item.checkState() == Qt.Checked item.setCheckState(Qt.Unchecked if checked else Qt.Checked) def getFileFilter(self, datatype): """ Returns a suitable file filter pattern for the specified parameter definition :param param: :return: """ if datatype == QgsProcessing.TypeRaster: return QgsProviderRegistry.instance().fileRasterFilters() elif datatype == QgsProcessing.TypeFile: return self.tr('All files (*.*)') else: exts = QgsVectorFileWriter.supportedFormatExtensions() for i in range(len(exts)): exts[i] = self.tr('{0} files (*.{1})').format(exts[i].upper(), exts[i].lower()) return self.tr('All files (*.*)') + ';;' + ';;'.join(exts) def addFiles(self): filter = self.getFileFilter(self.datatype) settings = QgsSettings() path = str(settings.value('/Processing/LastInputPath')) ret, selected_filter = QFileDialog.getOpenFileNames(self, self.tr('Select File(s)'), path, filter) if ret: files = list(ret) settings.setValue('/Processing/LastInputPath', os.path.dirname(str(files[0]))) for filename in files: item = QStandardItem(filename) item.setData(filename, Qt.UserRole) item.setCheckState(Qt.Checked) item.setCheckable(True) item.setDropEnabled(False) self.model.appendRow(item)
class DialogImportData(QDialog, DIALOG_UI): def __init__(self, iface, db, qgis_utils): QDialog.__init__(self) self.setupUi(self) QgsGui.instance().enableAutoGeometryRestore(self) self.iface = iface self.db = db self.qgis_utils = qgis_utils self.base_configuration = BaseConfiguration() self.ilicache = IliCache(self.base_configuration) self.ilicache.refresh() self._conf_db = ConfigDbSupported() self._params = None self._current_db = None self.xtf_file_browse_button.clicked.connect( make_file_selector( self.xtf_file_line_edit, title=QCoreApplication.translate( "DialogImportData", 'Open Transfer or Catalog File'), file_filter=QCoreApplication.translate( "DialogImportData", 'Transfer File (*.xtf *.itf);;Catalogue File (*.xml *.xls *.xlsx)' ))) self.validators = Validators() self.xtf_file_line_edit.setPlaceholderText( QCoreApplication.translate("DialogImportData", "[Name of the XTF to be created]")) fileValidator = FileValidator(pattern=['*.xtf', '*.itf', '*.xml']) self.xtf_file_line_edit.setValidator(fileValidator) self.xtf_file_line_edit.textChanged.connect(self.update_import_models) self.xtf_file_line_edit.textChanged.emit( self.xtf_file_line_edit.text()) # db self.connection_setting_button.clicked.connect(self.show_settings) self.connection_setting_button.setText( QCoreApplication.translate("DialogImportData", 'Connection Settings')) # LOG self.log_config.setTitle( QCoreApplication.translate("DialogImportData", "Show log")) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) self._accept_button = self.buttonBox.addButton( QCoreApplication.translate("DialogImportData", "Import data"), QDialogButtonBox.AcceptRole) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.show_help) self.update_connection_info() self.restore_configuration() def update_connection_info(self): db_description = self.db.get_description_conn_string() if db_description: self.db_connect_label.setText(db_description) self.db_connect_label.setToolTip(self.db.get_display_conn_string()) self._accept_button.setEnabled(True) else: self.db_connect_label.setText( QCoreApplication.translate("DialogImportData", "The database is not defined!")) self.db_connect_label.setToolTip('') self._accept_button.setEnabled(False) def update_import_models(self): message_error = None if not self.xtf_file_line_edit.text().strip(): color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel(self.import_models_qmodel) else: if os.path.isfile(self.xtf_file_line_edit.text().strip()): color = '#fff' # White self.import_models_qmodel = QStandardItemModel() models_name = self.find_models_xtf( self.xtf_file_line_edit.text().strip()) for model_name in models_name: if not model_name in DEFAULT_HIDDEN_MODELS: item = QStandardItem(model_name) item.setCheckable(False) item.setEditable(False) self.import_models_qmodel.appendRow(item) if self.import_models_qmodel.rowCount() > 0: self.import_models_list_view.setModel( self.import_models_qmodel) else: message_error = QCoreApplication.translate( "DialogImportData", "No models were found in the XTF. Is it a valid file?") color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel( self.import_models_qmodel) else: message_error = QCoreApplication.translate( "DialogImportData", "Please set a valid XTF file") color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel( self.import_models_qmodel) self.xtf_file_line_edit.setStyleSheet( 'QLineEdit {{ background-color: {} }}'.format(color)) if message_error: self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.import_models_list_view.setFocus() return def find_models_xtf(self, xtf_path): models_name = list() pattern = re.compile(r'<MODEL[^>]*>(?P<text>[^<]*)</MODEL>') with open(xtf_path, 'r') as f: for txt in pattern.finditer(f.read()): model_tag = str(txt.group(0)) name = re.findall('NAME="(.*?)"', model_tag, re.IGNORECASE) models_name.extend(name) return models_name def get_ili_models(self): ili_models = list() for index in range(self.import_models_qmodel.rowCount()): item = self.import_models_qmodel.item(index) ili_models.append(item.text()) return ili_models def show_settings(self): dlg = self.qgis_utils.get_settings_dialog() dlg.set_action_type(EnumDbActionType.IMPORT) dlg.tabWidget.setCurrentIndex(SETTINGS_CONNECTION_TAB_INDEX) if dlg.exec_(): self.db = dlg.get_db_connection() self.update_connection_info() def accepted(self): configuration = self.update_configuration() if not os.path.isfile(self.xtf_file_line_edit.text().strip()): message_error = 'Please set a valid XTF file before importing data. XTF file does not exist' self.txtStdout.setText( QCoreApplication.translate("DialogImportData", message_error)) self.show_message(message_error, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.xtf_file_line_edit.validator().validate( configuration.xtffile, 0)[0] == QValidator.Acceptable: message_error = 'Please set a valid XTF before importing data.' self.txtStdout.setText( QCoreApplication.translate("DialogImportData", message_error)) self.show_message(message_error, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.get_ili_models(): message_error = QCoreApplication.translate( "DialogImportData", "The selected XTF file does not have information according to the LADM-COL model to import." ) self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.import_models_list_view.setFocus() return with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.progress_bar.setValue(0) self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() dataImporter = iliimporter.Importer(dataImport=True) item_db = self._conf_db.get_db_items()[self.db.mode] dataImporter.tool_name = item_db.get_model_baker_tool_name() dataImporter.configuration = configuration self.save_configuration(configuration) dataImporter.stdout.connect(self.print_info) dataImporter.stderr.connect(self.on_stderr) dataImporter.process_started.connect(self.on_process_started) dataImporter.process_finished.connect(self.on_process_finished) self.progress_bar.setValue(25) try: if dataImporter.run() != iliimporter.Importer.SUCCESS: self.enable() self.progress_bar.hide() self.show_message( QCoreApplication.translate( "DialogImportData", "An error occurred when importing the data. For more information see the log..." ), Qgis.Warning) return except JavaNotFoundError: # Set JAVA PATH get_java_path_dlg = DialogGetJavaPath() get_java_path_dlg.setModal(True) if get_java_path_dlg.exec_(): configuration = self.update_configuration() if not get_java_path_from_qgis_model_baker(): message_error_java = QCoreApplication.translate( "DialogImportData", """Java could not be found. You can configure the JAVA_HOME environment variable, restart QGIS and try again.""" ) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(message_error_java) self.show_message(message_error_java, Qgis.Warning) self.enable() self.progress_bar.hide() return self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) self.show_message( QCoreApplication.translate( "DialogImportData", "Import of the data was successfully completed"), Qgis.Success) def save_configuration(self, configuration): settings = QSettings() settings.setValue( 'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_import', configuration.xtffile) settings.setValue('Asistente-LADM_COL/QgisModelBaker/show_log', not self.log_config.isCollapsed()) def restore_configuration(self): settings = QSettings() self.xtf_file_line_edit.setText( settings.value( 'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_import')) # Show log value_show_log = settings.value( 'Asistente-LADM_COL/QgisModelBaker/show_log', False, type=bool) self.log_config.setCollapsed(not value_show_log) # set model repository # if there is no option by default use online model repository self.use_local_models = settings.value( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', type=bool) if self.use_local_models: self.custom_model_directories = settings.value( 'Asistente-LADM_COL/models/custom_models') if settings.value( 'Asistente-LADM_COL/models/custom_models') else None def update_configuration(self): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ item_db = self._conf_db.get_db_items()[self.db.mode] configuration = ImportDataConfiguration() item_db.set_db_configuration_params(self.db.dict_conn_params, configuration) configuration.xtffile = self.xtf_file_line_edit.text().strip() configuration.delete_data = False configuration.epsg = DEFAULT_EPSG configuration.inheritance = DEFAULT_INHERITANCE configuration.create_basket_col = CREATE_BASKET_COL configuration.create_import_tid = CREATE_IMPORT_TID configuration.stroke_arcs = STROKE_ARCS java_path = get_java_path_from_qgis_model_baker() if java_path: self.base_configuration.java_path = java_path # Check custom model directories if self.use_local_models: if self.custom_model_directories is None: self.base_configuration.custom_model_directories_enabled = False else: self.base_configuration.custom_model_directories = self.custom_model_directories self.base_configuration.custom_model_directories_enabled = True configuration.base_configuration = self.base_configuration if self.get_ili_models(): configuration.ilimodels = ';'.join(self.get_ili_models()) configuration.disable_validation = not QSettings().value( 'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting', True, bool) return configuration def print_info(self, text, text_color='#000000', clear=False): self.txtStdout.setTextColor(QColor(text_color)) self.txtStdout.append(text) QCoreApplication.processEvents() def on_stderr(self, text): color_log_text(text, self.txtStdout) self.advance_progress_bar_by_text(text) def on_process_started(self, command): self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(command) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): color = '#004905' if exit_code == 0 else '#aa2222' self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append('Finished ({})'.format(exit_code)) if result == iliimporter.Importer.SUCCESS: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: self.show_message( QCoreApplication.translate("DialogImportData", "Error when importing data"), Qgis.Warning) self.enable() # Open log if self.log_config.isCollapsed(): self.log_config.setCollapsed(False) def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models...': self.progress_bar.setValue(50) QCoreApplication.processEvents() elif text.strip() == 'Info: create table structure...': self.progress_bar.setValue(55) QCoreApplication.processEvents() elif text.strip() == 'Info: first validation pass...': self.progress_bar.setValue(60) QCoreApplication.processEvents() elif text.strip() == 'Info: second validation pass...': self.progress_bar.setValue(80) QCoreApplication.processEvents() def show_help(self): self.qgis_utils.show_help("import_data") def disable(self): self.db_config.setEnabled(False) self.ili_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.db_config.setEnabled(True) self.ili_config.setEnabled(True) self.buttonBox.setEnabled(True) def show_message(self, message, level): self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)
class SimulationResults(uicls, basecls): """Dialog with methods for handling simulations results.""" PROGRESS_COLUMN_IDX = 3 def __init__(self, plugin_dock, parent=None): super().__init__(parent) self.setupUi(self) self.plugin_dock = plugin_dock self.api_client = self.plugin_dock.threedi_api self.download_results_thread = None self.download_worker = None self.finished_simulations = {} self.tv_model = None self.last_progress_item = None self.setup_view_model() self.plugin_dock.simulations_progresses_sentinel.progresses_fetched.connect(self.update_finished_list) self.pb_cancel.clicked.connect(self.close) self.pb_download.clicked.connect(self.download_results) self.tv_finished_sim_tree.selectionModel().selectionChanged.connect(self.toggle_download_results) def setup_view_model(self): """Setting up model and columns for TreeView.""" self.tv_model = QStandardItemModel(0, 4) delegate = DownloadProgressDelegate(self.tv_finished_sim_tree) self.tv_finished_sim_tree.setItemDelegateForColumn(self.PROGRESS_COLUMN_IDX, delegate) self.tv_model.setHorizontalHeaderLabels(["Simulation name", "User", "Expires", "Download progress"]) self.tv_finished_sim_tree.setModel(self.tv_model) def toggle_download_results(self): """Toggle download if any simulation is selected.""" if self.download_results_thread is None: selection_model = self.tv_finished_sim_tree.selectionModel() if selection_model.hasSelection(): self.pb_download.setEnabled(True) else: self.pb_download.setDisabled(True) def add_finished_simulation_to_model(self, simulation, status): """Method for adding simulation to the model.""" sim_id = simulation.id sim_name_item = QStandardItem(f"{simulation.name} ({sim_id})") sim_name_item.setData(sim_id, Qt.UserRole) user_item = QStandardItem(simulation.user) delta = relativedelta(status.created, ThreediCalls.EXPIRATION_TIME) expires_item = QStandardItem(f"{delta.days} day(s)") progress_item = QStandardItem() progress_item.setData(-1, Qt.UserRole) self.tv_model.insertRow(0, [sim_name_item, user_item, expires_item, progress_item]) self.finished_simulations[sim_id] = simulation def update_finished_list(self, progresses): """Update finished simulations list.""" for sim_id, (sim, status, progress) in progresses.items(): status_name = status.name if status_name != "finished": continue if sim_id not in self.finished_simulations: self.add_finished_simulation_to_model(sim, status) def on_download_progress_update(self, percentage): """Update download progress bar.""" self.last_progress_item.setData(percentage, Qt.UserRole) if percentage == 0: row = self.last_progress_item.index().row() name_text = self.tv_model.item(row, 0).text() msg = f"Downloading results of {name_text} started!" self.plugin_dock.communication.bar_info(msg) def on_download_finished_success(self, msg): """Reporting finish successfully status and closing download thread.""" self.plugin_dock.communication.bar_info(msg, log_text_color=Qt.darkGreen) self.download_results_thread.quit() self.download_results_thread.wait() self.download_results_thread = None self.download_worker = None self.toggle_download_results() def on_download_finished_failed(self, msg): """Reporting failure and closing download thread.""" self.plugin_dock.communication.bar_error(msg, log_text_color=Qt.red) self.download_results_thread.quit() self.download_results_thread.wait() self.download_results_thread = None self.download_worker = None self.toggle_download_results() def terminate_download_thread(self): """Forcing termination of download background thread.""" if self.download_results_thread is not None and self.download_results_thread.isRunning(): self.plugin_dock.communication.bar_info("Terminating download thread.") self.download_results_thread.terminate() self.plugin_dock.communication.bar_info("Waiting for download thread termination.") self.download_results_thread.wait() self.plugin_dock.communication.bar_info("Download worker terminated.") self.download_results_thread = None self.download_worker = None def pick_results_destination_dir(self): """Pick folder where results will be written to.""" working_dir = self.plugin_dock.plugin_settings.working_dir last_folder = QSettings().value("threedi/last_results_folder", working_dir, type=str) directory = QFileDialog.getExistingDirectory(self, "Select Results Directory", last_folder) if len(directory) == 0: return None QSettings().setValue("threedi/last_results_folder", directory) return directory def download_results(self): """Download simulation results files.""" current_index = self.tv_finished_sim_tree.currentIndex() if not current_index.isValid(): return working_dir = self.plugin_dock.plugin_settings.working_dir local_schematisations = list_local_schematisations(working_dir) try: current_row = current_index.row() name_item = self.tv_model.item(current_row, 0) sim_id = name_item.data(Qt.UserRole) simulation = self.finished_simulations[sim_id] simulation_name = simulation.name.replace(" ", "_") simulation_model_id = int(simulation.threedimodel_id) tc = ThreediCalls(self.plugin_dock.threedi_api) try: model_3di = tc.fetch_3di_model(simulation_model_id) gridadmin_downloads = tc.fetch_3di_model_gridadmin_download(simulation_model_id) if model_3di.schematisation_id: model_schematisation_id = model_3di.schematisation_id model_schematisation_name = model_3di.schematisation_name model_revision_number = model_3di.revision_number try: local_schematisation = local_schematisations[model_schematisation_id] except KeyError: local_schematisation = LocalSchematisation( working_dir, model_schematisation_id, model_schematisation_name, create=True ) try: local_revision = local_schematisation.revisions[model_revision_number] except KeyError: local_revision = LocalRevision(local_schematisation, model_revision_number) local_revision.make_revision_structure() results_dir = local_revision.results_dir else: warn_msg = ( "The 3Di model to which these results belong was uploaded with Tortoise and does not " "belong to any schematisation. Therefore, it cannot be determined to which " "schematisation the results should be downloaded.\n\nPlease select a directory to save " "the result files to." ) self.plugin_dock.communication.show_warn(warn_msg) results_dir = self.pick_results_destination_dir() if not results_dir: self.plugin_dock.communication.show_warn(warn_msg) return except ApiException as e: if e.status == 404: warn_msg = ( "The 3Di model to which these results belong is owned by an organisation for which " "you do not have sufficient rights. Therefore, you cannot download the computational " "grid (gridadmin.h5) and it cannot be determined to which schematisation the results " "should be downloaded.\n\nContact the servicedesk to obtain access rights to the " "organisation that owns the 3Di model.\n\nPlease select a directory to save the result" " files to." ) self.plugin_dock.communication.show_warn(warn_msg) results_dir = self.pick_results_destination_dir() if not results_dir: return gridadmin_downloads = None else: raise e simulation_subdirectory = os.path.join(results_dir, f"sim_{sim_id}_{simulation_name}") downloads = tc.fetch_simulation_downloads(sim_id) if gridadmin_downloads is not None: downloads.append(gridadmin_downloads) downloads.sort(key=lambda x: x[-1].size) self.last_progress_item = self.tv_model.item(current_row, self.PROGRESS_COLUMN_IDX) except ApiException as e: error_msg = extract_error_message(e) self.plugin_dock.communication.show_error(error_msg) return except Exception as e: error_msg = f"Error: {e}" self.plugin_dock.communication.show_error(error_msg) return self.pb_download.setDisabled(True) self.download_results_thread = QThread() self.download_worker = DownloadProgressWorker(simulation, downloads, simulation_subdirectory) self.download_worker.moveToThread(self.download_results_thread) self.download_worker.thread_finished.connect(self.on_download_finished_success) self.download_worker.download_failed.connect(self.on_download_finished_failed) self.download_worker.download_progress.connect(self.on_download_progress_update) self.download_results_thread.started.connect(self.download_worker.run) self.download_results_thread.start()
class ThinGreyscaleDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent=None): """Constructor.""" self.iface = iface self.plugin_dir = dirname(__file__) self.THINGREYSCALE = self.tr('ThinGreyscale') self.BROWSE = self.tr('Browse') self.CANCEL = self.tr('Cancel') self.CLOSE = self.tr('Close') self.HELP = self.tr('Help') self.OK = self.tr('OK') self.DEFAULTPROVIDER = 'GTiff' self.DEFAULTEXTENSION = '.tif' self.EXTRAEXTENSION = ' *.tiff' super(ThinGreyscaleDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.showInfo("Connecting UI components") okButton = self.button_box.button(QDialogButtonBox.Ok) okButton.setText(self.OK) cancelButton = self.button_box.button(QDialogButtonBox.Cancel) cancelButton.setText(self.CANCEL) cancelButton.setEnabled(False) closeButton = self.button_box.button(QDialogButtonBox.Close) closeButton.setText(self.CLOSE) browseButton = self.browseButton browseButton.setText(self.BROWSE) self.calcHistPushButton.setEnabled(False) self.listModel = QStandardItemModel(self.levelsListView) self.levelsListView.setModel(self.listModel) self.levelsListView.sizeHintForColumn(20) #self.levelValuesCheckBox.setEnabled(False) # Help button helpButton = self.helpButton helpButton.setText(self.HELP) # Connect signals self.showInfo("Connecting signals") okButton.clicked.connect(self.startWorker) cancelButton.clicked.connect(self.killWorker) closeButton.clicked.connect(self.reject) helpButton.clicked.connect(self.help) browseButton.clicked.connect(self.browse) inpIndexCh = self.inputRaster.currentIndexChanged['QString'] inpIndexCh.connect(self.layerchanged) bandCh = self.bandComboBox.currentIndexChanged['QString'] bandCh.connect(self.bandChanged) #self.iface.legendInterface().itemAdded.connect( # self.layerlistchanged) #self.iface.legendInterface().itemRemoved.connect( # self.layerlistchanged) #QObject.disconnect(self.button_box, SIGNAL("rejected()"), self.reject) self.button_box.rejected.disconnect(self.reject) calchistPr = self.calcHistPushButton.clicked calchistPr.connect(self.calculateHistogram) sugglevPr = self.suggestlevelsPushButton.clicked sugglevPr.connect(self.suggestLevels) addlevPr = self.addlevelPushButton.clicked addlevPr.connect(self.addLevel) dellevPr = self.deletelevelsPushButton.clicked dellevPr.connect(self.removeLevel) maxvalCh = self.maxValueSpinBox.valueChanged maxvalCh.connect(self.minmaxvalueChanged) maxvalFi = self.maxValueSpinBox.editingFinished maxvalFi.connect(self.minmaxvalueEdFinished) minvalCh = self.minValueSpinBox.valueChanged minvalCh.connect(self.minmaxvalueChanged) minvalFi = self.minValueSpinBox.editingFinished minvalFi.connect(self.minmaxvalueEdFinished) # Set instance variables #self.mem_layer = None self.worker = None self.inputlayerid = None self.inputlayer = None self.layerlistchanging = False self.minvalue = 1 self.inputrasterprovider = None self.histobins = 50 self.setupScene = QGraphicsScene(self) self.histoGraphicsView.setScene(self.setupScene) # Is the layer band of an integer type self.intband = False self.histogramAvailable = False self.histo = None self.histopadding = 1 def startWorker(self): """Initialises and starts the worker thread.""" try: layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) inputlayer = QgsProject.instance().mapLayer(layerId) #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) if inputlayer is None: self.showError(self.tr('No input layer defined')) return # create a reference to the layer that is being processed # (for use when creating the resulting raster layer) self.thinninglayer = inputlayer self.levels = [] #self.levelsListView.selectAll() #selected = self.levelsListView.selectedIndexes() if self.levelsListView.model().rowCount() == 0: self.showInfo("Levels must be specified!") return for i in range(self.levelsListView.model().rowCount()): levelstring = self.levelsListView.model().item(i).text() #for i in selected: # levelstring = self.levelsListView.model().itemData(i)[0] if self.intband: self.levels.append(int(levelstring)) else: self.levels.append(float(levelstring)) #self.levelsListView.clearSelection() # create a new worker instance worker = Worker(inputlayer, self.levels, self.intband) # configure the QgsMessageBar msgBar = self.iface.messageBar().createMessage( self.tr('Skeletonising'), '') self.aprogressBar = QProgressBar() self.aprogressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) acancelButton = QPushButton() acancelButton.setText(self.CANCEL) acancelButton.clicked.connect(self.killWorker) msgBar.layout().addWidget(self.aprogressBar) msgBar.layout().addWidget(acancelButton) # Has to be popped after the thread has finished (in # workerFinished). self.iface.messageBar().pushWidget(msgBar, Qgis.Info) self.messageBar = msgBar # start the worker in a new thread thread = QThread(self) worker.moveToThread(thread) worker.finished.connect(self.workerFinished) worker.error.connect(self.workerError) worker.status.connect(self.workerInfo) worker.progress.connect(self.progressBar.setValue) worker.progress.connect(self.aprogressBar.setValue) worker.iterprogress.connect(self.iterProgressBar.setValue) thread.started.connect(worker.run) thread.start() self.thread = thread self.worker = worker self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.button_box.button(QDialogButtonBox.Close).setEnabled(False) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(True) except: import traceback self.showError(traceback.format_exc()) else: pass def workerFinished(self, ok, ret): """Handles the output from the worker and cleans up after the worker has finished.""" # clean up the worker and thread self.showInfo("Handling the result") self.worker.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() # remove widget from message bar (pop) self.iface.messageBar().popWidget(self.messageBar) if ok and ret is not None: #self.showInfo("Ret: "+str(ret[10,])) # Transformation: self.minx = self.thinninglayer.extent().xMinimum() self.maxx = self.thinninglayer.extent().xMaximum() self.miny = self.thinninglayer.extent().yMinimum() self.maxy = self.thinninglayer.extent().yMaximum() self.rows = self.thinninglayer.height() self.cols = self.thinninglayer.width() self.xres = (self.maxx - self.minx) / float(self.cols) self.yres = (self.maxy - self.miny) / float(self.rows) geotransform = (self.minx, self.xres, 0, self.maxy, 0, -self.yres) try: format = self.DEFAULTPROVIDER driver = gdal.GetDriverByName(format) NOVALUE = 0 metadata = driver.GetMetadata() fileName = self.outputRaster.text() if self.outputRaster.text() == "": self.showInfo("No output file specified, " + "creating a temporary file") # Get a temporary file fileName = mktemp(prefix='greyskel', suffix=self.DEFAULTEXTENSION) fileInfo = QFileInfo(fileName) filepath = fileInfo.absolutePath() baseName = fileInfo.baseName() suffix = fileInfo.suffix() thisfilename = filepath + baseName + '.' + suffix thisfilename = fileName self.showInfo("File name: " + thisfilename) gdaldatatype = gdal.GDT_Byte skelmatrix = None if self.levelValuesCheckBox.isChecked(): # Transform the pixel values back to the original # level values my_dict = {} # Add zero to handle the "empty" pixels my_dict[0] = 0 for i in range(len(self.levels)): my_dict[i + 1] = self.levels[i] skelmatrix = np.vectorize(my_dict.__getitem__, otypes=[np.float])(ret) gdaldatatype = gdal.GDT_Int32 if not self.intband: gdaldatatype = gdal.GDT_Float32 else: skelmatrix = ret outDataset = driver.Create(thisfilename, self.cols, self.rows, 1, gdaldatatype) if self.thinninglayer.dataProvider().crs() is not None: srs = self.thinninglayer.dataProvider().crs() outDataset.SetProjection(srs.toWkt().encode('ascii', 'ignore')) skeletonband = outDataset.GetRasterBand(1) skeletonband.WriteArray(skelmatrix) skeletonband.SetNoDataValue(NOVALUE) #stats = skeletonband.GetStatistics(False, True) #skeletonband.SetStatistics(stats[0], stats[1], # stats[2], stats[3]) outDataset.SetGeoTransform(geotransform) outDataset = None # To close the file # report the result rlayer = QgsRasterLayer(thisfilename, baseName) self.layerlistchanging = True #QgsMapLayerRegistry.instance().addMapLayer(rlayer) QgsProject.instance().addMapLayer(rlayer) self.layerlistchanging = False except: import traceback self.showError("Can't write the skeleton file: %s" % self.outputRaster.text() + ' - ' + traceback.format_exc()) okb = self.button_box.button(QDialogButtonBox.Ok) okb.setEnabled(True) closb = self.button_box.button(QDialogButtonBox.Close) closb.setEnabled(True) cancb = self.button_box.button(QDialogButtonBox.Cancel) cancb.setEnabled(False) return QgsMessageLog.logMessage(self.tr('ThinGreyscale finished'), self.THINGREYSCALE, Qgis.Info) else: # notify the user that something went wrong if not ok: self.showError(self.tr('Aborted') + '!') else: self.showError(self.tr('No skeleton created') + '!') self.progressBar.setValue(0.0) #self.aprogressBar.setValue(0.0) self.iterProgressBar.setValue(0.0) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.button_box.button(QDialogButtonBox.Close).setEnabled(True) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(False) def workerError(self, exception_string): """Report an error from the worker.""" #QgsMessageLog.logMessage(self.tr('Worker failed - exception') + # ': ' + str(exception_string), # self.THINGREYSCALE, # QgsMessageLog.CRITICAL) self.showError(exception_string) def workerInfo(self, message_string): """Report an info message from the worker.""" QgsMessageLog.logMessage(self.tr('Worker') + ': ' + message_string, self.THINGREYSCALE, Qgis.Info) def layerchanged(self, number=0): """Do the necessary updates after a layer selection has been changed.""" self.showInfo("Layer changed") # If the layer list is being updated, don't do anything if self.layerlistchanging: return layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) self.inputlayerid = layerId #self.inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) self.inputlayer = QgsProject.instance().mapLayer(layerId) if self.inputlayer is not None: self.inputrasterprovider = self.inputlayer.dataProvider() self.bandComboBox.clear() bandcount = self.inputlayer.bandCount() #self.showInfo("Layer bandcount: "+str(bandcount)) for i in range(bandcount): self.bandComboBox.addItem(self.inputlayer.bandName(i + 1), i) #self.showInfo("Band " + str(i) + ": " + # self.inputlayer.bandName(i+1)) # Check if the driver supports Create() or CreateCopy() #gdalmetadata = self.inputlayer.metadata() #self.showInfo("Layer metadata: " + # str(gdalmetadata.encode('utf-8'))) #provstring = '<p>GDAL provider</p>\n' #providerpos = gdalmetadata.find(provstring) #brpos = gdalmetadata.find('<br>', providerpos + len(provstring)) #self.gdalprovider = gdalmetadata[int(providerpos + # len(provstring)):int(brpos)] #self.showInfo('GDAL provider: '+self.gdalprovider) #drivername = self.gdalprovider.encode('ascii', 'ignore') #theDriver = gdal.GetDriverByName(drivername) #if theDriver is None: # self.showInfo("Unable to get the raster driver") #else: #data theMetadata = theDriver.GetMetadata() #self.showInfo("Driver metadata: "+str(theMetadata)) #if ((gdal.DCAP_CREATE in theMetadata) and # theMetadata[gdal.DCAP_CREATE] == 'YES'): # self.canCreate = True #if (theMetadata.has_key(gdal.DCAP_CREATECOPY) and #if ((gdal.DCAP_CREATECOPY in theMetadata) and # theMetadata[gdal.DCAP_CREATECOPY] == 'YES'): # self.canCreateCopy = True #self.showInfo('raster provider type: ' + # str(self.inputlayer.providerType())) # Determine the file suffix #self.gdalext = "" #if gdal.DMD_EXTENSION in theMetadata: # self.gdalext = "." + theMetadata[gdal.DMD_EXTENSION] #else: # self.showInfo("No extension available in GDAL metadata") # by parsing the layer metadata looking for # "Dataset Description" #descstring = 'Dataset Description</p>\n<p>' #descpos = gdalmetadata.find(descstring) #ppos = gdalmetadata.find('</p>',descpos+len(descstring)) #filename = gdalmetadata[descpos+len(descstring):ppos] #self.gdalext = splitext(filename)[1] #self.showInfo('GDAL extension: '+self.gdalext) # Determine the datatype #datatypestring = 'Data Type</p>\n<p>' #datatypepos = gdalmetadata.find(datatypestring) #ppos = gdalmetadata.find('</p>', # datatypepos + len(datatypestring)) #datatypedesc = gdalmetadata[datatypepos + # len(datatypestring):ppos] #shortdesc = datatypedesc.split()[0] #self.showInfo('GDAL data type: GDT_'+shortdesc) # Call the findGdalDatatype function #self.findGdalDatatype(shortdesc) # self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.calcHistPushButton.setEnabled(True) self.suggestlevelsPushButton.setEnabled(True) def bandChanged(self): band = self.bandComboBox.currentIndex() + 1 self.showInfo("Band changed: " + str(band)) statistics = self.inputrasterprovider.bandStatistics(band) #self.showInfo("Band statistics: " + str(statistics.minimumValue) + # " - " + str(statistics.maximumValue) + # " - " + str(statistics.mean)) self.bandmin = statistics.minimumValue self.bandmax = statistics.maximumValue dt = self.inputrasterprovider.dataType(band) # Integer data type if (dt == Qgis.Byte or dt == Qgis.UInt16 or dt == Qgis.Int16 or dt == Qgis.UInt32 or dt == Qgis.Int32): self.intband = True self.minValueSpinBox.setDecimals(0) self.maxValueSpinBox.setDecimals(0) self.levelSpinBox.setDecimals(0) self.bandMinLabel.setText(str(int(statistics.minimumValue))) self.bandMaxLabel.setText(str(int(statistics.maximumValue))) else: self.intband = False self.minValueSpinBox.setDecimals(5) self.maxValueSpinBox.setDecimals(5) self.levelSpinBox.setDecimals(5) minlabtext = "{0:.5f}".format(statistics.minimumValue) self.bandMinLabel.setText(minlabtext) maxlabtext = "{0:.5f}".format(statistics.maximumValue) self.bandMaxLabel.setText(maxlabtext) #self.minValueSpinBox.setMinimum(statistics.minimumValue) self.maxValueSpinBox.setMinimum(statistics.minimumValue) #self.minValueSpinBox.setMaximum(statistics.maximumValue) self.maxValueSpinBox.setMaximum(statistics.maximumValue) #self.minValueSpinBox.setValue(statistics.minimumValue) if not (statistics.statsGathered & statistics.Mean): bandmean = (statistics.minimumValue + statistics.maximumValue) / 2 else: #self.showInfo("statsgathered: " + str(statistics.statsGathered)) bandmean = statistics.mean if self.intband: self.minValueSpinBox.setValue(int(ceil(bandmean))) else: self.minValueSpinBox.setValue(bandmean) self.maxValueSpinBox.setValue(statistics.maximumValue) self.histMinValue.setText(str(statistics.minimumValue)) self.histMaxValue.setText(str(statistics.maximumValue)) self.levelSpinBox.setMinimum(statistics.minimumValue) self.levelSpinBox.setMaximum(statistics.maximumValue) self.histogramAvailable = False #if self.inputrasterprovider.hasStatistics(band): #if statistics.statsGathered: #histogram = statistics.histogramVector #self.showInfo("Histogram: " + str(histogram)) #range = min to max #np.histogram(band, 50, range) def minmaxvalueChanged(self): #if self.minValueSpinBox is None: # return minvalue = self.minValueSpinBox.value() #if minvalue is None: # return #if self.maxValueSpinBox is None: # return maxvalue = self.maxValueSpinBox.value() #if maxvalue is None: # return if isnan(maxvalue) or isnan(minvalue): return self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " + str(maxvalue)) #if self.intband: # minvalue = int(minvalue) # maxvalue = int(maxvalue) if abs(maxvalue - minvalue) < 0.00001: #if maxvalue == maxvalue: self.calcHistPushButton.setEnabled(False) else: self.calcHistPushButton.setEnabled(True) # Update the min and max value spinboxes self.minValueSpinBox.setMaximum(maxvalue) self.maxValueSpinBox.setMinimum(minvalue) self.minValueSpinBox.setMinimum(self.bandmin) def minmaxvalueEdFinished(self): minvalue = self.minValueSpinBox.value() maxvalue = self.maxValueSpinBox.value() if self.intband: minvalue = int(minvalue) maxvalue = int(maxvalue) self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " + str(maxvalue)) # Update the spin box for adding levels self.levelSpinBox.setMinimum(minvalue) self.levelSpinBox.setMaximum(maxvalue) if self.levelSpinBox.value() < minvalue: self.levelSpinBox.setValue(minvalue) if self.levelSpinBox.value() > maxvalue: self.levelSpinBox.setValue(maxvalue) # Update the min and max value spinboxes self.minValueSpinBox.setMaximum(maxvalue) self.maxValueSpinBox.setMinimum(minvalue) # Adjust the levels: i = 0 while self.levelsListView.model().item(i): #for i in range(self.levelsListView.model().rowCount()): #self.showInfo("Element: " + # str(self.levelsListView.model().item(i).text())) #continue value = float(self.levelsListView.model().item(i).text()) if value < minvalue: if i == 0: self.levelsListView.model().item(i).setText(str(minvalue)) i = i + 1 else: self.levelsListView.model().removeRow(i) elif value > maxvalue: if i == self.levelsListView.model().rowCount() - 1: self.levelsListView.model().item(i).setText(str(maxvalue)) i = i + 1 else: self.levelsListView.model().removeRow(i) else: i = i + 1 self.drawHistogram() def calculateHistogram(self): self.showInfo("Calculating histogram...") if self.inputlayer is None: return self.showInfo("Calculating histogram...") # Check if there is only one value myrange = (self.minValueSpinBox.value(), self.maxValueSpinBox.value()) self.inputextent = self.inputlayer.extent() self.inputrdp = self.inputlayer.dataProvider() width = self.inputlayer.width() height = self.inputlayer.height() if width == 0 or height == 0: self.showInfo("Image has zero width or height") return extwidth = self.inputextent.width() extheight = self.inputextent.height() # Read the raster block and get the maximum value rasterblock = self.inputrdp.block(1, self.inputextent, width, height) # Create a numpy array version of the image imageMat = np.zeros((height, width), dtype=np.float16) # This one takes a lot of time! for row in range(height): for column in range(width): imageMat[row, column] = rasterblock.value(row, column) self.showInfo("Image: " + str(height) + ", " + str(width) + " - " + str(imageMat[row, column])) self.histo = np.histogram(imageMat, self.histobins, myrange) #relevantpixels = imageMat[np.where(imageMat >= bandval)] minlevel = float(self.bandMinLabel.text()) relevantpixels = imageMat[np.where(imageMat >= minlevel)] #self.showInfo("Histogram: " + str(self.histo)) nanpercentage = 100.0 - 100.0 * len(relevantpixels) / (width * height) self.bandNANLabel.setText("{0:.1f}".format(nanpercentage)) #self.showInfo("Percentage NAN: " + str(100.0 - 100.0 * # len(relevantpixels) / (width * height))) #self.showInfo("First element: " + str(self.histo[0])) #self.showInfo("First element, first: " + str(self.histo[0][0])) #self.showInfo("First element, second: " + str(self.histo[0][1])) self.histMinValue.setText(str(self.minValueSpinBox.value())) self.histMaxValue.setText(str(self.maxValueSpinBox.value())) if self.intband: self.histMinValue.setText(str(int(self.minValueSpinBox.value()))) self.histMaxValue.setText(str(int(self.maxValueSpinBox.value()))) self.histogramAvailable = True self.drawHistogram() def drawHistogram(self): #if self.inputlayer is None: # return self.showInfo("Drawing histogram...") viewprect = QRectF(self.histoGraphicsView.viewport().rect()) self.histoGraphicsView.setSceneRect(viewprect) self.setupScene.clear() self.setupScene.update() histbottom = self.histoGraphicsView.sceneRect().bottom() histtop = self.histoGraphicsView.sceneRect().top() left = self.histoGraphicsView.sceneRect().left() + self.histopadding right = self.histoGraphicsView.sceneRect().right() - self.histopadding histheight = histbottom - histtop histwidth = right - left step = 1.0 * histwidth / self.histobins maxlength = histheight padding = 1 ll = QPoint(self.histopadding - 1, histheight - padding) start = QPointF(self.histoGraphicsView.mapToScene(ll)) # Check if there is only one value #myrange = (self.minValueSpinBox.value(),self.maxValueSpinBox.value()) if self.histogramAvailable: maxvalue = 0.0 for i in range(len(self.histo[0])): if self.histo[0][i] > maxvalue: maxvalue = self.histo[0][i] if maxvalue == 0: return self.maxBinNumber.setText(str(maxvalue)) # Create the histogram: #self.showInfo("maxvalue: " + str(maxvalue)) #self.showInfo("maxlength: " + str(maxlength)) #self.showInfo("step: " + str(step)) for i in range(self.histobins): binnumber = self.histo[0][i] if binnumber == 0: continue height = (1.0 * self.histo[0][i] / maxvalue * (maxlength - padding)) rectangle = QGraphicsRectItem(start.x() + step * i, start.y(), step, -height) rectangle.setPen(QPen(QColor(102, 102, 102))) rectangle.setBrush(QBrush(QColor(240, 240, 240))) self.setupScene.addItem(rectangle) #self.showInfo(str(i) + ": " + str(height)) #if self.levelsListView.model().rowCount() > 0: # Add lines for the levels minvalue = float(self.histMinValue.text()) maxvalue = float(self.histMaxValue.text()) datarange = maxvalue - minvalue if datarange == 0: return i = 0 while self.levelsListView.model().item(i): #self.showInfo("Element: " + # str(self.levelsListView.model().item(i).text())) #continue value = float(self.levelsListView.model().item(i).text()) xvalue = start.x() + histwidth * (value - minvalue) / datarange line = QGraphicsLineItem(xvalue, 0, xvalue, histheight) if i == 0 or i == (self.levelsListView.model().rowCount() - 1): line.setPen(QPen(QColor(204, 0, 0))) else: line.setPen(QPen(QColor(0, 204, 0))) self.setupScene.addItem(line) i = i + 1 def suggestLevels(self): self.listModel.clear() self.showInfo("Suggesting levels") levels = self.levelsSpinBox.value() startvalue = self.minValueSpinBox.value() endvalue = self.maxValueSpinBox.value() increment = (endvalue - startvalue) / levels for i in range(levels + 1): value = startvalue + increment * i if self.intband: value = int(value) item = QStandardItem(str(value)) self.listModel.appendRow(item) self.drawHistogram() def addLevel(self): newvalue = self.levelSpinBox.value() if self.intband: newvalue = int(newvalue) for i in range(self.listModel.rowCount()): # Check if the value is already in the list if self.listModel.item(i).text() == str(newvalue): return else: # Maintain a sorted list of distances if (float(self.listModel.item(i).text()) > float(str(newvalue))): item = QStandardItem(str(newvalue)) self.listModel.insertRow(i, item) self.drawHistogram() return item = QStandardItem(str(newvalue)) self.listModel.appendRow(item) #if self.histogramAvailable: # addLevelsToHistogram() self.drawHistogram() def removeLevel(self): self.levelsListView.setUpdatesEnabled(False) indexes = self.levelsListView.selectedIndexes() indexes.sort() for i in range(len(indexes) - 1, -1, -1): self.listModel.removeRow(indexes[i].row()) self.levelsListView.setUpdatesEnabled(True) #if self.histogramAvailable: # removeLevelFromHistogram() self.drawHistogram() def layerlistchanged(self): self.layerlistchanging = True self.showInfo("Layer list changed") # Repopulate the input layer combo box # Save the currently selected input layer inputlayerid = self.inputlayerid self.inputRaster.clear() for alayer in self.iface.legendInterface().layers(): if alayer.type() == QgsMapLayer.RasterLayer: gdalmetadata = alayer.metadata() # Skip WMS layers WMSstring = 'Web Map Service' wmspos = gdalmetadata.find(WMSstring) if wmspos != -1: continue self.inputRaster.addItem(alayer.name(), alayer.id()) # Set the previous selection for i in range(self.inputRaster.count()): if self.inputRaster.itemData(i) == inputlayerid: self.inputRaster.setCurrentIndex(i) self.layerlistchanging = False #self.updateui() def updateui(self): """Do the necessary updates after a layer selection has been changed.""" #if self.layerlistchanged: # return #self.outputRaster.setText(self.inputRaster.currentText() + # '_' + 'thinned') layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) inputlayer = QgsProject.instance().mapLayer(layerId) if inputlayer is not None: pass else: pass def findGdalDatatype(self, shortdesc): gdaldatatype = None # // Unknown or unspecified type # GDT_Unknown = GDALDataType(C.GDT_Unknown) if shortdesc == 'Unknown': gdaldatatype = gdal.GDT_Unknown # // Eight bit unsigned integer # GDT_Byte = GDALDataType(C.GDT_Byte) elif shortdesc == 'Byte': gdaldatatype = gdal.GDT_Byte # // Sixteen bit unsigned integer # GDT_UInt16 = GDALDataType(C.GDT_UInt16) elif shortdesc == 'UInt16': gdaldatatype = gdal.GDT_UInt16 # // Sixteen bit signed integer # GDT_Int16 = GDALDataType(C.GDT_Int16) elif shortdesc == 'Int16': gdaldatatype = gdal.GDT_Int16 # // Thirty two bit unsigned integer # GDT_UInt32 = GDALDataType(C.GDT_UInt32) elif shortdesc == 'UInt32': gdaldatatype = gdal.GDT_UInt32 # // Thirty two bit signed integer # GDT_Int32 = GDALDataType(C.GDT_Int32) elif shortdesc == 'Int32': gdaldatatype = gdal.GDT_Int32 # // Thirty two bit floating point # GDT_Float32 = GDALDataType(C.GDT_Float32) elif shortdesc == 'Float32': gdaldatatype = gdal.GDT_Float32 # // Sixty four bit floating point # GDT_Float64 = GDALDataType(C.GDT_Float64) elif shortdesc == 'Float64': gdaldatatype = gdal.GDT_Float64 # // Complex Int16 # GDT_CInt16 = GDALDataType(C.GDT_CInt16) elif shortdesc == 'CInt16': gdaldatatype = gdal.CInt16 # // Complex Int32 # GDT_CInt32 = GDALDataType(C.GDT_CInt32) elif shortdesc == 'CInt32': gdaldatatype = gdal.CInt32 # // Complex Float32 # GDT_CFloat32 = GDALDataType(C.GDT_CFloat32) elif shortdesc == 'CFloat32': gdaldatatype = gdal.CFloat32 # // Complex Float64 # GDT_CFloat64 = GDALDataType(C.GDT_CFloat64) elif shortdesc == 'CFloat64': gdaldatatype = gdal.CFloat64 # // maximum type # + 1 # GDT_TypeCount = GDALDataType(C.GDT_TypeCount) elif shortdesc == 'TypeCount': gdaldatatype = gdal.TypeCount self.gdaldatatype = gdaldatatype def killWorker(self): """Kill the worker thread.""" if self.worker is not None: QgsMessageLog.logMessage(self.tr('Killing worker'), self.THINGREYSCALE, Qgis.Info) self.worker.kill() def showError(self, text): """Show an error.""" self.iface.messageBar().pushMessage(self.tr('Error'), text, level=QgsMessageBar.CRITICAL, duration=3) QgsMessageLog.logMessage('Error: ' + text, self.THINGREYSCALE, QgsMessageLog.CRITICAL) def showWarning(self, text): """Show a warning.""" self.iface.messageBar().pushMessage(self.tr('Warning'), text, level=QgsMessageBar.WARNING, duration=2) QgsMessageLog.logMessage('Warning: ' + text, self.THINGREYSCALE, QgsMessageLog.WARNING) def showInfo(self, text): """Show info.""" self.iface.messageBar().pushMessage(self.tr('Info'), text, level=Qgis.Info, duration=2) QgsMessageLog.logMessage('Info: ' + text, self.THINGREYSCALE, Qgis.Info) # def help(self): # #QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir + # "/help/build/html/index.html")) # QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir + # "/help/index.html")) # #showPluginHelp() def tr(self, message): """Get the translation for a string using Qt translation API. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ return QCoreApplication.translate('ThinGreyScaleDialog', message) def browse(self): settings = QSettings() key = '/UI/lastShapefileDir' outDir = settings.value(key) home = outDir #outDir = expanduser("~") #filter = (self.DEFAULTPROVIDER + " (*" + # self.DEFAULTEXTENSION + ");;All files (*)") filter = (self.DEFAULTPROVIDER + " (*" + self.DEFAULTEXTENSION + self.EXTRAEXTENSION + ")") #if (self.gdalprovider != self.DEFAULTPROVIDER and # (self.canCreateCopy or # self.canCreate)): # filter = (self.gdalprovider + " (*" + self.gdalext + # ");;" + filter) outFilePath = QFileDialog.getSaveFileName(self, 'Specify file name for skeleton', outDir, filter) outFilePath = unicode(outFilePath) if outFilePath: root, ext = splitext(outFilePath) if ext.lower() != '.tif' and ext.lower() != '.tiff': outFilePath = '%s.tif' % outFilePath outDir = dirname(outFilePath) settings.setValue(key, outDir) # (self.canCreateCopy or self.canCreate): # fileName = splitext(str(fileName))[0]+self.gdalext self.outputRaster.setText(outFilePath) # Overriding def resizeEvent(self, event): #self.showInfo("resizeEvent") self.calculateHistogram() def help(self): #QDesktopServices.openUrl(QUrl.fromLocalFile( # self.plugin_dir + "/help/html/index.html")) showPluginHelp(None, "help/html/index") # Implement the accept method to avoid exiting the dialog when # starting the work def accept(self): """Accept override.""" pass # Implement the reject method to have the possibility to avoid # exiting the dialog when cancelling def reject(self): """Reject override.""" # exit the dialog QDialog.reject(self)
def __init__(self, iface, layer, parent=None): QWidget.__init__(self, parent) self.iface = iface self.layer = layer uri = QgsDataSourceUri(layer.source()) dbplugin = None db = None if layer.dataProvider().name() == 'postgres': dbplugin = createDbPlugin('postgis', 'postgres') elif layer.dataProvider().name() == 'spatialite': dbplugin = createDbPlugin('spatialite', 'spatialite') elif layer.dataProvider().name() == 'oracle': dbplugin = createDbPlugin('oracle', 'oracle') elif layer.dataProvider().name() == 'virtual': dbplugin = createDbPlugin('vlayers', 'virtual') elif layer.dataProvider().name() == 'ogr': dbplugin = createDbPlugin('gpkg', 'gpkg') if dbplugin: dbplugin.connectToUri(uri) db = dbplugin.db self.dbplugin = dbplugin 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 = self.tr('QueryLayer') if self.allowMultiColumnPk: self.uniqueColumnCheck.setText( self.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText( self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) 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.editSql.textChanged.connect(self.updatePresetButtonsState) self.presetName.textChanged.connect(self.updatePresetButtonsState) self.presetCombo.currentIndexChanged.connect( self.updatePresetButtonsState) 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 self.layerTypeWidget.hide() # show if load as raster is supported # self.loadLayerBtn.clicked.connect(self.loadSqlLayer) self.updateLayerBtn.clicked.connect(self.updateSqlLayer) self.getColumnsBtn.clicked.connect(self.fillColumnCombos) self.queryBuilderFirst = True self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif")) self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder) self.presetName.textChanged.connect(self.nameChanged) # Update from layer # First the SQL from QgsDataSourceUri table sql = uri.table().replace('\n', ' ').strip() if uri.keyColumn() == '_uid_': match = re.search( r'^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$', sql, re.S | re.X | re.IGNORECASE) if match: sql = match.group(1) else: match = re.search(r'^\((SELECT .+ FROM .+)\)$', sql, re.S | re.X | re.IGNORECASE) if match: sql = match.group(1) # Need to check on table() since the parentheses were removed by the regexp if not uri.table().startswith('(') and not uri.table().endswith(')'): schema = uri.schema() if schema and schema.upper() != 'PUBLIC': sql = 'SELECT * FROM {0}.{1}'.format( self.db.connector.quoteId(schema), self.db.connector.quoteId(sql)) else: sql = 'SELECT * FROM {0}'.format( self.db.connector.quoteId(sql)) self.editSql.setText(sql) self.executeSql() # Then the columns self.geomCombo.setCurrentIndex( self.geomCombo.findText(uri.geometryColumn(), Qt.MatchExactly)) if uri.keyColumn() != '_uid_': self.uniqueColumnCheck.setCheckState(Qt.Checked) if self.allowMultiColumnPk: itemsData = uri.keyColumn().split(',') for item in self.uniqueModel.findItems("*", Qt.MatchWildcard): if item.data() in itemsData: item.setCheckState(Qt.Checked) else: keyColumn = uri.keyColumn() if self.uniqueModel.findItems(keyColumn): self.uniqueCombo.setEditText(keyColumn) # Finally layer name, filter and selectAtId self.layerNameEdit.setText(layer.name()) self.filter = uri.sql() if uri.selectAtIdDisabled(): self.avoidSelectById.setCheckState(Qt.Checked)
def __init__(self, parent=None, iface=None): """Constructor. :param parent: Optional widget to use as parent :type parent: QWidget :param iface: An instance of QGisInterface :type iface: QGisInterface """ super(ResourceSharingDialog, self).__init__(parent) self.setupUi(self) self.iface = iface # Reconfigure UI self.setModal(True) self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) self.button_install.setEnabled(False) self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Set up the "main menu" - QListWidgetItem # All collections icon_all = QIcon() icon_all.addFile(str(resources_path('img', 'plugin.svg')), QSize(), QIcon.Normal, QIcon.Off) item_all = QListWidgetItem() item_all.setIcon(icon_all) item_all.setText(self.tr('All collections')) # Installed collections icon_installed = QIcon() icon_installed.addFile( str(resources_path('img', 'plugin-installed.svg')), QSize(), QIcon.Normal, QIcon.Off) item_installed = QListWidgetItem() item_installed.setIcon(icon_installed) item_installed.setText(self.tr('Installed collections')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Settings / repositories icon_settings = QIcon() icon_settings.addFile(str(resources_path('img', 'settings.svg')), QSize(), QIcon.Normal, QIcon.Off) item_settings = QListWidgetItem() item_settings.setIcon(icon_settings) item_settings.setText(self.tr('Settings')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Add the items to the list widget self.menu_list_widget.addItem(item_all) self.menu_list_widget.addItem(item_installed) self.menu_list_widget.addItem(item_settings) # Init the message bar self.message_bar = QgsMessageBar(self) self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.vlayoutRightColumn.insertWidget(0, self.message_bar) # Progress dialog for long running processes self.progress_dialog = None # Init the repository manager dialog self.repository_manager = RepositoryManager() self.collection_manager = CollectionManager() # Collections list view self.collections_model = QStandardItemModel(0, 1) self.collections_model.sort(0, Qt.AscendingOrder) self.collection_proxy = CustomSortFilterProxyModel(self) self.collection_proxy.setSourceModel(self.collections_model) self.list_view_collections.setModel(self.collection_proxy) # Active selected collection self._selected_collection_id = None # Slots self.button_add.clicked.connect(self.add_repository) self.button_edit.clicked.connect(self.edit_repository) self.button_delete.clicked.connect(self.delete_repository) self.button_reload.clicked.connect(self.reload_repositories) self.menu_list_widget.currentRowChanged.connect(self.set_current_tab) self.list_view_collections.selectionModel().currentChanged.connect( self.on_list_view_collections_clicked) self.line_edit_filter.textChanged.connect(self.filter_collections) self.button_install.clicked.connect(self.install_collection) self.button_open.clicked.connect(self.open_collection) self.button_uninstall.clicked.connect(self.uninstall_collection) self.button_box.button(QDialogButtonBox.Help).clicked.connect( self.open_help) # Populate the repositories widget and collections list view self.populate_repositories_widget() self.reload_collections_model()
def __init__(self, *args, **kwargs): super(ListWidget, self).__init__(*args, **kwargs) self.listmodel = QStandardItemModel() self._bindvalue = None
class DialogImportData(QDialog, DIALOG_UI): open_dlg_import_schema = pyqtSignal(dict) # dict with key-value params BUTTON_NAME_IMPORT_DATA = QCoreApplication.translate( "DialogImportData", "Import data") BUTTON_NAME_GO_TO_CREATE_STRUCTURE = QCoreApplication.translate( "DialogImportData", "Go to Create Structure...") def __init__(self, iface, conn_manager, context, link_to_import_schema=True, parent=None): QDialog.__init__(self, parent) self.setupUi(self) QgsGui.instance().enableAutoGeometryRestore(self) self.iface = iface self.conn_manager = conn_manager self.db_source = context.get_db_sources()[0] self.link_to_import_schema = link_to_import_schema self.db = self.conn_manager.get_db_connector_from_source( self.db_source) self.base_configuration = BaseConfiguration() self.logger = Logger() self.app = AppInterface() self.__ladmcol_models = LADMColModelRegistry() self.java_dependency = JavaDependency() self.java_dependency.download_dependency_completed.connect( self.download_java_complete) self.java_dependency.download_dependency_progress_changed.connect( self.download_java_progress_change) self.ilicache = IliCache(self.base_configuration) self.ilicache.refresh() self._dbs_supported = ConfigDBsSupported() self._running_tool = False # There may be 1 case where we need to emit a db_connection_changed from the Import Data dialog: # 1) Connection Settings was opened and the DB conn was changed. self._db_was_changed = False # To postpone calling refresh gui until we close this dialog instead of settings # Similarly, we could call a refresh on layers and relations cache in 1 case: # 1) If the ID dialog was called for the COLLECTED source: opening Connection Settings and changing the DB # connection. self._schedule_layers_and_relations_refresh = False # We need bar definition above calling clear_messages self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.xtf_file_browse_button.clicked.connect( make_file_selector( self.xtf_file_line_edit, title=QCoreApplication.translate( "DialogImportData", "Open Transfer or Catalog File"), file_filter=QCoreApplication.translate( "DialogImportData", 'Transfer File (*.xtf *.itf);;Catalogue File (*.xml *.xls *.xlsx)' ))) self.validators = Validators() self.xtf_file_line_edit.setPlaceholderText( QCoreApplication.translate("DialogImportData", "[Name of the XTF to be imported]")) fileValidator = FileValidator(pattern=['*.xtf', '*.itf', '*.xml']) self.xtf_file_line_edit.setValidator(fileValidator) self.xtf_file_line_edit.textChanged.connect(self.update_import_models) self.xtf_file_line_edit.textChanged.emit( self.xtf_file_line_edit.text()) # db self.connection_setting_button.clicked.connect(self.show_settings) self.connection_setting_button.setText( QCoreApplication.translate("DialogImportData", "Connection Settings")) # LOG self.log_config.setTitle( QCoreApplication.translate("DialogImportData", "Show log")) self.buttonBox.accepted.disconnect() self.buttonBox.clicked.connect(self.accepted_import_data) self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) self._accept_button = self.buttonBox.addButton( self.BUTTON_NAME_IMPORT_DATA, QDialogButtonBox.AcceptRole) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.show_help) self.update_connection_info() self.restore_configuration() def accepted_import_data(self, button): if self.buttonBox.buttonRole(button) == QDialogButtonBox.AcceptRole: if button.text() == self.BUTTON_NAME_IMPORT_DATA: self.accepted() elif button.text() == self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE: self.close() # Close import data dialog self.open_dlg_import_schema.emit({ 'selected_models': self.get_ili_models() }) # Emit signal to open import schema dialog def reject(self): if self._running_tool: QMessageBox.information( self, QCoreApplication.translate("DialogImportData", "Warning"), QCoreApplication.translate( "DialogImportData", "The Import Data tool is still running. Please wait until it finishes." )) else: self.close_dialog() def close_dialog(self): """ We use this method to be safe when emitting the db_connection_changed, otherwise we could trigger slots that unload the plugin, destroying dialogs and thus, leading to crashes. """ if self._schedule_layers_and_relations_refresh: self.conn_manager.db_connection_changed.connect( self.app.core.cache_layers_and_relations) if self._db_was_changed: # If the db was changed, it implies it complies with ladm_col, hence the second parameter self.conn_manager.db_connection_changed.emit( self.db, True, self.db_source) if self._schedule_layers_and_relations_refresh: self.conn_manager.db_connection_changed.disconnect( self.app.core.cache_layers_and_relations) self.logger.info(__name__, "Dialog closed.") self.done(QDialog.Accepted) def update_connection_info(self): db_description = self.db.get_description_conn_string() if db_description: self.db_connect_label.setText(db_description) self.db_connect_label.setToolTip(self.db.get_display_conn_string()) self._accept_button.setEnabled(True) else: self.db_connect_label.setText( QCoreApplication.translate("DialogImportData", "The database is not defined!")) self.db_connect_label.setToolTip('') self._accept_button.setEnabled(False) def update_import_models(self): self.clear_messages() error_msg = None if not self.xtf_file_line_edit.text().strip(): color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel(self.import_models_qmodel) else: if os.path.isfile(self.xtf_file_line_edit.text().strip()): color = '#fff' # White self.import_models_qmodel = QStandardItemModel() model_names = get_models_from_xtf( self.xtf_file_line_edit.text().strip()) for model in self.__ladmcol_models.supported_models(): if not model.hidden() and model.full_name() in model_names: item = QStandardItem(model.full_alias()) item.setData(model.full_name(), Qt.UserRole) item.setCheckable(False) item.setEditable(False) self.import_models_qmodel.appendRow(item) if self.import_models_qmodel.rowCount() > 0: self.import_models_list_view.setModel( self.import_models_qmodel) else: error_msg = QCoreApplication.translate( "DialogImportData", "No models were found in the XTF. Is it a valid file?") color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel( self.import_models_qmodel) else: error_msg = QCoreApplication.translate( "DialogImportData", "Please set a valid XTF file") color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel( self.import_models_qmodel) self.xtf_file_line_edit.setStyleSheet( 'QLineEdit {{ background-color: {} }}'.format(color)) if error_msg: self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.import_models_list_view.setFocus() return def get_ili_models(self): ili_models = list() for index in range(self.import_models_qmodel.rowCount()): item = self.import_models_qmodel.item(index) ili_models.append(item.data(Qt.UserRole)) return ili_models def show_settings(self): # We only need those tabs related to Model Baker/ili2db operations dlg = SettingsDialog(self.conn_manager, parent=self) dlg.setWindowTitle( QCoreApplication.translate("DialogImportData", "Target DB Connection Settings")) dlg.show_tip( QCoreApplication.translate( "DialogImportData", "Configure where do you want the XTF data to be imported.")) dlg.set_db_source(self.db_source) dlg.set_tab_pages_list( [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX]) # Connect signals (DBUtils, Core) dlg.db_connection_changed.connect(self.db_connection_changed) if self.db_source == COLLECTED_DB_SOURCE: self._schedule_layers_and_relations_refresh = True dlg.set_action_type(EnumDbActionType.IMPORT) if dlg.exec_(): self.db = dlg.get_db_connection() self.update_connection_info() def db_connection_changed(self, db, ladm_col_db, db_source): self._db_was_changed = True self.clear_messages() def accepted(self): self._running_tool = True self.txtStdout.clear() self.progress_bar.setValue(0) self.bar.clearWidgets() if not os.path.isfile(self.xtf_file_line_edit.text().strip()): self._running_tool = False error_msg = QCoreApplication.translate( "DialogImportData", "Please set a valid XTF file before importing data. XTF file does not exist." ) self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.xtf_file_line_edit.setFocus() return java_home_set = self.java_dependency.set_java_home() if not java_home_set: message_java = QCoreApplication.translate( "DialogImportData", """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(message_java) self.java_dependency.get_java_on_demand() self.disable() return configuration = self.update_configuration() if configuration.disable_validation: # If data validation at import is disabled, we ask for confirmation self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Question) self.msg.setText( QCoreApplication.translate( "DialogImportData", "Are you sure you want to import your data without validation?" )) self.msg.setWindowTitle( QCoreApplication.translate("DialogImportData", "Import XTF without validation?")) self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) res = self.msg.exec_() if res == QMessageBox.No: self._running_tool = False return if not self.xtf_file_line_edit.validator().validate( configuration.xtffile, 0)[0] == QValidator.Acceptable: self._running_tool = False error_msg = QCoreApplication.translate( "DialogImportData", "Please set a valid XTF before importing data.") self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.get_ili_models(): self._running_tool = False error_msg = QCoreApplication.translate( "DialogImportData", "The selected XTF file does not have information according to the LADM-COL model to import." ) self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.import_models_list_view.setFocus() return # Get list of models present in the XTF file, in the DB and in the list of required models (by the plugin) ili_models = set([ili_model for ili_model in self.get_ili_models()]) supported_models_in_ili = set([ m.full_name() for m in self.__ladmcol_models.supported_models() ]).intersection(ili_models) if not supported_models_in_ili: self._running_tool = False error_msg = QCoreApplication.translate("DialogImportData", "The selected XTF file does not have data from any LADM-COL model supported by the LADM-COL Assistant. " \ "Therefore, you cannot import it! These are the models supported:\n\n * {}").format(" \n * ".join([m.full_alias() for m in self.__ladmcol_models.supported_models()])) self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.import_models_list_view.setFocus() return db_models = set(self.db.get_models()) suggested_models = sorted(ili_models.difference(db_models)) if not ili_models.issubset(db_models): self._running_tool = False error_msg = QCoreApplication.translate("DialogImportData", "IMPORT ERROR: The XTF file to import does not have the same models as the target database schema. " \ "Please create a schema that also includes the following missing modules:\n\n * {}").format( " \n * ".join(suggested_models)) self.txtStdout.clear() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.xtf_file_line_edit.setFocus() # button is removed to define order in GUI for button in self.buttonBox.buttons(): if button.text() == self.BUTTON_NAME_IMPORT_DATA: self.buttonBox.removeButton(button) # Check if button was previously added self.remove_create_structure_button() if self.link_to_import_schema: self.buttonBox.addButton( self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE, QDialogButtonBox.AcceptRole).setStyleSheet( "color: #aa2222;") self.buttonBox.addButton(self.BUTTON_NAME_IMPORT_DATA, QDialogButtonBox.AcceptRole) return with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() dataImporter = iliimporter.Importer(dataImport=True) db_factory = self._dbs_supported.get_db_factory(self.db.engine) dataImporter.tool = db_factory.get_model_baker_db_ili_mode() dataImporter.configuration = configuration self.save_configuration(configuration) dataImporter.stdout.connect(self.print_info) dataImporter.stderr.connect(self.on_stderr) dataImporter.process_started.connect(self.on_process_started) dataImporter.process_finished.connect(self.on_process_finished) self.progress_bar.setValue(25) try: if dataImporter.run() != iliimporter.Importer.SUCCESS: self._running_tool = False self.show_message( QCoreApplication.translate( "DialogImportData", "An error occurred when importing the data. For more information see the log..." ), Qgis.Warning) return except JavaNotFoundError: self._running_tool = False error_msg_java = QCoreApplication.translate( "DialogImportData", "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again." ).format(JAVA_REQUIRED_VERSION) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(error_msg_java) self.show_message(error_msg_java, Qgis.Warning) return self._running_tool = False self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) self.show_message( QCoreApplication.translate( "DialogImportData", "Import of the data was successfully completed"), Qgis.Success) def download_java_complete(self): self.accepted() def download_java_progress_change(self, progress): self.progress_bar.setValue(progress / 2) if (progress % 20) == 0: self.txtStdout.append('...') def remove_create_structure_button(self): for button in self.buttonBox.buttons(): if button.text() == self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE: self.buttonBox.removeButton(button) def save_configuration(self, configuration): settings = QSettings() settings.setValue( 'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_import', configuration.xtffile) settings.setValue('Asistente-LADM-COL/QgisModelBaker/show_log', not self.log_config.isCollapsed()) def restore_configuration(self): settings = QSettings() self.xtf_file_line_edit.setText( settings.value( 'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_import')) # Show log value_show_log = settings.value( 'Asistente-LADM-COL/QgisModelBaker/show_log', False, type=bool) self.log_config.setCollapsed(not value_show_log) # set model repository # if there is no option by default use online model repository self.use_local_models = settings.value( 'Asistente-LADM-COL/models/custom_model_directories_is_checked', DEFAULT_USE_CUSTOM_MODELS, type=bool) if self.use_local_models: self.custom_model_directories = settings.value( 'Asistente-LADM-COL/models/custom_models', DEFAULT_MODELS_DIR) def update_configuration(self): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ db_factory = self._dbs_supported.get_db_factory(self.db.engine) configuration = ImportDataConfiguration() db_factory.set_ili2db_configuration_params(self.db.dict_conn_params, configuration) configuration.xtffile = self.xtf_file_line_edit.text().strip() configuration.delete_data = False configuration.srs_auth = QSettings().value( 'Asistente-LADM-COL/QgisModelBaker/srs_auth', DEFAULT_SRS_AUTH, str) configuration.srs_code = QSettings().value( 'Asistente-LADM-COL/QgisModelBaker/srs_code', int(DEFAULT_SRS_CODE), int) configuration.inheritance = ILI2DBNames.DEFAULT_INHERITANCE configuration.create_basket_col = ILI2DBNames.CREATE_BASKET_COL configuration.create_import_tid = ILI2DBNames.CREATE_IMPORT_TID configuration.stroke_arcs = ILI2DBNames.STROKE_ARCS configuration.with_importtid = True full_java_exe_path = JavaDependency.get_full_java_exe_path() if full_java_exe_path: self.base_configuration.java_path = full_java_exe_path # User could have changed the default values self.use_local_models = QSettings().value( 'Asistente-LADM-COL/models/custom_model_directories_is_checked', DEFAULT_USE_CUSTOM_MODELS, type=bool) self.custom_model_directories = QSettings().value( 'Asistente-LADM-COL/models/custom_models', DEFAULT_MODELS_DIR) # Check custom model directories if self.use_local_models: if not self.custom_model_directories: self.base_configuration.custom_model_directories_enabled = False else: self.base_configuration.custom_model_directories = self.custom_model_directories self.base_configuration.custom_model_directories_enabled = True configuration.base_configuration = self.base_configuration if self.get_ili_models(): configuration.ilimodels = ';'.join(self.get_ili_models()) configuration.disable_validation = not QSettings().value( 'Asistente-LADM-COL/models/validate_data_importing_exporting', True, bool) return configuration def print_info(self, text, text_color='#000000', clear=False): self.txtStdout.setTextColor(QColor(text_color)) self.txtStdout.append(text) QCoreApplication.processEvents() def on_stderr(self, text): color_log_text(text, self.txtStdout) self.advance_progress_bar_by_text(text) def on_process_started(self, command): self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(command) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): color = '#004905' if exit_code == 0 else '#aa2222' self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append('Finished ({})'.format(exit_code)) if result == iliimporter.Importer.SUCCESS: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: self.show_message( QCoreApplication.translate("DialogImportData", "Error when importing data"), Qgis.Warning) # Open log if self.log_config.isCollapsed(): self.log_config.setCollapsed(False) def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models...': self.progress_bar.setValue(50) QCoreApplication.processEvents() elif text.strip() == 'Info: create table structure...': self.progress_bar.setValue(55) QCoreApplication.processEvents() elif text.strip() == 'Info: first validation pass...': self.progress_bar.setValue(60) QCoreApplication.processEvents() elif text.strip() == 'Info: second validation pass...': self.progress_bar.setValue(80) QCoreApplication.processEvents() def clear_messages(self): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.txtStdout.clear() # Clear previous log messages self.progress_bar.setValue(0) # Initialize progress bar def show_help(self): show_plugin_help("import_data") def disable(self): self.source_config.setEnabled(False) self.target_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.source_config.setEnabled(True) self.target_config.setEnabled(True) self.buttonBox.setEnabled(True) def show_message(self, message, level): if level == Qgis.Warning: self.enable() self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage("Asistente LADM-COL", message, level, duration=0)
class ConfigDialog(BASE, WIDGET): def __init__(self, toolbox): super(ConfigDialog, self).__init__(None) self.setupUi(self) self.toolbox = toolbox self.groupIcon = QIcon() self.groupIcon.addPixmap(self.style().standardPixmap( QStyle.SP_DirClosedIcon), QIcon.Normal, QIcon.Off) self.groupIcon.addPixmap(self.style().standardPixmap( QStyle.SP_DirOpenIcon), QIcon.Normal, QIcon.On) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) self.model = QStandardItemModel() self.tree.setModel(self.model) self.delegate = SettingDelegate() self.tree.setItemDelegateForColumn(1, self.delegate) self.searchBox.textChanged.connect(self.textChanged) self.fillTree() self.tree.expanded.connect(self.adjustColumns) def textChanged(self): text = str(self.searchBox.text().lower()) self._filterItem(self.model.invisibleRootItem(), text) if text: self.tree.expandAll() else: self.tree.collapseAll() def _filterItem(self, item, text): if item.hasChildren(): show = False for i in range(item.rowCount()): child = item.child(i) showChild = self._filterItem(child, text) show = (showChild or show) self.tree.setRowHidden(item.row(), item.index().parent(), not show) return show elif isinstance(item, QStandardItem): hide = bool(text) and (text not in item.text().lower()) self.tree.setRowHidden(item.row(), item.index().parent(), hide) return not hide def fillTree(self): self.fillTreeUsingProviders() def fillTreeUsingProviders(self): self.items = {} self.model.clear() self.model.setHorizontalHeaderLabels([self.tr('Setting'), self.tr('Value')]) settings = ProcessingConfig.getSettings() rootItem = self.model.invisibleRootItem() """ Filter 'General', 'Models' and 'Scripts' items """ priorityKeys = [self.tr('General'), self.tr('Models'), self.tr('Scripts')] for group in priorityKeys: groupItem = QStandardItem(group) icon = ProcessingConfig.getGroupIcon(group) groupItem.setIcon(icon) groupItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [groupItem, emptyItem]) # add menu item only if it has any search matches for setting in settings[group]: if setting.hidden or setting.name.startswith("MENU_"): continue labelItem = QStandardItem(setting.description) labelItem.setIcon(icon) labelItem.setEditable(False) self.items[setting] = SettingItem(setting) groupItem.insertRow(0, [labelItem, self.items[setting]]) """ Filter 'Providers' items """ providersItem = QStandardItem(self.tr('Providers')) icon = QIcon(os.path.join(pluginPath, 'images', 'alg.png')) providersItem.setIcon(icon) providersItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [providersItem, emptyItem]) for group in list(settings.keys()): if group in priorityKeys or group == menusSettingsGroup: continue groupItem = QStandardItem(group) icon = ProcessingConfig.getGroupIcon(group) groupItem.setIcon(icon) groupItem.setEditable(False) for setting in settings[group]: if setting.hidden: continue labelItem = QStandardItem(setting.description) labelItem.setIcon(icon) labelItem.setEditable(False) self.items[setting] = SettingItem(setting) groupItem.insertRow(0, [labelItem, self.items[setting]]) emptyItem = QStandardItem() emptyItem.setEditable(False) providersItem.appendRow([groupItem, emptyItem]) """ Filter 'Menus' items """ menusItem = QStandardItem(self.tr('Menus')) icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png')) menusItem.setIcon(icon) menusItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [menusItem, emptyItem]) button = QPushButton(self.tr('Reset to defaults')) button.clicked.connect(self.resetMenusToDefaults) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(button) layout.addStretch() widget = QWidget() widget.setLayout(layout) self.tree.setIndexWidget(emptyItem.index(), widget) providers = Processing.providers for provider in providers: providerDescription = provider.getDescription() groupItem = QStandardItem(providerDescription) icon = provider.getIcon() groupItem.setIcon(icon) groupItem.setEditable(False) for alg in provider.algs: algItem = QStandardItem(alg.i18n_name or alg.name) algItem.setIcon(icon) algItem.setEditable(False) try: settingMenu = ProcessingConfig.settings["MENU_" + alg.commandLineName()] settingButton = ProcessingConfig.settings["BUTTON_" + alg.commandLineName()] settingIcon = ProcessingConfig.settings["ICON_" + alg.commandLineName()] except: continue self.items[settingMenu] = SettingItem(settingMenu) self.items[settingButton] = SettingItem(settingButton) self.items[settingIcon] = SettingItem(settingIcon) menuLabelItem = QStandardItem("Menu path") menuLabelItem.setEditable(False) buttonLabelItem = QStandardItem("Add button in toolbar") buttonLabelItem.setEditable(False) iconLabelItem = QStandardItem("Icon") iconLabelItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) algItem.insertRow(0, [menuLabelItem, self.items[settingMenu]]) algItem.insertRow(0, [buttonLabelItem, self.items[settingButton]]) algItem.insertRow(0, [iconLabelItem, self.items[settingIcon]]) groupItem.insertRow(0, [algItem, emptyItem]) emptyItem = QStandardItem() emptyItem.setEditable(False) menusItem.appendRow([groupItem, emptyItem]) self.tree.sortByColumn(0, Qt.AscendingOrder) self.adjustColumns() def resetMenusToDefaults(self): providers = Processing.providers for provider in providers: for alg in provider.algs: d = defaultMenuEntries.get(alg.commandLineName(), "") setting = ProcessingConfig.settings["MENU_" + alg.commandLineName()] item = self.items[setting] item.setData(d, Qt.EditRole) def accept(self): for setting in list(self.items.keys()): if isinstance(setting.value, bool): setting.setValue(self.items[setting].checkState() == Qt.Checked) else: try: setting.setValue(str(self.items[setting].text())) except ValueError as e: QMessageBox.warning(self, self.tr('Wrong value'), self.tr('Wrong value for parameter "%s":\n\n%s' % (setting.description, str(e)))) return setting.save() Processing.updateAlgsList() settingsWatcher.settingsChanged.emit() QDialog.accept(self) def adjustColumns(self): self.tree.resizeColumnToContents(0) self.tree.resizeColumnToContents(1)
class ModelDeletionDialog(uicls, basecls): """Dialog for model(s) deletion.""" def __init__(self, plugin_dock, parent): super().__init__(parent) self.setupUi(self) self.parent_widget = parent self.plugin_dock = plugin_dock self.communication = self.plugin_dock.communication self.threedi_api = self.plugin_dock.threedi_api self.local_schematisation = self.plugin_dock.current_local_schematisation self.threedi_models = None self.models_model = QStandardItemModel() self.models_tv.setModel(self.models_model) self.pb_delete.clicked.connect(self.delete_models) self.pb_cancel.clicked.connect(self.reject) self.models_tv.selectionModel().selectionChanged.connect( self.toggle_delete_models) self.fetch_3di_models() def toggle_delete_models(self): """Toggle delete button if any model is selected.""" selection_model = self.models_tv.selectionModel() if selection_model.hasSelection(): self.pb_delete.setEnabled(True) else: self.pb_delete.setDisabled(True) def fetch_3di_models(self): """Fetching 3Di models list.""" try: tc = ThreediCalls(self.threedi_api) threedi_models, models_count = tc.fetch_3di_models_with_count( limit=tc.FETCH_LIMIT, schematisation_name=self.local_schematisation.name, show_invalid=True) self.models_model.clear() if models_count < self.parent_widget.MAX_SCHEMATISATION_MODELS: self.accept() header = [ "ID", "Model", "Schematisation", "Revision", "Last updated", "Updated by" ] self.models_model.setHorizontalHeaderLabels(header) for sim_model in sorted(threedi_models, key=attrgetter("revision_commit_date"), reverse=True): if sim_model.schematisation_id != self.local_schematisation.id: continue id_item = QStandardItem(str(sim_model.id)) name_item = QStandardItem(sim_model.name) name_item.setData(sim_model, role=Qt.UserRole) schema_item = QStandardItem(sim_model.schematisation_name) rev_item = QStandardItem(sim_model.revision_number) last_updated_day = sim_model.revision_commit_date.split("T")[0] lu_datetime = QDateTime.fromString(last_updated_day, "yyyy-MM-dd") lu_item = QStandardItem(lu_datetime.toString("dd-MMMM-yyyy")) ub_item = QStandardItem(sim_model.user) self.models_model.appendRow([ id_item, name_item, schema_item, rev_item, lu_item, ub_item ]) self.threedi_models = threedi_models except ApiException as e: error_msg = extract_error_message(e) self.communication.show_error(error_msg) except Exception as e: error_msg = f"Error: {e}" self.communication.show_error(error_msg) def delete_models(self): """Deleting selected model(s).""" selection_model = self.models_tv.selectionModel() if not selection_model.hasSelection(): return try: tc = ThreediCalls(self.threedi_api) for index in selection_model.selectedRows(): current_row = index.row() model_id_item = self.models_model.item(current_row, 0) model_id = int(model_id_item.text()) tc.delete_3di_model(model_id) except ApiException as e: error_msg = extract_error_message(e) self.communication.show_error(error_msg) except Exception as e: error_msg = f"Error: {e}" self.communication.show_error(error_msg) finally: self.fetch_3di_models()
def __init__(self, iface, layer, parent=None): QWidget.__init__(self, parent) self.iface = iface self.layer = layer uri = QgsDataSourceUri(layer.source()) dbplugin = None db = None if layer.dataProvider().name() == 'postgres': dbplugin = createDbPlugin('postgis', 'postgres') elif layer.dataProvider().name() == 'spatialite': dbplugin = createDbPlugin('spatialite', 'spatialite') elif layer.dataProvider().name() == 'oracle': dbplugin = createDbPlugin('oracle', 'oracle') elif layer.dataProvider().name() == 'virtual': dbplugin = createDbPlugin('vlayers', 'virtual') elif layer.dataProvider().name() == 'ogr': dbplugin = createDbPlugin('gpkg', 'gpkg') if dbplugin: dbplugin.connectToUri(uri) db = dbplugin.db self.dbplugin = dbplugin 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.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText(self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) 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 self.layerTypeWidget.hide() # show if load as raster is supported #self.loadLayerBtn.clicked.connect(self.loadSqlLayer) self.updateLayerBtn.clicked.connect(self.updateSqlLayer) self.getColumnsBtn.clicked.connect(self.fillColumnCombos) self.queryBuilderFirst = True self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif")) self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder) self.presetName.textChanged.connect(self.nameChanged) # Update from layer # Fisrtly the SQL from QgsDataSourceUri table sql = uri.table() if uri.keyColumn() == '_uid_': match = re.search('^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$', sql, re.S) if match: sql = match.group(1) else: match = re.search('^\((SELECT .+ FROM .+)\)$', sql, re.S) if match: sql = match.group(1) self.editSql.setText(sql) self.executeSql() # Then the columns self.geomCombo.setCurrentIndex(self.geomCombo.findText(uri.geometryColumn(), Qt.MatchExactly)) if uri.keyColumn() != '_uid_': self.uniqueColumnCheck.setCheckState(Qt.Checked) if self.allowMultiColumnPk: itemsData = uri.keyColumn().split(',') for item in self.uniqueModel.findItems("*", Qt.MatchWildcard): if item.data() in itemsData: item.setCheckState(Qt.Checked) else: keyColumn = uri.keyColumn() for item in self.uniqueModel.findItems("*", Qt.MatchWildcard): if item.data() == keyColumn: self.uniqueCombo.setCurrentIndex(self.uniqueModel.indexFromItem(item).row()) # Finally layer name, filter and selectAtId self.layerNameEdit.setText(layer.name()) self.filter = uri.sql() if uri.selectAtIdDisabled(): self.avoidSelectById.setCheckState(Qt.Checked)
class ResourceSharingDialog(QDialog, FORM_CLASS): TAB_ALL = 0 TAB_INSTALLED = 1 TAB_SETTINGS = 2 def __init__(self, parent=None, iface=None): """Constructor. :param parent: Optional widget to use as parent :type parent: QWidget :param iface: An instance of QGisInterface :type iface: QGisInterface """ super(ResourceSharingDialog, self).__init__(parent) self.setupUi(self) self.iface = iface # Reconfigure UI self.setModal(True) self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) self.button_install.setEnabled(False) self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Set up the "main menu" - QListWidgetItem # All collections icon_all = QIcon() icon_all.addFile(str(resources_path('img', 'plugin.svg')), QSize(), QIcon.Normal, QIcon.Off) item_all = QListWidgetItem() item_all.setIcon(icon_all) item_all.setText(self.tr('All collections')) # Installed collections icon_installed = QIcon() icon_installed.addFile( str(resources_path('img', 'plugin-installed.svg')), QSize(), QIcon.Normal, QIcon.Off) item_installed = QListWidgetItem() item_installed.setIcon(icon_installed) item_installed.setText(self.tr('Installed collections')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Settings / repositories icon_settings = QIcon() icon_settings.addFile(str(resources_path('img', 'settings.svg')), QSize(), QIcon.Normal, QIcon.Off) item_settings = QListWidgetItem() item_settings.setIcon(icon_settings) item_settings.setText(self.tr('Settings')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Add the items to the list widget self.menu_list_widget.addItem(item_all) self.menu_list_widget.addItem(item_installed) self.menu_list_widget.addItem(item_settings) # Init the message bar self.message_bar = QgsMessageBar(self) self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.vlayoutRightColumn.insertWidget(0, self.message_bar) # Progress dialog for long running processes self.progress_dialog = None # Init the repository manager dialog self.repository_manager = RepositoryManager() self.collection_manager = CollectionManager() # Collections list view self.collections_model = QStandardItemModel(0, 1) self.collections_model.sort(0, Qt.AscendingOrder) self.collection_proxy = CustomSortFilterProxyModel(self) self.collection_proxy.setSourceModel(self.collections_model) self.list_view_collections.setModel(self.collection_proxy) # Active selected collection self._selected_collection_id = None # Slots self.button_add.clicked.connect(self.add_repository) self.button_edit.clicked.connect(self.edit_repository) self.button_delete.clicked.connect(self.delete_repository) self.button_reload.clicked.connect(self.reload_repositories) self.menu_list_widget.currentRowChanged.connect(self.set_current_tab) self.list_view_collections.selectionModel().currentChanged.connect( self.on_list_view_collections_clicked) self.line_edit_filter.textChanged.connect(self.filter_collections) self.button_install.clicked.connect(self.install_collection) self.button_open.clicked.connect(self.open_collection) self.button_uninstall.clicked.connect(self.uninstall_collection) self.button_box.button(QDialogButtonBox.Help).clicked.connect( self.open_help) # Populate the repositories widget and collections list view self.populate_repositories_widget() self.reload_collections_model() def set_current_tab(self, index): """Set stacked widget based on the active tab. :param index: The index of the active widget (in the list widget). :type index: int """ # Clear message bar self.message_bar.clearWidgets() if index == (self.menu_list_widget.count() - 1): # Last menu entry - Settings self.stacked_menu_widget.setCurrentIndex(1) else: # Not settings, must be Collections (all or installed) if index == 1: # Installed collections self.collection_proxy.accepted_status = \ COLLECTION_INSTALLED_STATUS # Set the web view title = self.tr('Installed Collections') description = self.tr( 'On the left you see the list of all the ' 'installed collections.') else: # All collections (0) self.collection_proxy.accepted_status = COLLECTION_ALL_STATUS # Set the web view title = self.tr('All Collections') description = self.tr( 'On the left you see a list of all the collections ' 'that are available from the registered repositories.<br> ' 'Installed collections are emphasized (in <b>bold</b>).') context = { 'resources_path': str(resources_path()), 'title': title, 'description': description } self.web_view_details.setHtml( render_template('tab_description.html', context)) self.stacked_menu_widget.setCurrentIndex(0) def add_repository(self): """Open add repository dialog.""" dlg = ManageRepositoryDialog(self) if not dlg.exec_(): return for repoName, repo in self.repository_manager.directories.items(): if dlg.line_edit_url.text().strip() == repo['url']: self.message_bar.pushMessage( self.tr( 'Unable to add another repository with the same URL!'), Qgis.Warning, 5) return if dlg.line_edit_name.text().strip() == repoName: self.message_bar.pushMessage( self.tr('Repositories must have unique names!'), Qgis.Warning, 5) return repo_name = dlg.line_edit_name.text() repo_url = dlg.line_edit_url.text().strip() repo_auth_cfg = dlg.line_edit_auth_id.text().strip() if repo_name in self.repository_manager.directories: repo_name += '(2)' # Show progress dialog self.show_progress_dialog("Fetching repository's metadata") # Add repository try: status, adderror = self.repository_manager.add_directory( repo_name, repo_url, repo_auth_cfg) if status: self.message_bar.pushMessage( self.tr('Repository was successfully added'), Qgis.Success, 5) else: self.message_bar.pushMessage( self.tr('Unable to add repository: %s') % adderror, Qgis.Warning, 5) except Exception as e: self.message_bar.pushMessage(self.tr('%s') % e, Qgis.Warning, 5) finally: self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() # Deactivate edit and delete button self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def edit_repository(self): """Open edit repository dialog.""" selected_item = self.tree_repositories.currentItem() if selected_item: repo_name = selected_item.text(0) if not repo_name: return # Check if it is among the officially approved QGIS repositories settings = QgsSettings() settings.beginGroup(repo_settings_group()) if settings.value(repo_name + '/url') in \ self.repository_manager._online_directories.values(): self.message_bar.pushMessage( self.tr('You can not edit the official repositories!'), Qgis.Warning, 5) return dlg = ManageRepositoryDialog(self) dlg.line_edit_name.setText(repo_name) dlg.line_edit_url.setText( self.repository_manager.directories[repo_name]['url']) dlg.line_edit_auth_id.setText( self.repository_manager.directories[repo_name]['auth_cfg']) if not dlg.exec_(): return # Check if the changed URL is already present and that # the new repository name is unique new_url = dlg.line_edit_url.text().strip() old_url = self.repository_manager.directories[repo_name]['url'] new_name = dlg.line_edit_name.text().strip() for repoName, repo in self.repository_manager.directories.items(): if new_url == repo['url'] and (old_url != new_url): self.message_bar.pushMessage( self.tr('Unable to add another repository with the same ' 'URL!'), Qgis.Warning, 5) return if new_name == repoName and (repo_name != new_name): self.message_bar.pushMessage( self.tr('Repositories must have unique names!'), Qgis.Warning, 5) return # Redundant if (new_name in self.repository_manager.directories) and (new_name != repo_name): new_name += '(2)' new_auth_cfg = dlg.line_edit_auth_id.text() # Show progress dialog self.show_progress_dialog("Fetching repository's metadata") # Edit repository try: status, editerror = self.repository_manager.edit_directory( repo_name, new_name, old_url, new_url, new_auth_cfg) if status: self.message_bar.pushMessage( self.tr('Repository is successfully updated'), Qgis.Success, 5) else: self.message_bar.pushMessage( self.tr('Unable to edit repository: %s') % editerror, Qgis.Warning, 5) except Exception as e: self.message_bar.pushMessage(self.tr('%s') % e, Qgis.Warning, 5) finally: self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() # Deactivate the edit and delete buttons self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def delete_repository(self): """Delete a repository in the tree widget.""" selected_item = self.tree_repositories.currentItem() if selected_item: repo_name = selected_item.text(0) if not repo_name: return # Check if it is among the offical repositories repo_url = self.repository_manager.directories[repo_name]['url'] if repo_url in self.repository_manager._online_directories.values(): self.message_bar.pushMessage( self.tr('You can not remove official repositories!'), Qgis.Warning, 5) return warning = self.tr('Are you sure you want to remove the following ' 'repository?') + '\n' + repo_name if QMessageBox.warning(self, self.tr('QGIS Resource Sharing'), warning, QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return # Remove repository installed_collections = \ self.collection_manager.get_installed_collections(repo_url) if installed_collections: message = ('You have installed collections from this ' 'repository. Please uninstall them first!') self.message_bar.pushMessage(message, Qgis.Warning, 5) else: self.repository_manager.remove_directory(repo_name) # Reload data and widget self.reload_data_and_widget() # Deactivate the edit and delete buttons self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def reload_repositories(self): """Slot for when user clicks reload repositories button.""" # Show progress dialog self.show_progress_dialog('Reloading all repositories') for repo_name in self.repository_manager.directories: directory = self.repository_manager.directories[repo_name] url = directory['url'] auth_cfg = directory['auth_cfg'] try: status, reloaderror = self.repository_manager.reload_directory( repo_name, url, auth_cfg) if status: self.message_bar.pushMessage( self.tr('Repository %s is successfully reloaded') % repo_name, Qgis.Info, 5) else: self.message_bar.pushMessage( self.tr('Unable to reload %s: %s') % (repo_name, reloaderror), Qgis.Warning, 5) except Exception as e: self.message_bar.pushMessage( self.tr('%s') % e, Qgis.Warning, 5) self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() def install_collection(self): """Slot for when the user clicks the install/reinstall button.""" # Save the current index to enable selection after installation self.current_index = self.list_view_collections.currentIndex() self.show_progress_dialog('Starting installation...') self.progress_dialog.canceled.connect(self.install_canceled) self.installer_thread = QThread() self.installer_worker = CollectionInstaller( self.collection_manager, self._selected_collection_id) self.installer_worker.moveToThread(self.installer_thread) self.installer_worker.finished.connect(self.install_finished) self.installer_worker.aborted.connect(self.install_aborted) self.installer_worker.progress.connect(self.install_progress) self.installer_thread.started.connect(self.installer_worker.run) self.installer_thread.start() def install_finished(self): # Process the result self.progress_dialog.hide() installStatus = self.installer_worker.install_status if not installStatus: message = self.installer_worker.error_message # Clean up the worker and thread self.installer_worker.deleteLater() self.installer_thread.quit() self.installer_thread.wait() self.installer_thread.deleteLater() if installStatus: self.reload_collections_model() # Report what has been installed message = '<b>%s</b> was successfully installed, containing:\n<ul>' % ( config.COLLECTIONS[self._selected_collection_id]['name']) number = 0 if 'style' in config.COLLECTIONS[ self._selected_collection_id].keys(): number = config.COLLECTIONS[ self._selected_collection_id]['style'] message = message + '\n<li> ' + str( number) + ' Layer style (QML) file' if number > 1: message = message + 's' if 'symbol' in config.COLLECTIONS[ self._selected_collection_id].keys(): number = config.COLLECTIONS[ self._selected_collection_id]['symbol'] message = message + '\n<li> ' + str( number) + ' XML symbol file' if number > 1: message = message + 's' if 'svg' in config.COLLECTIONS[ self._selected_collection_id].keys(): number = config.COLLECTIONS[ self._selected_collection_id]['svg'] message = message + '\n<li> ' + str(number) + ' SVG file' if number > 1: message = message + 's' if 'models' in config.COLLECTIONS[ self._selected_collection_id].keys(): number = config.COLLECTIONS[ self._selected_collection_id]['models'] message = message + '\n<li> ' + str(number) + ' model' if number > 1: message = message + 's' if 'expressions' in config.COLLECTIONS[ self._selected_collection_id].keys(): number = config.COLLECTIONS[ self._selected_collection_id]['expressions'] message = message + '\n<li> ' + str( number) + ' expression file' if number > 1: message = message + 's' if 'processing' in config.COLLECTIONS[ self._selected_collection_id].keys(): number = config.COLLECTIONS[ self._selected_collection_id]['processing'] message = message + '\n<li> ' + str( number) + ' processing script' if number > 1: message = message + 's' if 'rscripts' in config.COLLECTIONS[ self._selected_collection_id].keys(): number = config.COLLECTIONS[ self._selected_collection_id]['rscripts'] message = message + '\n<li> ' + str(number) + ' R script' if number > 1: message = message + 's' message = message + '\n</ul>' QMessageBox.information(self, 'Resource Sharing', message) self.populate_repositories_widget() # Set the selection oldRow = self.current_index.row() newIndex = self.collections_model.createIndex(oldRow, 0) selection_model = self.list_view_collections.selectionModel() selection_model.setCurrentIndex(newIndex, selection_model.ClearAndSelect) selection_model.select(newIndex, selection_model.ClearAndSelect) # Update the buttons self.button_install.setEnabled(True) self.button_install.setText('Reinstall') self.button_open.setEnabled(True) self.button_uninstall.setEnabled(True) self.show_collection_metadata(self._selected_collection_id) def install_canceled(self): self.progress_dialog.hide() self.show_progress_dialog('Cancelling installation...') self.installer_worker.abort() def install_aborted(self): if self.installer_thread.isRunning(): self.installer_thread.quit() self.installer_thread.finished.connect(self.progress_dialog.hide) def install_progress(self, text): self.progress_dialog.setLabelText(text) def uninstall_collection(self): """Slot called when user clicks the uninstall button.""" # get the QModelIndex for the item to be uninstalled uninstall_index = self.list_view_collections.currentIndex() coll_id = self._selected_collection_id try: self.collection_manager.uninstall(coll_id) except Exception as e: LOGGER.error('Could not uninstall collection ' + config.COLLECTIONS[coll_id]['name'] + ':\n' + str(e)) else: QMessageBox.information( self, 'Resource Sharing', 'The collection was successfully uninstalled!') self.reload_collections_model() # Fix the GUI currentMenuRow = self.menu_list_widget.currentRow() self.set_current_tab(currentMenuRow) self.populate_repositories_widget() rowCount = self.collection_proxy.rowCount() if rowCount > 0: # Set the current (and selected) row in the listview newRow = uninstall_index.row() # Check if this was the last element rowCount = self.collection_proxy.rowCount() if newRow == rowCount: newRow = newRow - 1 # Select the new current element newIndex = self.collections_model.createIndex(newRow, 0) selection_model = self.list_view_collections.selectionModel() selection_model.setCurrentIndex(newIndex, selection_model.ClearAndSelect) # Get the id of the current collection proxyModel = self.list_view_collections.model() proxyIndex = proxyModel.index(newRow, 0) current_coll_id = proxyIndex.data(COLLECTION_ID_ROLE) self._selected_collection_id = current_coll_id # Update buttons status = config.COLLECTIONS[current_coll_id]['status'] if status == COLLECTION_INSTALLED_STATUS: self.button_install.setEnabled(True) self.button_install.setText('Reinstall') self.button_open.setEnabled(True) self.button_uninstall.setEnabled(True) else: self.button_install.setEnabled(True) self.button_install.setText('Install') self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Update the web_view_details frame self.show_collection_metadata(current_coll_id) else: self.button_install.setEnabled(False) self.button_install.setText('Install') self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) def open_collection(self): """Slot for when user clicks 'Open' button.""" collection_path = local_collection_path(self._selected_collection_id) directory_url = QUrl.fromLocalFile(str(collection_path)) QDesktopServices.openUrl(directory_url) def reload_data_and_widget(self): """Reload repositories and collections and update widgets related.""" self.reload_repositories_widget() self.reload_collections_model() def reload_repositories_widget(self): """Refresh tree repositories using new repositories data.""" self.repository_manager.load_directories() self.populate_repositories_widget() def populate_repositories_widget(self): """Populate the current dictionary repositories to the tree widget.""" # Clear the current tree widget self.tree_repositories.clear() installed_collections = \ self.collection_manager.get_installed_collections() # Export the updated ones from the repository manager for repo_name in self.repository_manager.directories: url = self.repository_manager.directories[repo_name]['url'] item = QTreeWidgetItem(self.tree_repositories, REPOSITORY_ITEM) item.setText(0, repo_name) item.setText(1, url) for coll_id in config.COLLECTIONS: if ('repository_name' in config.COLLECTIONS[coll_id].keys() and config.COLLECTIONS[coll_id]['repository_name'] == repo_name): coll_name = config.COLLECTIONS[coll_id]['name'] coll_tags = config.COLLECTIONS[coll_id]['tags'] collectionItem = QTreeWidgetItem(item, COLLECTION_ITEM) collitemtext = coll_name if installed_collections and coll_id in installed_collections.keys( ): collitemtext = coll_name + ' (installed)' collectionFont = QFont() collectionFont.setWeight(60) collectionItem.setFont(0, collectionFont) collectionItem.setText(0, collitemtext) collectionItem.setText(1, coll_tags) self.tree_repositories.resizeColumnToContents(0) self.tree_repositories.resizeColumnToContents(1) self.tree_repositories.sortItems(1, Qt.AscendingOrder) def reload_collections_model(self): """Reload the collections model with the current collections.""" self.collections_model.clear() installed_collections = \ self.collection_manager.get_installed_collections() for id in config.COLLECTIONS: collection_name = config.COLLECTIONS[id]['name'] collection_author = config.COLLECTIONS[id]['author'] collection_tags = config.COLLECTIONS[id]['tags'] collection_description = config.COLLECTIONS[id]['description'] collection_status = config.COLLECTIONS[id]['status'] repository_name = '' if 'repository_name' in config.COLLECTIONS[id].keys(): repository_name = config.COLLECTIONS[id]['repository_name'] item = QStandardItem(collection_name + ' (' + repository_name + ')') item.setEditable(False) item.setData(id, COLLECTION_ID_ROLE) item.setData(collection_name, COLLECTION_NAME_ROLE) item.setData(collection_description, COLLECTION_DESCRIPTION_ROLE) item.setData(collection_author, COLLECTION_AUTHOR_ROLE) item.setData(collection_tags, COLLECTION_TAGS_ROLE) item.setData(collection_status, COLLECTION_STATUS_ROLE) # Make installed collections stand out if installed_collections and id in installed_collections.keys(): collectionFont = QFont() collectionFont.setWeight(60) item.setFont(collectionFont) self.collections_model.appendRow(item) self.collections_model.sort(0, Qt.AscendingOrder) def on_tree_repositories_itemSelectionChanged(self): """Slot for the itemSelectionChanged signal of tree_repositories.""" selected_item = self.tree_repositories.currentItem() if selected_item and selected_item.type() == REPOSITORY_ITEM: if selected_item: repo_name = selected_item.text(0) if not repo_name: return if not repo_name in self.repository_manager.directories.keys(): return repo_url = self.repository_manager.directories[repo_name]['url'] # Disable the edit and delete buttons for "official" repositories if repo_url in self.repository_manager._online_directories.values( ): self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) else: # Activate the edit and delete buttons self.button_edit.setEnabled(True) self.button_delete.setEnabled(True) elif selected_item and selected_item.type() == COLLECTION_ITEM: self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) else: self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def on_list_view_collections_clicked(self, index): """Slot for when the list_view_collections is clicked.""" real_index = self.collection_proxy.mapToSource(index) if real_index.row() != -1: collection_item = self.collections_model.itemFromIndex(real_index) collection_id = collection_item.data(COLLECTION_ID_ROLE) self._selected_collection_id = collection_id # Enable / disable buttons status = config.COLLECTIONS[self._selected_collection_id]['status'] is_installed = status == COLLECTION_INSTALLED_STATUS if is_installed: self.button_install.setEnabled(True) self.button_install.setText('Reinstall') self.button_open.setEnabled(True) self.button_uninstall.setEnabled(True) else: self.button_install.setEnabled(True) self.button_install.setText('Install') self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Show metadata self.show_collection_metadata(collection_id) @pyqtSlot(str) def filter_collections(self, text): search = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp) self.collection_proxy.setFilterRegExp(search) def show_collection_metadata(self, id): """Show the collection metadata given the ID.""" html = self.collection_manager.get_html(id) self.web_view_details.setHtml(html) def reject(self): """Slot when the dialog is closed.""" # Serialize collections to settings self.repository_manager.serialize_repositories() self.done(0) def open_help(self): """Open help.""" doc_url = QUrl('http://qgis-contribution.github.io/' + 'QGIS-ResourceSharing/') QDesktopServices.openUrl(doc_url) def show_progress_dialog(self, text): """Show infinite progress dialog with given text. :param text: Text as the label of the progress dialog :type text: str """ if self.progress_dialog is None: self.progress_dialog = QProgressDialog(self) self.progress_dialog.setWindowModality(Qt.WindowModal) self.progress_dialog.setAutoClose(False) title = self.tr('Resource Sharing') self.progress_dialog.setWindowTitle(title) # Just use an infinite progress bar here self.progress_dialog.setMaximum(0) self.progress_dialog.setMinimum(0) self.progress_dialog.setValue(0) self.progress_dialog.setLabelText(text) self.progress_dialog.show()
class DlgSqlWindow(QWidget, Ui_Dialog): nameChanged = pyqtSignal(str) QUERY_HISTORY_LIMIT = 20 def __init__(self, iface, db, parent=None): QWidget.__init__(self, parent) self.mainWindow = parent self.iface = iface self.db = db self.dbType = db.connection().typeNameString() self.connectionName = db.connection().connectionName() self.filter = "" self.modelAsync = None 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( self.tr(u"{0} - {1} [{2}]").format(self.windowTitle(), self.connectionName, self.dbType)) self.defaultLayerName = self.tr('QueryLayer') if self.allowMultiColumnPk: self.uniqueColumnCheck.setText(self.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText(self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) self.initCompleter() settings = QgsSettings() self.history = settings.value('DB_Manager/queryHistory/' + self.dbType, {self.connectionName: []}) if self.connectionName not in self.history: self.history[self.connectionName] = [] self.queryHistoryWidget.setVisible(False) self.queryHistoryTableWidget.verticalHeader().hide() self.queryHistoryTableWidget.doubleClicked.connect(self.insertQueryInEditor) self.populateQueryHistory() self.btnQueryHistory.toggled.connect(self.showHideQueryHistory) self.btnCancel.setEnabled(False) self.btnCancel.clicked.connect(self.executeSqlCanceled) self.btnCancel.setShortcut(QKeySequence.Cancel) self.progressBar.setEnabled(False) self.progressBar.setRange(0, 100) self.progressBar.setValue(0) self.progressBar.setFormat("") self.progressBar.setAlignment(Qt.AlignCenter) # 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.presetSaveAsFile.clicked.connect(self.saveAsFilePreset) self.presetLoadFile.clicked.connect(self.loadFilePreset) 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 insertQueryInEditor(self, item): sql = item.data(Qt.DisplayRole) self.editSql.insertText(sql) def showHideQueryHistory(self, visible): self.queryHistoryWidget.setVisible(visible) def populateQueryHistory(self): self.queryHistoryTableWidget.clearContents() self.queryHistoryTableWidget.setRowCount(0) dictlist = self.history[self.connectionName] if not dictlist: return for i in range(len(dictlist)): self.queryHistoryTableWidget.insertRow(0) queryItem = QTableWidgetItem(dictlist[i]['query']) rowsItem = QTableWidgetItem(str(dictlist[i]['rows'])) durationItem = QTableWidgetItem(str(dictlist[i]['secs'])) self.queryHistoryTableWidget.setItem(0, 0, queryItem) self.queryHistoryTableWidget.setItem(0, 1, rowsItem) self.queryHistoryTableWidget.setItem(0, 2, durationItem) self.queryHistoryTableWidget.resizeColumnsToContents() self.queryHistoryTableWidget.resizeRowsToContents() def writeQueryHistory(self, sql, affectedRows, secs): if len(self.history[self.connectionName]) >= self.QUERY_HISTORY_LIMIT: self.history[self.connectionName].pop(0) settings = QgsSettings() self.history[self.connectionName].append({'query': sql, 'rows': affectedRows, 'secs': secs}) settings.setValue('DB_Manager/queryHistory/' + self.dbType, self.history) self.populateQueryHistory() def getQueryHash(self, name): return 'q%s' % md5(name.encode('utf8')).hexdigest() 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 = str(self.presetName.text()) QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/name', name) QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/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 saveAsFilePreset(self): settings = QgsSettings() lastDir = settings.value('DB_Manager/lastDirSQLFIle', "") query = self.editSql.text() if query == "": return filename, _ = QFileDialog.getSaveFileName( self, self.tr('Save SQL Query'), lastDir, self.tr("SQL File (*.sql *.SQL)")) if filename: if not filename.lower().endswith('.sql'): filename += ".sql" with open(filename, 'w') as f: f.write(query) lastDir = os.path.dirname(filename) settings.setValue('DB_Manager/lastDirSQLFile', lastDir) def loadFilePreset(self): settings = QgsSettings() lastDir = settings.value('DB_Manager/lastDirSQLFIle', "") filename, _ = QFileDialog.getOpenFileName( self, self.tr("Load SQL Query"), lastDir, self.tr("SQL File (*.sql *.SQL);;All Files (*)")) if filename: with open(filename, 'r') as f: self.editSql.clear() for line in f: self.editSql.insertText(line) lastDir = os.path.dirname(filename) settings.setValue('DB_Manager/lastDirSQLFile', lastDir) def deletePreset(self): name = self.presetCombo.currentText() QgsProject.instance().removeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name)) self.presetCombo.removeItem(self.presetCombo.findText(name)) self.presetCombo.setCurrentIndex(-1) def loadPreset(self, name): query = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query')[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 updateUiWhileSqlExecution(self, status): if status: for i in range(0, self.mainWindow.tabs.count()): if i != self.mainWindow.tabs.currentIndex(): self.mainWindow.tabs.setTabEnabled(i, False) self.mainWindow.menuBar.setEnabled(False) self.mainWindow.toolBar.setEnabled(False) self.mainWindow.tree.setEnabled(False) for w in self.findChildren(QWidget): w.setEnabled(False) self.btnCancel.setEnabled(True) self.progressBar.setEnabled(True) self.progressBar.setRange(0, 0) else: for i in range(0, self.mainWindow.tabs.count()): if i != self.mainWindow.tabs.currentIndex(): self.mainWindow.tabs.setTabEnabled(i, True) self.mainWindow.refreshTabs() self.mainWindow.menuBar.setEnabled(True) self.mainWindow.toolBar.setEnabled(True) self.mainWindow.tree.setEnabled(True) for w in self.findChildren(QWidget): w.setEnabled(True) self.btnCancel.setEnabled(False) self.progressBar.setRange(0, 100) self.progressBar.setEnabled(False) def executeSqlCanceled(self): self.btnCancel.setEnabled(False) self.modelAsync.cancel() def executeSqlCompleted(self): self.updateUiWhileSqlExecution(False) with OverrideCursor(Qt.WaitCursor): if self.modelAsync.task.status() == QgsTask.Complete: model = self.modelAsync.model quotedCols = [] self.viewResult.setModel(model) self.lblResult.setText(self.tr("{0} rows, {1:.3f} seconds").format(model.affectedRows(), model.secs())) cols = self.viewResult.model().columnNames() for col in cols: quotedCols.append(self.db.connector.quoteId(col)) self.setColumnCombos(cols, quotedCols) self.writeQueryHistory(self.modelAsync.task.sql, model.affectedRows(), model.secs()) self.update() elif not self.modelAsync.canceled: DlgDbError.showError(self.modelAsync.error, self) self.uniqueModel.clear() self.geomCombo.clear() def executeSql(self): sql = self._getExecutableSqlQuery() if sql == "": return # delete the old model old_model = self.viewResult.model() self.viewResult.setModel(None) if old_model: old_model.deleteLater() try: self.modelAsync = self.db.sqlResultModelAsync(sql, self) self.modelAsync.done.connect(self.executeSqlCompleted) self.updateUiWhileSqlExecution(True) QgsApplication.taskManager().addTask(self.modelAsync.task) except Exception as e: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return 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._getExecutableSqlQuery() if query == "": return None # remove a trailing ';' from query if present if query.strip().endswith(';'): query = query.strip()[:-1] layerType = QgsMapLayerType.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayerType.RasterLayer # get a new layer name names = [] for layer in list(QgsProject.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: e = BaseError(self.tr("There was an error creating the SQL layer, please check the logs for further information.")) DlgDbError.showError(e, self) return None def loadSqlLayer(self): with OverrideCursor(Qt.WaitCursor): layer = self._getSqlLayer(self.filter) if layer is None: return QgsProject.instance().addMapLayers([layer], True) def fillColumnCombos(self): query = self._getExecutableSqlQuery() if query == "": return with OverrideCursor(Qt.WaitCursor): # remove a trailing ';' from query if present if query.strip().endswith(';'): query = query.strip()[:-1] # get all the columns 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" % (str(query), connector.quoteId(alias)) else: sql = u"SELECT * FROM (%s\n) WHERE 1=0" % str(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: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return finally: if c: c.close() del c self.setColumnCombos(cols, quotedCols) def setColumnCombos(self, cols, quotedCols): # get sensible default columns. do this before sorting in case there's hints in the column order (e.g., 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) 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 value in dictionary.values(): 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, self.tr("View Name"), self.tr("View name")) if ok: try: self.db.connector.createSpatialView(name, self._getExecutableSqlQuery()) 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 _getExecutableSqlQuery(self): sql = self._getSqlQuery() uncommented_sql = check_comments_in_sql(sql) return uncommented_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 FKProperty(WIDGET, BASE): """ Editor to create/edit ForeignKey column property """ def __init__(self, parent, relation={}): """ :param parent: Owner of the form :type parent: QWidget :param relation: Dictionary holding fields used to build foreign key column *entity_relation - EntityRelation object, if its None then this is a new column else its an edit *fk_entities - entities used for ForeignKey selection *profile - current profile *entity - current entity you are creating column for. *column_name - name of the column :type form_field: dictionary """ QDialog.__init__(self, parent) self.setupUi(self) self._entity_relation = relation['form_fields']['entity_relation'] self.fk_entities = relation['fk_entities'] self.profile = relation['profile'] self.entity = relation['entity'] self.column_name = relation['column_name'] self.in_db = relation['form_fields']['in_db'] self._show_in_parent = relation['show_in_parent'] self._show_in_child = relation['show_in_child'] self.column_model = QStandardItemModel() self.lvDisplayCol.setModel(self.column_model) self.init_gui() def init_gui(self): """ Initializes form fields """ self.cboPrimaryEntity.currentIndexChanged.connect( \ self.load_entity_columns) self.load_fk_entities() if self._entity_relation: parent = self._entity_relation.parent.short_name parent_column = self._entity_relation.parent_column display_cols = self._entity_relation.display_cols self.cboPrimaryEntity.setCurrentIndex( \ self.cboPrimaryEntity.findText(parent)) self.cboPrimaryUKey.setCurrentIndex( \ self.cboPrimaryUKey.findText(parent_column)) self.show_display_cols(display_cols) # Disable controls if column exists in the database self.cboPrimaryEntity.setEnabled(not self.in_db) self.cboPrimaryUKey.setEnabled(not self.in_db) self.lvDisplayCol.setEnabled(not self.in_db) self.show_in_parent_chk.clicked.connect(self.on_show_in_parent_clicked) self.show_in_child_chk.clicked.connect(self.on_show_in_child_clicked) def on_show_in_parent_clicked(self): """ A slot raised when show in parent is clicked. :return: :rtype: """ if self.show_in_parent_chk.isChecked(): self.show_in_child_chk.setChecked(False) self._show_in_parent = True def on_show_in_child_clicked(self): """ A slot raised when show in child is clicked. :return: :rtype: """ if self.show_in_child_chk.isChecked(): self.show_in_parent_chk.setChecked(False) self._show_in_child = True def show_in_parent(self): """ Returns show in parent. :return: Returns show in parent. :rtype: Boolean """ return self._show_in_parent def show_in_child(self): """ Returns show in child. :return: Returns show in child. :rtype: Boolean """ return self._show_in_child def show_display_cols(self, display_cols): """ checks previously selected display columns """ for row in range(self.column_model.rowCount()): if str(self.column_model.item(row).text()) in display_cols: self.column_model.item(row).setCheckState(Qt.Checked) def load_fk_entities(self): """ populates combobox with entities to select primary entity for the foreign key """ self.cboPrimaryEntity.clear() self.cboPrimaryEntity.insertItems( 0, [name[0] for name in self.fk_entities]) self.cboPrimaryEntity.setCurrentIndex(0) def entity_columns(self): """ returns: A list used to select child entity column when building a foreign key rtype: list """ index = self.cboPrimaryEntity.currentIndex() entity_columns = \ [column for column in self.fk_entities[index][1].columns.items()] column_names = [column[0] for column in entity_columns] return column_names def fk_display_columns(self): """ returns: A list of columns used to select display columns in foreign key rtype: list """ index = self.cboPrimaryEntity.currentIndex() entity_columns = \ [column for column in self.fk_entities[index][1].columns.items()] columns = [column[0] for column in entity_columns \ if column[1].TYPE_INFO != 'SERIAL'] return columns def load_entity_columns(self): """ """ columns = self.entity_columns() self.populate_column_combobox(columns) disp_columns = self.fk_display_columns() self.populate_column_listview(disp_columns) def populate_column_combobox(self, columns): """ Populate combobox with column names param columns: List of entity columns to select your primary unique column for the foreign key type columns: list """ self.cboPrimaryUKey.clear() self.cboPrimaryUKey.insertItems(0, columns) def populate_column_listview(self, columns): """ Populates list view with columns used in selecting display columns for foreign key param columns: A list of column names type columns: list """ self.column_model.clear() for column in columns: item = QStandardItem(column) item.setCheckable(True) self.column_model.appendRow(item) def add_values(self): """ Construct an EntityRelation instance from form fields """ er_fields = {} er_fields['parent'] = str(self.cboPrimaryEntity.currentText()) er_fields['parent_column'] = str(self.cboPrimaryUKey.currentText()) er_fields['display_columns'] = self.display_columns() er_fields['child'] = self.entity er_fields['child_column'] = self.column_name self._entity_relation = EntityRelation(self.profile, **er_fields) def display_columns(self): """ Scans StandardItemModel for display columns, and returns a list of selected/checked columns for display in foreign key rtype: list """ return [str(self.column_model.item(row).text()) \ for row in range(self.column_model.rowCount()) \ if self.column_model.item(row).checkState() == Qt.Checked] def entity_relation(self): """ returns: entity relation instance rtype: EntityRelation """ return self._entity_relation def accept(self): self.add_values() self.done(1) def reject(self): self.done(0)
def __init__(self, iface, db, parent=None): QWidget.__init__(self, parent) self.mainWindow = parent self.iface = iface self.db = db self.dbType = db.connection().typeNameString() self.connectionName = db.connection().connectionName() self.filter = "" self.modelAsync = None 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( self.tr(u"{0} - {1} [{2}]").format(self.windowTitle(), self.connectionName, self.dbType)) self.defaultLayerName = self.tr('QueryLayer') if self.allowMultiColumnPk: self.uniqueColumnCheck.setText(self.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText(self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) self.initCompleter() settings = QgsSettings() self.history = settings.value('DB_Manager/queryHistory/' + self.dbType, {self.connectionName: []}) if self.connectionName not in self.history: self.history[self.connectionName] = [] self.queryHistoryWidget.setVisible(False) self.queryHistoryTableWidget.verticalHeader().hide() self.queryHistoryTableWidget.doubleClicked.connect(self.insertQueryInEditor) self.populateQueryHistory() self.btnQueryHistory.toggled.connect(self.showHideQueryHistory) self.btnCancel.setEnabled(False) self.btnCancel.clicked.connect(self.executeSqlCanceled) self.btnCancel.setShortcut(QKeySequence.Cancel) self.progressBar.setEnabled(False) self.progressBar.setRange(0, 100) self.progressBar.setValue(0) self.progressBar.setFormat("") self.progressBar.setAlignment(Qt.AlignCenter) # 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.presetSaveAsFile.clicked.connect(self.saveAsFilePreset) self.presetLoadFile.clicked.connect(self.loadFilePreset) 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)
class contentAuthDlg(WIDGET, BASE): ''' Content authorization dialog ''' def __init__(self, plugin): QDialog.__init__(self, plugin.iface.mainWindow()) self.setupUi(self) QgsGui.enableAutoGeometryRestore(self) # Initialize the dialog self.initGui() # Load users self.loadContent() # Load Roles self.loadRoles() # Reference to the currently selected STDM content item self.currentContent = None def initGui(self): ''' Initialize GUI properties ''' # Disable any action by the user in the roles view self.lstRoles.setEnabled(False) # Connect signals self.lstContent.activated.connect(self.onContentClicked) self.lstContent.clicked.connect(self.onContentClicked) self.lstRoles.activated.connect(self.onRoleSelected) self.lstRoles.clicked.connect(self.onRoleSelected) def loadContent(self): ''' Loads STDM content items ''' self.content = Content() # self.content=Table('content_base',Base.metadata,autoload=True,autoload_with=STDMDb.instance().engine) cntItems = self.content.queryObject().all() ''' self.content=Table('content_base',Base.metadata,autoload=True,autoload_with=STDMDb.instance().engine) session= STDMDb.instance().session cntItems=session.query(self.content) ''' cnts = [cntItem.name for cntItem in cntItems] self.contentModel = UsersRolesModel(cnts) self.lstContent.setModel(self.contentModel) def loadRoles(self, contentname=""): ''' Loads the roles in the database cluster ''' self.roleProvider = RoleProvider() sysRoles = self.roleProvider.GetAllRoles() roles = [] # Load the corresponding roles for the specified content item cnt = Content() if contentname != "": self.currentContent = self.content.queryObject().filter( Content.name == contentname).first() if self.currentContent: roles = [rl.name for rl in self.currentContent.roles] # Initialize model self.roleMappingsModel = QStandardItemModel(self) self.roleMappingsModel.setColumnCount(1) # Add role items into the standard item model for r in range(len(sysRoles)): role = sysRoles[r] if role.name != "postgres": roleItem = self._createNewRoleItem(role.name) # Check if the db role is in the approved for the current content item roleIndex = getIndex(roles, role.name) if roleIndex != -1: roleItem.setCheckState(Qt.Checked) self.roleMappingsModel.appendRow(roleItem) self.lstRoles.setModel(self.roleMappingsModel) def _createNewRoleItem(self, rolename): ''' Creates a custom role item for use in a QStandardItemModel ''' # Set icon icon = QIcon() icon.addPixmap(GuiUtils.get_icon_pixmap("roles.png"), QIcon.Normal, QIcon.Off) roleItem = QStandardItem(icon, rolename) roleItem.setCheckable(True) roleItem.setCheckState(Qt.Unchecked) return roleItem def onContentClicked(self, index): ''' Slot activated when a content item is selected to load the roles for the specified content items ''' self.lstRoles.setEnabled(True) contentName = index.data() self.loadRoles(contentName) self.privilege_provider = SinglePrivilegeProvider( contentName, current_profile()) def onRoleSelected(self, index): ''' Slot which is called when a user checks/unchecks to add/remove a role for the specified content item. ''' if self.currentContent != None: item = self.roleMappingsModel.itemFromIndex(index) rolename = item.text() self.privilege_provider.role = rolename # Get role object from role name role = Role() rl = role.queryObject().filter(Role.name == rolename).first() self.blockSignals(True) # Add role to the content item if the item is selected or remove if it was previosuly checked if item.checkState() == Qt.Checked: self.currentContent.roles.append(rl) self.privilege_provider.grant_privilege() elif item.checkState() == Qt.Unchecked: self.currentContent.roles.remove(rl) self.privilege_provider.revoke_privilege() self.currentContent.update() self.blockSignals(False)
class DlgSqlWindow(QWidget, Ui_Dialog): nameChanged = pyqtSignal(str) QUERY_HISTORY_LIMIT = 20 def __init__(self, iface, db, parent=None): QWidget.__init__(self, parent) self.mainWindow = parent self.iface = iface self.db = db self.dbType = db.connection().typeNameString() self.connectionName = db.connection().connectionName() self.filter = "" self.modelAsync = None 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( self.tr(u"{0} - {1} [{2}]").format(self.windowTitle(), self.connectionName, self.dbType)) self.defaultLayerName = self.tr('QueryLayer') if self.allowMultiColumnPk: self.uniqueColumnCheck.setText(self.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText(self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) self.initCompleter() settings = QgsSettings() self.history = settings.value('DB_Manager/queryHistory/' + self.dbType, {self.connectionName: []}) if self.connectionName not in self.history: self.history[self.connectionName] = [] self.queryHistoryWidget.setVisible(False) self.queryHistoryTableWidget.verticalHeader().hide() self.queryHistoryTableWidget.doubleClicked.connect(self.insertQueryInEditor) self.populateQueryHistory() self.btnQueryHistory.toggled.connect(self.showHideQueryHistory) self.btnCancel.setEnabled(False) self.btnCancel.clicked.connect(self.executeSqlCanceled) self.btnCancel.setShortcut(QKeySequence.Cancel) self.progressBar.setEnabled(False) self.progressBar.setRange(0, 100) self.progressBar.setValue(0) self.progressBar.setFormat("") self.progressBar.setAlignment(Qt.AlignCenter) # 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.presetSaveAsFile.clicked.connect(self.saveAsFilePreset) self.presetLoadFile.clicked.connect(self.loadFilePreset) 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 insertQueryInEditor(self, item): sql = item.data(Qt.DisplayRole) self.editSql.insertText(sql) def showHideQueryHistory(self, visible): self.queryHistoryWidget.setVisible(visible) def populateQueryHistory(self): self.queryHistoryTableWidget.clearContents() self.queryHistoryTableWidget.setRowCount(0) dictlist = self.history[self.connectionName] if not dictlist: return for i in range(len(dictlist)): self.queryHistoryTableWidget.insertRow(0) queryItem = QTableWidgetItem(dictlist[i]['query']) rowsItem = QTableWidgetItem(str(dictlist[i]['rows'])) durationItem = QTableWidgetItem(str(dictlist[i]['secs'])) self.queryHistoryTableWidget.setItem(0, 0, queryItem) self.queryHistoryTableWidget.setItem(0, 1, rowsItem) self.queryHistoryTableWidget.setItem(0, 2, durationItem) self.queryHistoryTableWidget.resizeColumnsToContents() self.queryHistoryTableWidget.resizeRowsToContents() def writeQueryHistory(self, sql, affectedRows, secs): if len(self.history[self.connectionName]) >= self.QUERY_HISTORY_LIMIT: self.history[self.connectionName].pop(0) settings = QgsSettings() self.history[self.connectionName].append({'query': sql, 'rows': affectedRows, 'secs': secs}) settings.setValue('DB_Manager/queryHistory/' + self.dbType, self.history) self.populateQueryHistory() def getQueryHash(self, name): return 'q%s' % md5(name.encode('utf8')).hexdigest() 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 = str(self.presetName.text()) QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/name', name) QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/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 saveAsFilePreset(self): settings = QgsSettings() lastDir = settings.value('DB_Manager/lastDirSQLFIle', "") query = self._getSqlQuery() if query == "": return filename, _ = QFileDialog.getSaveFileName( self, self.tr('Save SQL Query'), lastDir, self.tr("SQL File (*.sql, *.SQL)")) if filename: if not filename.lower().endswith('.sql'): filename += ".sql" with open(filename, 'w') as f: f.write(query) lastDir = os.path.dirname(filename) settings.setValue('DB_Manager/lastDirSQLFile', lastDir) def loadFilePreset(self): settings = QgsSettings() lastDir = settings.value('DB_Manager/lastDirSQLFIle', "") filename, _ = QFileDialog.getOpenFileName( self, self.tr("Load SQL Query"), lastDir, self.tr("SQL File (*.sql, *.SQL)")) if filename: with open(filename, 'r') as f: self.editSql.clear() for line in f: self.editSql.insertText(line) lastDir = os.path.dirname(filename) settings.setValue('DB_Manager/lastDirSQLFile', lastDir) def deletePreset(self): name = self.presetCombo.currentText() QgsProject.instance().removeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name)) self.presetCombo.removeItem(self.presetCombo.findText(name)) self.presetCombo.setCurrentIndex(-1) def loadPreset(self, name): query = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query')[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 updateUiWhileSqlExecution(self, status): if status: for i in range(0, self.mainWindow.tabs.count()): if i != self.mainWindow.tabs.currentIndex(): self.mainWindow.tabs.setTabEnabled(i, False) self.mainWindow.menuBar.setEnabled(False) self.mainWindow.toolBar.setEnabled(False) self.mainWindow.tree.setEnabled(False) for w in self.findChildren(QWidget): w.setEnabled(False) self.btnCancel.setEnabled(True) self.progressBar.setEnabled(True) self.progressBar.setRange(0, 0) else: for i in range(0, self.mainWindow.tabs.count()): if i != self.mainWindow.tabs.currentIndex(): self.mainWindow.tabs.setTabEnabled(i, True) self.mainWindow.refreshTabs() self.mainWindow.menuBar.setEnabled(True) self.mainWindow.toolBar.setEnabled(True) self.mainWindow.tree.setEnabled(True) for w in self.findChildren(QWidget): w.setEnabled(True) self.btnCancel.setEnabled(False) self.progressBar.setRange(0, 100) self.progressBar.setEnabled(False) def executeSqlCanceled(self): self.btnCancel.setEnabled(False) self.modelAsync.cancel() def executeSqlCompleted(self): self.updateUiWhileSqlExecution(False) with OverrideCursor(Qt.WaitCursor): if self.modelAsync.task.status() == QgsTask.Complete: model = self.modelAsync.model quotedCols = [] self.viewResult.setModel(model) self.lblResult.setText(self.tr("{0} rows, {1:.3f} seconds").format(model.affectedRows(), model.secs())) cols = self.viewResult.model().columnNames() for col in cols: quotedCols.append(self.db.connector.quoteId(col)) self.setColumnCombos(cols, quotedCols) self.writeQueryHistory(self.modelAsync.task.sql, model.affectedRows(), model.secs()) self.update() elif not self.modelAsync.canceled: DlgDbError.showError(self.modelAsync.error, self) self.uniqueModel.clear() self.geomCombo.clear() def executeSql(self): sql = self._getExecutableSqlQuery() if sql == "": return # delete the old model old_model = self.viewResult.model() self.viewResult.setModel(None) if old_model: old_model.deleteLater() try: self.modelAsync = self.db.sqlResultModelAsync(sql, self) self.modelAsync.done.connect(self.executeSqlCompleted) self.updateUiWhileSqlExecution(True) QgsApplication.taskManager().addTask(self.modelAsync.task) except Exception as e: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return 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._getExecutableSqlQuery() if query == "": return None # remove a trailing ';' from query if present if query.strip().endswith(';'): query = query.strip()[:-1] layerType = QgsMapLayerType.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayerType.RasterLayer # get a new layer name names = [] for layer in list(QgsProject.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: e = BaseError(self.tr("There was an error creating the SQL layer, please check the logs for further information.")) DlgDbError.showError(e, self) return None def loadSqlLayer(self): with OverrideCursor(Qt.WaitCursor): layer = self._getSqlLayer(self.filter) if layer is None: return QgsProject.instance().addMapLayers([layer], True) def fillColumnCombos(self): query = self._getExecutableSqlQuery() if query == "": return with OverrideCursor(Qt.WaitCursor): # remove a trailing ';' from query if present if query.strip().endswith(';'): query = query.strip()[:-1] # get all the columns 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" % (str(query), connector.quoteId(alias)) else: sql = u"SELECT * FROM (%s\n) WHERE 1=0" % str(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: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return finally: if c: c.close() del c self.setColumnCombos(cols, quotedCols) def setColumnCombos(self, cols, quotedCols): # get sensible default columns. do this before sorting in case there's hints in the column order (e.g., 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) 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 value in dictionary.values(): 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, self.tr("View Name"), self.tr("View name")) if ok: try: self.db.connector.createSpatialView(name, self._getExecutableSqlQuery()) 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 _getExecutableSqlQuery(self): sql = self._getSqlQuery() # Clean it up! lines = [] for line in sql.split('\n'): if not line.strip().startswith('--'): lines.append(line) sql = ' '.join(lines) return sql.strip() 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 HistoryDialog(QDialog, Ui_HistoryDialogPythonConsole): def __init__(self, parent): QDialog.__init__(self, parent) self.setupUi(self) self.parent = parent self.setWindowTitle( QCoreApplication.translate("PythonConsole", "Python Console - Command History")) self.listView.setToolTip( QCoreApplication.translate("PythonConsole", "Double-click on item to execute")) self.listView.setFont(QgsCodeEditorPython.getMonospaceFont()) self.model = QStandardItemModel(self.listView) self._reloadHistory() self.deleteScut = QShortcut(QKeySequence(Qt.Key_Delete), self) self.deleteScut.activated.connect(self._deleteItem) self.listView.doubleClicked.connect(self._runHistory) self.reloadHistory.clicked.connect(self._reloadHistory) self.saveHistory.clicked.connect(self._saveHistory) self.runHistoryButton.clicked.connect(self._executeSelectedHistory) def _executeSelectedHistory(self): items = self.listView.selectionModel().selectedIndexes() items.sort() for item in items: self.parent.runCommand(item.data(Qt.DisplayRole)) def _runHistory(self, item): cmd = item.data(Qt.DisplayRole) self.parent.runCommand(cmd) def _saveHistory(self): self.parent.writeHistoryFile(True) def _reloadHistory(self): self.model.clear() item = None for i in self.parent.history: item = QStandardItem(i) if sys.platform.startswith('win'): item.setSizeHint(QSize(18, 18)) self.model.appendRow(item) self.listView.setModel(self.model) self.listView.scrollToBottom() if item: self.listView.setCurrentIndex(self.model.indexFromItem(item)) def _deleteItem(self): itemsSelected = self.listView.selectionModel().selectedIndexes() if itemsSelected: item = itemsSelected[0].row() # Remove item from the command history (just for the current session) self.parent.history.pop(item) self.parent.softHistory.pop(item) if item < self.parent.softHistoryIndex: self.parent.softHistoryIndex -= 1 self.parent.setText( self.parent.softHistory[self.parent.softHistoryIndex]) self.parent.move_cursor_to_end() # Remove row from the command history dialog self.model.removeRow(item)
class PdokServicesPlugin(object): def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # docked or dialog, defaults to dialog # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if isinstance(QSettings().value("/pdokservicesplugin/docked"), QVariant): # self.docked = QSettings().value("/pdokservicesplugin/docked", QVariant(False)) # else: # self.docked = QSettings().value("/pdokservicesplugin/docked", False) # # # Create the dialog and keep reference # if "True" == self.docked or "true" == self.docked or True is self.docked: # self.dlg = PdokServicesPluginDockWidget() # self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dlg) # else: # self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDatabaseFilePath()).path() + "/python/plugins/pdokservicesplugin" # initialize locale localePath = "" if isinstance(QSettings().value("locale/userLocale"), QVariant): locale = QSettings().value("locale/userLocale").value()[0:2] else: locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/pdokservicesplugin_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.currentLayer = None self.SETTINGS_SECTION = '/pdokservicesplugin/' self.pointer = None self.pdokgeocoder = PDOKGeoLocator(self.iface) self.geocoderSourceModel = None def getSettingsValue(self, key, default=''): if QSettings().contains(self.SETTINGS_SECTION + key): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 return str(QSettings().value(key).toString()) else: return str(QSettings().value(key)) else: return default def setSettingsValue(self, key, value): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue(key, QVariant(value)) else: QSettings().setValue(key, value) def initGui(self): # Create action that will start plugin configuration self.run_action = QAction(QIcon(":/plugins/pdokservicesplugin/icon.png"), \ u"Pdok Services Plugin", self.iface.mainWindow()) self.servicesLoaded = False # connect the action to the run method # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if "True" == self.docked or "true" == self.docked or True == self.docked: # self.run_action.triggered.connect(self.showAndRaise) # self.dlg.radioDocked.setChecked(True) # # docked the dialog is immidiately visible, so should run NOW # else: # self.run_action.triggered.connect(self.run) # self.dlg.radioDocked.setChecked(False) # self.setupfq() self.run_action.triggered.connect(self.run) #self.dlg.radioDocked.setChecked(False) self.setupfq() # Add toolbar button and menu item #self.iface.addToolBarIcon(self.action) self.toolbar = self.iface.addToolBar("PDOK services plugin") self.toolbar.setObjectName("PDOK services plugin") self.toolbar.addAction(self.run_action) self.toolbarSearch = QLineEdit() self.toolbarSearch.setMaximumWidth(200) self.toolbarSearch.setAlignment(Qt.AlignLeft) self.toolbarSearch.setPlaceholderText("PDOK Locatieserver zoek") self.toolbar.addWidget(self.toolbarSearch) self.toolbarSearch.returnPressed.connect(self.searchAddressFromToolbar) # address/point cleanup self.clean_action = QAction(QIcon(":/plugins/pdokservicesplugin/eraser.png"), \ u"Cleanup", self.eraseAddress()) self.toolbar.addAction(self.clean_action) self.clean_action.triggered.connect(self.eraseAddress) self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.run_action) # about self.aboutAction = QAction(QIcon(":/plugins/pdokservicesplugin/help.png"), \ "About", self.iface.mainWindow()) self.aboutAction.setWhatsThis("Pdok Services Plugin About") self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.aboutAction) self.aboutAction.triggered.connect(self.about) self.dlg.ui.btnLoadLayer.clicked.connect(self.loadService) self.dlg.geocoderSearch.returnPressed.connect(self.searchAddress) self.dlg.geocoderSearch.textEdited.connect(self.searchAddress) self.dlg.geocoderSearch.setPlaceholderText("PDOK Locatieserver zoek, bv postcode of postcode huisnummer") self.dlg.geocoderResultSearch.textChanged.connect(self.filterGeocoderResult) self.dlg.geocoderResultSearch.setPlaceholderText("een of meer zoekwoorden uit resultaat") #self.dlg.radioDocked.toggled.connect(self.set_docked) self.dlg.btnCheckPdokJson.clicked.connect(self.checkPdokJson) #self.iface.mapCanvas().renderStarting.connect(self.extentsChanged) ui = self.dlg.ui cbxs = [ui.cbx_gem, ui.cbx_wpl, ui.cbx_weg, ui.cbx_pcd, ui.cbx_adr, ui.cbx_pcl, ui.cbx_hmp] # connect all fq checkboxes with suggest, so upon a change in fq filter we re-search for cbx in cbxs: cbx.stateChanged.connect(self.searchAddress) self.run(True) # for now hiding the pointer as soon as the extent changes #def extentsChanged(self): # self.removePointer() def checkPdokJson(self): myversion = self.getSettingsValue('pdokversion', '1') msgtxt = '' msglvl = 0 # QgsMessageBar.INFO try: response = urllib.request.urlopen('http://www.qgis.nl/pdok.version') str_response = response.read().decode('utf-8') pdokversion = json.loads(str_response) if pdokversion > int(myversion): response = urllib.request.urlopen('http://www.qgis.nl/pdok.json') str_response = response.read().decode('utf-8') pdokjson = json.loads(str_response) with open(self.plugin_dir +'/pdok.json', 'w') as outfile: json.dump(pdokjson, outfile) msgtxt = "De laatste versie is opgehaald en zal worden gebruikt " + \ str(pdokversion) + ' (was ' + myversion +')' self.servicesLoaded = False # reset reading of json self.run() self.setSettingsValue('pdokversion', pdokversion) else: msgtxt = "Geen nieuwere versie beschikbaar dan " + str(pdokversion) except Exception as e: #print e msgtxt = "Fout bij ophalen van service info. Netwerk probleem?" msglvl = 2 # QgsMessageBar.CRITICAL # msg if hasattr(self.iface, 'messageBar'): self.iface.messageBar().pushMessage("PDOK services update", msgtxt, level=msglvl, duration=10) else: # 1.8 QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin", msgtxt) # def set_docked(self, foo): # self.setSettingsValue('docked', self.dlg.radioDocked.isChecked()) # #if Qgis.QGIS_VERSION_INT < 10900: # # # qgis <= 1.8 # # QSettings().setValue("/pdokservicesplugin/docked", QVariant(self.dlg.radioDocked.isChecked())) # #else: # # QSettings().setValue("/pdokservicesplugin/docked", self.dlg.radioDocked.isChecked()) def showAndRaise(self): self.dlg.show() self.dlg.raise_() # also remove the pointer self.removePointer() def about(self): infoString = "Written by Richard Duivenvoorde\nEmail - [email protected]\n" infoString += "Company - Zuidt - http://www.zuidt.nl\n" infoString += "Source: https://github.com/rduivenvoorde/pdokservicesplugin" QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin About", infoString) def unload(self): self.removePointer() # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Pdok Services Plugin",self.run_action) del self.toolbarSearch def showService(self, selectedIndexes): if len(selectedIndexes)==0: self.currentLayer = None self.dlg.ui.layerInfo.setHtml('') self.dlg.ui.comboSelectProj.clear() return # needed to scroll To the selected row incase of using the keyboard / arrows self.dlg.servicesView.scrollTo(self.dlg.servicesView.selectedIndexes()[0]) # itemType holds the data (== column 1) self.currentLayer = self.dlg.servicesView.selectedIndexes()[1].data(Qt.UserRole) if isinstance(self.currentLayer, QVariant): self.currentLayer = self.currentLayer.toMap() # QGIS 1.8: QVariants currentLayer = {} for key in list(self.currentLayer.keys()): val = self.currentLayer[key] currentLayer[str(key)]=str(val.toString()) self.currentLayer = currentLayer url = self.currentLayer['url'] title = self.currentLayer['title'] style = '' if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' servicetitle = self.currentLayer['servicetitle'] layername = self.currentLayer['layers'] abstract = self.currentLayer['abstract'] stype = self.currentLayer['type'].upper() minscale ='' if 'minscale' in self.currentLayer and self.currentLayer['minscale'] != None and self.currentLayer['minscale'] != '': minscale = "min. schaal 1:"+self.currentLayer['minscale'] maxscale = '' if 'maxscale' in self.currentLayer and self.currentLayer['maxscale'] != None and self.currentLayer['maxscale'] != '': maxscale = "max. schaal 1:"+self.currentLayer['maxscale'] self.dlg.ui.layerInfo.setText('') self.dlg.ui.btnLoadLayer.setEnabled(True) self.dlg.ui.layerInfo.setHtml('<h4>%s</h4><h3>%s</h3><lu><li>%s</li><li> </li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li></lu>' % (servicetitle, title, abstract, stype, url, layername, style, minscale, maxscale)) self.dlg.ui.comboSelectProj.clear() if stype=="WMS": try: crs = self.currentLayer['crs'] except KeyError: crs = 'EPSG:28992' crs = crs.split(',') self.dlg.ui.comboSelectProj.addItems(crs) for i in range(len(crs)): if crs[i] == 'EPSG:28992': self.dlg.ui.comboSelectProj.setCurrentIndex(i) if stype=="WMTS": tilematrixsets = self.currentLayer['tilematrixsets'].split(',') self.dlg.ui.comboSelectProj.addItems(tilematrixsets) for i in range(len(tilematrixsets)): if tilematrixsets[i].startswith('EPSG:28992'): self.dlg.ui.comboSelectProj.setCurrentIndex(i) def loadService(self): if self.currentLayer == None: return servicetype = self.currentLayer['type'] url = self.currentLayer['url'] # some services have an url with query parameters in it, we have to urlencode those: location,query = urllib.parse.splitquery(url) url = location if query != None and query != '': url +=('?'+urllib.parse.quote_plus(query)) title = self.currentLayer['title'] if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' else: style = '' # == default for this service layers = self.currentLayer['layers'] # mmm, tricky: we take the first one while we can actually want png/gif or jpeg if servicetype == "wms": imgformat = self.currentLayer['imgformats'].split(',')[0] if self.dlg.ui.comboSelectProj.currentIndex() == -1: crs = 'EPSG:28992' else: crs = self.dlg.ui.comboSelectProj.currentText() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 uri = url self.iface.addRasterLayer( uri, # service uri title, # name for layer (as seen in QGIS) "wms", # dataprovider key [layers], # array of layername(s) for provider (id's) [""], # array of stylename(s) NOTE: ignoring styles here!!! imgformat, # image format searchstring crs) # crs code searchstring else: # qgis > 1.8 uri = "crs="+crs+"&layers="+layers+"&styles="+style+"&format="+imgformat+"&url="+url; self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wmts": if Qgis.QGIS_VERSION_INT < 10900: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen in deze versie van QGIS.\nMisschien kunt u QGIS 2.0 installeren (die kan het WEL)?\nOf is de laag niet ook beschikbaar als wms of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return if self.dlg.ui.comboSelectProj.currentIndex() == -1: tilematrixset = 'EPSG:28992' else: tilematrixset = self.dlg.ui.comboSelectProj.currentText() imgformat = self.currentLayer['imgformats'].split(',')[0] # special case for luchtfoto #if layers=="luchtfoto": # # tileMatrixSet=nltilingschema&crs=EPSG:28992&layers=luchtfoto&styles=&format=image/jpeg&url=http://geodata1.nationaalgeoregister.nl/luchtfoto/wmts/1.0.0/WMTSCapabilities.xml # # {u'layers': u'luchtfoto', u'imgformats': u'image/jpeg', u'title': u'PDOK-achtergrond luchtfoto', u'url': u'http://geodata1.nationaalgeoregister.nl/luchtfoto/wms', u'abstract': u'', u'tilematrixsets': u'nltilingschema', u'type': u'wmts'} # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url #else: # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url; #uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; if tilematrixset.startswith('EPSG:'): crs=tilematrixset i = crs.find(':', 5) if i > -1: crs=crs[:i] elif tilematrixset.startswith('OGC:1.0'): crs='EPSG:3857' uri = "tileMatrixSet="+tilematrixset+"&crs="+crs+"&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; #print "############ PDOK URI #################" #print uri self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wfs": location, query = urllib.parse.splitquery(url) #uri = location+"?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME="+layers+"&SRSNAME=EPSG:28992" #uri = location + "?SERVICE=WFS&REQUEST=GetFeature&TYPENAME=" + layers + "&SRSNAME=EPSG:28992" # adding a bbox paramater forces QGIS to NOT cache features but retrieve new features all the time # QGIS will update the BBOX to the right value #uri += "&BBOX=-10000,310000,290000,650000" uri = " pagingEnabled='true' restrictToRequestBBOX='1' srsname='EPSG:28992' typename='"+layers+"' url='"+url+"' version='2.0.0' " self.iface.addVectorLayer(uri, title, "WFS") elif servicetype == "wcs": # cache=AlwaysCache&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs uri = '' # cache=AlwaysCache # cache=PreferNetwork # cache=AlwaysNetwork # cache=AlwaysNetwork&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.1&identifier="+layers+"&url="+url # working for ahn1 ahn2 and ahn3: GEOTIFF_FLOAT32 format = 'GEOTIFF_FLOAT32' # working for ahn25m is only image/tiff if layers=='ahn25m': format = 'image/tiff' # we handcrated some wcs layers with 2 different image formats: tiff (RGB) and tiff (float32): if 'imgformats' in self.currentLayer: format = self.currentLayer['imgformats'].split(',')[0] uri = "cache=AlwaysNetwork&crs=EPSG:28992&format="+format+"&version=1.1.2&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.2&identifier=" + layers + "&url=" + url self.iface.addRasterLayer(uri, title, "wcs") else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen door de plugin of door QGIS.\nIs het niet beschikbaar als wms, wmts of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return def filterGeocoderResult(self, string): #print "filtering geocoder results: %s" % string self.dlg.geocoderResultView.selectRow(0) self.geocoderProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.geocoderProxyModel.setFilterFixedString(string) def searchAddressFromToolbar(self): self.removePointer() self.geocoderSourceModel.clear() self.geocode() def searchAddress(self): self.removePointer() #print "search geocoder for: %s" % self.dlg.geocoderSearch.text() self.geocoderSourceModel.clear() #self.geocode(self.dlg.geocoderSearch.text()) self.suggest() def eraseAddress(self): """ clean the input and remove the pointer """ self.removePointer() if self.geocoderSourceModel is not None: self.geocoderSourceModel.clear() if self.dlg.geocoderSearch is not None: self.dlg.geocoderSearch.clear() if self.toolbarSearch is not None: self.toolbarSearch.clear() def filterLayers(self, string): # remove selection if one row is selected self.dlg.servicesView.selectRow(0) #self.currentLayer = None self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setFilterFixedString(string) #def addSourceRow(self, service, layer): def addSourceRow(self, serviceLayer): # you can attache different "data's" to to an QStandarditem # default one is the visible one: itemType = QStandardItem("%s" % (serviceLayer["type"].upper()) ) # userrole is a free form one: # only attach the data to the first item # service layer = a dict/object with all props of the layer itemType.setData( serviceLayer, Qt.UserRole ) itemType.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) # only wms services have styles (sometimes) layername = serviceLayer["title"] if 'style' in serviceLayer: itemLayername = QStandardItem("%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) ) layername = "%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) else: itemLayername = QStandardItem("%s" % (serviceLayer["title"])) itemLayername.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["servicetitle"] )) # itemFilter is the item used to search filter in. That is why layername is a combi of layername + filter here itemFilter = QStandardItem("%s %s %s %s" % (serviceLayer["type"], layername, serviceLayer["servicetitle"], serviceLayer["abstract"]) ) itemServicetitle = QStandardItem("%s" % (serviceLayer["servicetitle"])) itemServicetitle.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) self.sourceModel.appendRow( [ itemLayername, itemType, itemServicetitle, itemFilter ] ) # run method that performs all the real work def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value("/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget(int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(os.path.dirname(__file__), ".", "pdok.json") f = open(pdokjson, 'r', encoding='utf-8') self.pdok = json.load(f) f.close() self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers(QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str) or isinstance(service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText("woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect(self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable(False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel().selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True; self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth(0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer() def setupfq(self): """ Setup the fq checkboxes in the gui, by looking into the settings for the 'pdokservicesplugin/checkedfqs' key, which contains a list of type strings like ['weg','adres'] """ checked_fqs = self.getSettingsValue('checkedfqs', []) #self.info('setup fq: {}'.format(checked_fqs)) if len(checked_fqs) > 0: # else there is not saved state... take gui defaults self.dlg.ui.cbx_gem.setChecked('gemeente' in checked_fqs) self.dlg.ui.cbx_wpl.setChecked('woonplaats' in checked_fqs) self.dlg.ui.cbx_weg.setChecked('weg' in checked_fqs) self.dlg.ui.cbx_pcd.setChecked('postcode' in checked_fqs) self.dlg.ui.cbx_adr.setChecked('adres' in checked_fqs) self.dlg.ui.cbx_pcl.setChecked('perceel' in checked_fqs) self.dlg.ui.cbx_hmp.setChecked('hectometerpaal' in checked_fqs) def createfq(self): """ This creates a fq-string (Filter Query, see https://github.com/PDOK/locatieserver/wiki/Zoekvoorbeelden-Locatieserver) Based on the checkboxes in the dialog. Defaults to '' Example: 'fq=+type:adres+type:gemeente' (only gemeente AND addresses) :return: """ fqlist = [] if self.dlg.ui.cbx_gem.isChecked(): fqlist.append('gemeente') if self.dlg.ui.cbx_wpl.isChecked(): fqlist.append('woonplaats') if self.dlg.ui.cbx_weg.isChecked(): fqlist.append('weg') if self.dlg.ui.cbx_pcd.isChecked(): fqlist.append('postcode') if self.dlg.ui.cbx_adr.isChecked(): fqlist.append('adres') if self.dlg.ui.cbx_pcl.isChecked(): fqlist.append('perceel') if self.dlg.ui.cbx_hmp.isChecked(): fqlist.append('hectometerpaal') self.setSettingsValue('checkedfqs', fqlist) #self.info(self.getSettingsValue('checkedfqs', ['leeg?'])) fq = '' if len(fqlist) > 0: fq = '&fq=+type:' + '+type:'.join(fqlist) return fq def suggest(self): self.dlg.ui.lookupinfo.setHtml('') search_text = self.dlg.geocoderSearch.text() if len(search_text) <= 1: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "meer input aub: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) return # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "zoeken: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) results = self.pdokgeocoder.suggest(search_text, self.createfq()) if len(results) == 0: # ignore, as we are suggesting, maybe more characters will reveal something... return for result in results: #print address adrestekst = QStandardItem("%s" % (result["adrestekst"])) adrestekst.setData(result, Qt.UserRole) type = QStandardItem("%s" % (result["type"])) id = QStandardItem("%s" % (result["id"])) score = QStandardItem("%s" % (result["score"])) adrestekst.setData(result, Qt.UserRole) self.geocoderSourceModel.appendRow([adrestekst, type]) self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) self.dlg.geocoderResultView.resizeColumnsToContents() self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def geocode(self): self.dlg.geocoderSearch.setText(self.toolbarSearch.text()) self.suggest() if self.dlg.geocoderResultView.model().rowCount()>0: self.dlg.geocoderResultView.selectRow(0) self.zoomToAddress() else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ "Niets gevonden.\nProbeer een andere spelling, of alleen postcode/huisnummer?\n\nSelecteer meer (Locatieserver) 'typen' in de PdokServicesPlugin dialoog.\n\nOf gebruik de 'PDOK geocoder'-tab in de PdokServicesPlugin dialoog." ), QMessageBox.Ok, QMessageBox.Ok) # def geocode(self): # self.dlg.ui.lookupinfo.setHtml('') # search_text = self.toolbarSearch.text() # addresses = self.pdokgeocoder.search(search_text) # if len(addresses) == 0: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "Niets gevonden. Probeer een andere spelling of alleen postcode/huisnummer." # ), QMessageBox.Ok, QMessageBox.Ok) # return # for address in addresses: # #print address # adrestekst = QStandardItem("%s" % (address["adrestekst"])) # adrestekst.setData(address, Qt.UserRole) # straat = QStandardItem("%s" % (address["straat"])) # nummer = QStandardItem("%s" % (address["nummer"])) # postcode = QStandardItem("%s" % (address["postcode"])) # plaats = QStandardItem("%s" % (address["plaats"])) # gemeente = QStandardItem("%s" % (address["gemeente"])) # provincie = QStandardItem("%s" % (address["provincie"])) # self.geocoderSourceModel.appendRow([adrestekst, straat, nummer, postcode, plaats, gemeente, provincie]) # # self.dlg.geocoderResultView.selectRow(0) # self.zoomToAddress() # # self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") # self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Straat") # self.geocoderSourceModel.setHeaderData(2, Qt.Horizontal, "Nr") # self.geocoderSourceModel.setHeaderData(3, Qt.Horizontal, "Postcode") # self.geocoderSourceModel.setHeaderData(4, Qt.Horizontal, "Plaats") # self.geocoderSourceModel.setHeaderData(5, Qt.Horizontal, "Gemeente") # self.geocoderSourceModel.setHeaderData(6, Qt.Horizontal, "Provincie") # # self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(3).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(4).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(5).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(6).setTextAlignment(Qt.AlignLeft) # # self.dlg.geocoderResultView.resizeColumnsToContents() # self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def zoomToAddress(self): # get x,y from data of record self.removePointer() data = self.dlg.geocoderResultView.selectedIndexes()[0].data(Qt.UserRole) if 'centroide_rd' in data: # free OR lookup service geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] else: # no centroid yet, probably only object id, retrieve it via lookup service id = data['id'] data = self.pdokgeocoder.lookup(id) geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] lookup_data= data['data'] lis = '' for key in lookup_data.keys(): lis = lis + '<li>{}: {}</li>'.format(key, lookup_data[key]) self.dlg.ui.lookupinfo.setHtml( '<h4>{}</h4><lu>{}</lu>'.format(adrestekst, lis)) # just always transform from 28992 to mapcanvas crs crs = self.iface.mapCanvas().mapSettings().destinationCrs() crs28992 = QgsCoordinateReferenceSystem() crs28992.createFromId(28992) crsTransform = QgsCoordinateTransform(crs28992, crs, QgsProject.instance()) z = 1587 if adrestekst.lower().startswith('adres'): z = 794 elif adrestekst.lower().startswith('perceel'): z = 794 elif adrestekst.lower().startswith('hectometer'): z = 1587 elif adrestekst.lower().startswith('straat'): z = 3175 elif adrestekst.lower().startswith('postcode'): z = 6350 elif adrestekst.lower().startswith('woonplaats'): z = 25398 elif adrestekst.lower().startswith('gemeente'): z = 50797 elif adrestekst.lower().startswith('provincie'): z = 812750 geom.transform(crsTransform) center = geom.asPoint() self.setPointer(center) # zoom to with center is actually setting a point rectangle and then zoom rect = QgsRectangle(center, center) self.iface.mapCanvas().setExtent(rect) self.iface.mapCanvas().zoomScale(z) self.iface.mapCanvas().refresh() def setPointer(self, point): self.removePointer() self.pointer = QgsVertexMarker(self.iface.mapCanvas()) self.pointer.setColor(QColor(255, 255, 0)) self.pointer.setIconSize(10) self.pointer.setPenWidth(5) self.pointer.setCenter(point) def removePointer(self): if self.pointer is not None: self.iface.mapCanvas().scene().removeItem(self.pointer) def info(self, msg=""): QgsMessageLog.logMessage('{}'.format(msg), 'PDOK-services Plugin', Qgis.Info)
def __init__(self, parent=None, iface=None): """Constructor. :param parent: Optional widget to use as parent :type parent: QWidget :param iface: An instance of QGisInterface :type iface: QGisInterface """ super(ResourceSharingDialog, self).__init__(parent) self.setupUi(self) self.iface = iface # Reconfigure UI self.setModal(True) self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) self.button_install.setEnabled(False) self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Set QListWidgetItem # All icon_all = QIcon() icon_all.addFile( resources_path('img', 'plugin.svg'), QSize(), QIcon.Normal, QIcon.Off) item_all = QListWidgetItem() item_all.setIcon(icon_all) item_all.setText(self.tr('All')) # Installed icon_installed = QIcon() icon_installed.addFile( resources_path('img', 'plugin-installed.svg'), QSize(), QIcon.Normal, QIcon.Off) item_installed = QListWidgetItem() item_installed.setIcon(icon_installed) item_installed.setText(self.tr('Installed')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Settings icon_settings = QIcon() icon_settings.addFile( resources_path('img', 'settings.svg'), QSize(), QIcon.Normal, QIcon.Off) item_settings = QListWidgetItem() item_settings.setIcon(icon_settings) item_settings.setText(self.tr('Settings')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Add the list widget item to the widget self.menu_list_widget.addItem(item_all) self.menu_list_widget.addItem(item_installed) self.menu_list_widget.addItem(item_settings) # Init the message bar self.message_bar = QgsMessageBar(self) self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.vlayoutRightColumn.insertWidget(0, self.message_bar) # Progress dialog for any long running process self.progress_dialog = None # Init repository manager self.repository_manager = RepositoryManager() self.collection_manager = CollectionManager() # Collections list view self.collections_model = QStandardItemModel(0, 1) self.collections_model.sort(0, Qt.AscendingOrder) self.collection_proxy = CustomSortFilterProxyModel(self) self.collection_proxy.setSourceModel(self.collections_model) self.list_view_collections.setModel(self.collection_proxy) # Active selected collection self._selected_collection_id = None # Slots self.button_add.clicked.connect(self.add_repository) self.button_edit.clicked.connect(self.edit_repository) self.button_delete.clicked.connect(self.delete_repository) self.button_reload.clicked.connect(self.reload_repositories) self.menu_list_widget.currentRowChanged.connect(self.set_current_tab) self.list_view_collections.selectionModel().currentChanged.connect( self.on_list_view_collections_clicked) self.line_edit_filter.textChanged.connect(self.filter_collections) self.button_install.clicked.connect(self.install_collection) self.button_open.clicked.connect(self.open_collection) self.button_uninstall.clicked.connect(self.uninstall_collection) self.button_box.button(QDialogButtonBox.Help).clicked.connect( self.open_help) # Populate repositories widget and collections list view self.populate_repositories_widget() self.reload_collections_model()
class DlgSqlLayerWindow(QWidget, Ui_Dialog): nameChanged = pyqtSignal(str) def __init__(self, iface, layer, parent=None): QWidget.__init__(self, parent) self.iface = iface self.layer = layer uri = QgsDataSourceUri(layer.source()) dbplugin = None db = None if layer.dataProvider().name() == 'postgres': dbplugin = createDbPlugin('postgis', 'postgres') elif layer.dataProvider().name() == 'spatialite': dbplugin = createDbPlugin('spatialite', 'spatialite') elif layer.dataProvider().name() == 'oracle': dbplugin = createDbPlugin('oracle', 'oracle') elif layer.dataProvider().name() == 'virtual': dbplugin = createDbPlugin('vlayers', 'virtual') elif layer.dataProvider().name() == 'ogr': dbplugin = createDbPlugin('gpkg', 'gpkg') if dbplugin: dbplugin.connectToUri(uri) db = dbplugin.db self.dbplugin = dbplugin 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.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText(self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) 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.editSql.textChanged.connect(self.updatePresetButtonsState) self.presetName.textChanged.connect(self.updatePresetButtonsState) self.presetCombo.currentIndexChanged.connect(self.updatePresetButtonsState) 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 self.layerTypeWidget.hide() # show if load as raster is supported # self.loadLayerBtn.clicked.connect(self.loadSqlLayer) self.updateLayerBtn.clicked.connect(self.updateSqlLayer) self.getColumnsBtn.clicked.connect(self.fillColumnCombos) self.queryBuilderFirst = True self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif")) self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder) self.presetName.textChanged.connect(self.nameChanged) # Update from layer # First the SQL from QgsDataSourceUri table sql = uri.table() if uri.keyColumn() == '_uid_': match = re.search(r'^\(SELECT .+ AS _uid_,\* FROM \((.*)\) AS _subq_.+_\s*\)$', sql, re.S | re.X) if match: sql = match.group(1) else: match = re.search(r'^\((SELECT .+ FROM .+)\)$', sql, re.S | re.X) if match: sql = match.group(1) # Need to check on table() since the parentheses were removed by the regexp if not uri.table().startswith('(') and not uri.table().endswith(')'): schema = uri.schema() if schema and schema.upper() != 'PUBLIC': sql = 'SELECT * FROM {0}.{1}'.format(self.db.connector.quoteId(schema), self.db.connector.quoteId(sql)) else: sql = 'SELECT * FROM {0}'.format(self.db.connector.quoteId(sql)) self.editSql.setText(sql) self.executeSql() # Then the columns self.geomCombo.setCurrentIndex(self.geomCombo.findText(uri.geometryColumn(), Qt.MatchExactly)) if uri.keyColumn() != '_uid_': self.uniqueColumnCheck.setCheckState(Qt.Checked) if self.allowMultiColumnPk: itemsData = uri.keyColumn().split(',') for item in self.uniqueModel.findItems("*", Qt.MatchWildcard): if item.data() in itemsData: item.setCheckState(Qt.Checked) else: keyColumn = uri.keyColumn() if self.uniqueModel.findItems(keyColumn): self.uniqueCombo.setEditText(keyColumn) # Finally layer name, filter and selectAtId self.layerNameEdit.setText(layer.name()) self.filter = uri.sql() if uri.selectAtIdDisabled(): self.avoidSelectById.setCheckState(Qt.Checked) def getQueryHash(self, name): return 'q%s' % md5(name.encode('utf8')).hexdigest() def updatePresetButtonsState(self, *args): """Slot called when the combo box or the sql or the query name have changed: sets store button state""" self.presetStore.setEnabled(bool(self._getSqlQuery() and self.presetName.text())) self.presetDelete.setEnabled(bool(self.presetCombo.currentIndex() != -1)) 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/' + self.getQueryHash(name) + '/name', name) QgsProject.instance().writeEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/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' + self.getQueryHash(name)) self.presetCombo.removeItem(self.presetCombo.findText(name)) self.presetCombo.setCurrentIndex(-1) def loadPreset(self, name): query = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query')[0] name = QgsProject.instance().readEntry('DBManager', 'savedQueries/' + self.getQueryHash(name) + '/name')[0] self.editSql.setText(query) def clearSql(self): self.editSql.clear() self.editSql.setFocus() self.filter = "" def executeSql(self): sql = self._getSqlQuery() if sql == "": return with OverrideCursor(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("{0} rows, {1:.3f} seconds").format(model.affectedRows(), model.secs())) cols = self.viewResult.model().columnNames() for col in cols: quotedCols.append(self.db.connector.quoteId(col)) except BaseError as e: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return self.setColumnCombos(cols, quotedCols) self.update() 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 layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayer.RasterLayer # get a new layer name names = [] for layer in list(QgsProject.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): with OverrideCursor(Qt.WaitCursor): layer = self._getSqlLayer(self.filter) if layer is None: return QgsProject.instance().addMapLayers([layer], True) def updateSqlLayer(self): with OverrideCursor(Qt.WaitCursor): layer = self._getSqlLayer(self.filter) if layer is None: return # self.layer.dataProvider().setDataSourceUri(layer.dataProvider().dataSourceUri()) # self.layer.dataProvider().reloadData() XMLDocument = QDomDocument("style") XMLMapLayers = XMLDocument.createElement("maplayers") XMLMapLayer = XMLDocument.createElement("maplayer") self.layer.writeLayerXml(XMLMapLayer, XMLDocument, QgsReadWriteContext()) XMLMapLayer.firstChildElement("datasource").firstChild().setNodeValue(layer.source()) XMLMapLayers.appendChild(XMLMapLayer) XMLDocument.appendChild(XMLMapLayers) self.layer.readLayerXml(XMLMapLayer, QgsReadWriteContext()) self.layer.reload() self.iface.actionDraw().trigger() self.iface.mapCanvas().refresh() def fillColumnCombos(self): query = self._getSqlQuery() if query == "": return with OverrideCursor(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" % (str(query), connector.quoteId(alias)) else: sql = u"SELECT * FROM (%s\n) WHERE 1=0" % str(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: DlgDbError.showError(e, self) self.uniqueModel.clear() self.geomCombo.clear() return finally: if c: c.close() del c self.setColumnCombos(cols, quotedCols) def setColumnCombos(self, cols, quotedCols): # get sensible default columns. do this before sorting in case there's hints in the column order (e.g., 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 _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 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.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText(self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) 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' + str(name.__hash__()) + '/name', name) QgsProject.instance().writeEntry('DBManager', 'savedQueries/q' + str(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' + str(name.__hash__())) self.presetCombo.removeItem(self.presetCombo.findText(name)) self.presetCombo.setCurrentIndex(-1) def loadPreset(self, name): query = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + str(name.__hash__()) + '/query')[0] name = QgsProject.instance().readEntry('DBManager', 'savedQueries/q' + str(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 layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked() else QgsMapLayer.RasterLayer # get a new layer name names = [] for layer in list(QgsProject.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 QgsProject.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" % (str(query), connector.quoteId(alias)) else: sql = u"SELECT * FROM (%s\n) WHERE 1=0" % str(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 list(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 ConfigDialog(BASE, WIDGET): def __init__(self, showSearch=True): super(ConfigDialog, self).__init__(None) self.setupUi(self) self.groupIcon = QgsApplication.getThemeIcon('mIconFolder.svg') self.model = QStandardItemModel() self.tree.setModel(self.model) self.delegate = SettingDelegate() self.tree.setItemDelegateForColumn(1, self.delegate) if showSearch: if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText( QApplication.translate('ConfigDialog', 'Search…')) self.searchBox.textChanged.connect(self.textChanged) else: self.searchBox.hide() self.fillTree() self.saveMenus = False self.tree.expanded.connect(self.itemExpanded) self.auto_adjust_columns = True def textChanged(self, text=None): if text is not None: text = str(text.lower()) else: text = str(self.searchBox.text().lower()) found = self._filterItem(self.model.invisibleRootItem(), text) self.auto_adjust_columns = False if text: self.tree.expandAll() else: self.tree.collapseAll() self.adjustColumns() self.auto_adjust_columns = True if text: return found else: self.tree.collapseAll() return False def _filterItem(self, item, text, forceShow=False): if item.hasChildren(): show = forceShow or isinstance( item, QStandardItem) and bool(text) and (text in item.text().lower()) for i in range(item.rowCount()): child = item.child(i) show = self._filterItem(child, text, forceShow) or show self.tree.setRowHidden(item.row(), item.index().parent(), not show) return show elif isinstance(item, QStandardItem): show = forceShow or bool(text) and (text in item.text().lower()) self.tree.setRowHidden(item.row(), item.index().parent(), not show) return show def fillTree(self): self.fillTreeUsingProviders() def fillTreeUsingProviders(self): self.items = {} self.model.clear() self.model.setHorizontalHeaderLabels( [self.tr('Setting'), self.tr('Value')]) settings = ProcessingConfig.getSettings() rootItem = self.model.invisibleRootItem() """ Filter 'General', 'Models' and 'Scripts' items """ priorityKeys = [ self.tr('General'), self.tr('Models'), self.tr('Scripts') ] for group in priorityKeys: groupItem = QStandardItem(group) icon = ProcessingConfig.getGroupIcon(group) groupItem.setIcon(icon) groupItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [groupItem, emptyItem]) if not group in settings: continue # add menu item only if it has any search matches for setting in settings[group]: if setting.hidden or setting.name.startswith("MENU_"): continue labelItem = QStandardItem(setting.description) labelItem.setIcon(icon) labelItem.setEditable(False) self.items[setting] = SettingItem(setting) groupItem.insertRow(0, [labelItem, self.items[setting]]) """ Filter 'Providers' items """ providersItem = QStandardItem(self.tr('Providers')) icon = QgsApplication.getThemeIcon("/processingAlgorithm.svg") providersItem.setIcon(icon) providersItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [providersItem, emptyItem]) for group in list(settings.keys()): if group in priorityKeys or group == menusSettingsGroup: continue groupItem = QStandardItem(group) icon = ProcessingConfig.getGroupIcon(group) groupItem.setIcon(icon) groupItem.setEditable(False) for setting in settings[group]: if setting.hidden: continue labelItem = QStandardItem(setting.description) labelItem.setIcon(icon) labelItem.setEditable(False) self.items[setting] = SettingItem(setting) groupItem.insertRow(0, [labelItem, self.items[setting]]) emptyItem = QStandardItem() emptyItem.setEditable(False) providersItem.appendRow([groupItem, emptyItem]) """ Filter 'Menus' items """ self.menusItem = QStandardItem(self.tr('Menus')) icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png')) self.menusItem.setIcon(icon) self.menusItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) rootItem.insertRow(0, [self.menusItem, emptyItem]) button = QPushButton(self.tr('Reset to defaults')) button.clicked.connect(self.resetMenusToDefaults) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(button) layout.addStretch() widget = QWidget() widget.setLayout(layout) self.tree.setIndexWidget(emptyItem.index(), widget) for provider in QgsApplication.processingRegistry().providers(): providerDescription = provider.name() groupItem = QStandardItem(providerDescription) icon = provider.icon() groupItem.setIcon(icon) groupItem.setEditable(False) for alg in provider.algorithms(): algItem = QStandardItem(alg.displayName()) algItem.setIcon(icon) algItem.setEditable(False) try: settingMenu = ProcessingConfig.settings["MENU_" + alg.id()] settingButton = ProcessingConfig.settings["BUTTON_" + alg.id()] settingIcon = ProcessingConfig.settings["ICON_" + alg.id()] except: continue self.items[settingMenu] = SettingItem(settingMenu) self.items[settingButton] = SettingItem(settingButton) self.items[settingIcon] = SettingItem(settingIcon) menuLabelItem = QStandardItem("Menu path") menuLabelItem.setEditable(False) buttonLabelItem = QStandardItem("Add button in toolbar") buttonLabelItem.setEditable(False) iconLabelItem = QStandardItem("Icon") iconLabelItem.setEditable(False) emptyItem = QStandardItem() emptyItem.setEditable(False) algItem.insertRow(0, [menuLabelItem, self.items[settingMenu]]) algItem.insertRow(0, [buttonLabelItem, self.items[settingButton]]) algItem.insertRow(0, [iconLabelItem, self.items[settingIcon]]) groupItem.insertRow(0, [algItem, emptyItem]) emptyItem = QStandardItem() emptyItem.setEditable(False) self.menusItem.appendRow([groupItem, emptyItem]) self.tree.sortByColumn(0, Qt.AscendingOrder) self.adjustColumns() def resetMenusToDefaults(self): for provider in QgsApplication.processingRegistry().providers(): for alg in provider.algorithms(): d = defaultMenuEntries.get(alg.id(), "") setting = ProcessingConfig.settings["MENU_" + alg.id()] item = self.items[setting] item.setData(d, Qt.EditRole) self.saveMenus = True def accept(self): qsettings = QgsSettings() for setting in list(self.items.keys()): if setting.group != menusSettingsGroup or self.saveMenus: if isinstance(setting.value, bool): setting.setValue( self.items[setting].checkState() == Qt.Checked) else: try: setting.setValue(str(self.items[setting].text())) except ValueError as e: QMessageBox.warning( self, self.tr('Wrong value'), self.tr('Wrong value for parameter "{0}":\n\n{1}'). format(setting.description, str(e))) return setting.save(qsettings) with OverrideCursor(Qt.WaitCursor): for p in QgsApplication.processingRegistry().providers(): p.refreshAlgorithms() settingsWatcher.settingsChanged.emit() def itemExpanded(self, idx): if idx == self.menusItem.index(): self.saveMenus = True if self.auto_adjust_columns: self.adjustColumns() def adjustColumns(self): self.tree.resizeColumnToContents(0) self.tree.resizeColumnToContents(1)
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.tr("Column(s) with unique values")) else: self.uniqueColumnCheck.setText(self.tr("Column with unique values")) self.editSql.setFocus() self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.editSql.setMarginVisible(True) 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 __init__(self, iface, parent=None): """Constructor.""" self.iface = iface self.plugin_dir = dirname(__file__) self.THINGREYSCALE = self.tr('ThinGreyscale') self.BROWSE = self.tr('Browse') self.CANCEL = self.tr('Cancel') self.CLOSE = self.tr('Close') self.HELP = self.tr('Help') self.OK = self.tr('OK') self.DEFAULTPROVIDER = 'GTiff' self.DEFAULTEXTENSION = '.tif' self.EXTRAEXTENSION = ' *.tiff' super(ThinGreyscaleDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.showInfo("Connecting UI components") okButton = self.button_box.button(QDialogButtonBox.Ok) okButton.setText(self.OK) cancelButton = self.button_box.button(QDialogButtonBox.Cancel) cancelButton.setText(self.CANCEL) cancelButton.setEnabled(False) closeButton = self.button_box.button(QDialogButtonBox.Close) closeButton.setText(self.CLOSE) browseButton = self.browseButton browseButton.setText(self.BROWSE) self.calcHistPushButton.setEnabled(False) self.listModel = QStandardItemModel(self.levelsListView) self.levelsListView.setModel(self.listModel) self.levelsListView.sizeHintForColumn(20) #self.levelValuesCheckBox.setEnabled(False) # Help button helpButton = self.helpButton helpButton.setText(self.HELP) # Connect signals self.showInfo("Connecting signals") okButton.clicked.connect(self.startWorker) cancelButton.clicked.connect(self.killWorker) closeButton.clicked.connect(self.reject) helpButton.clicked.connect(self.help) browseButton.clicked.connect(self.browse) inpIndexCh = self.inputRaster.currentIndexChanged['QString'] inpIndexCh.connect(self.layerchanged) bandCh = self.bandComboBox.currentIndexChanged['QString'] bandCh.connect(self.bandChanged) #self.iface.legendInterface().itemAdded.connect( # self.layerlistchanged) #self.iface.legendInterface().itemRemoved.connect( # self.layerlistchanged) #QObject.disconnect(self.button_box, SIGNAL("rejected()"), self.reject) self.button_box.rejected.disconnect(self.reject) calchistPr = self.calcHistPushButton.clicked calchistPr.connect(self.calculateHistogram) sugglevPr = self.suggestlevelsPushButton.clicked sugglevPr.connect(self.suggestLevels) addlevPr = self.addlevelPushButton.clicked addlevPr.connect(self.addLevel) dellevPr = self.deletelevelsPushButton.clicked dellevPr.connect(self.removeLevel) maxvalCh = self.maxValueSpinBox.valueChanged maxvalCh.connect(self.minmaxvalueChanged) maxvalFi = self.maxValueSpinBox.editingFinished maxvalFi.connect(self.minmaxvalueEdFinished) minvalCh = self.minValueSpinBox.valueChanged minvalCh.connect(self.minmaxvalueChanged) minvalFi = self.minValueSpinBox.editingFinished minvalFi.connect(self.minmaxvalueEdFinished) # Set instance variables #self.mem_layer = None self.worker = None self.inputlayerid = None self.inputlayer = None self.layerlistchanging = False self.minvalue = 1 self.inputrasterprovider = None self.histobins = 50 self.setupScene = QGraphicsScene(self) self.histoGraphicsView.setScene(self.setupScene) # Is the layer band of an integer type self.intband = False self.histogramAvailable = False self.histo = None self.histopadding = 1
class MultipleSelectTreeView(QListView): """ Custom QListView implementation that displays checkable items from a multiple select column type. """ def __init__(self, column, parent=None): """ Class constructor. :param column: Multiple select column object. :type column: MultipleSelectColumn :param parent: Parent widget for the control. :type parent: QWidget """ QListView.__init__(self, parent) # Disable editing of lookup values self.setEditTriggers(QAbstractItemView.NoEditTriggers) self.column = column self._item_model = QStandardItemModel(self) self._value_list = self.column.value_list # Stores lookup objects based on primary keys self._lookup_cache = {} self._initialize() self._association = self.column.association self._first_parent_col = self._association.first_reference_column.name self._second_parent_col = self._association.second_reference_column.name # Association model self._assoc_cls = entity_model(self._association) def reset_model(self): """ Resets the item model. """ self._item_model.clear() self._item_model.setColumnCount(2) def clear(self): """ Clears all items in the model. """ self._item_model.clear() @property def association(self): """ :return: Returns the association object corresponding to the column. :rtype: AssociationEntity """ return self._association @property def value_list(self): """ :return: Returns the ValueList object corresponding to the configured column object. :rtype: ValueList """ return self._value_list @property def item_model(self): """ :return: Returns the model corresponding to the checkable items. :rtype: QStandardItemModel """ return self._item_model def _add_item(self, id, value): """ Adds a row corresponding to id and corresponding value from a lookup table. :param id: Primary key of a lookup record. :type id: int :param value: Lookup value :type value: str """ value_item = QStandardItem(value) value_item.setCheckable(True) id_item = QStandardItem(str(id)) self._item_model.appendRow([value_item, id_item]) def _initialize(self): # Populate list with lookup items self.reset_model() # Add all lookup values in the value list table vl_cls = entity_model(self._value_list) if not vl_cls is None: vl_obj = vl_cls() res = vl_obj.queryObject().all() for r in res: self._lookup_cache[r.id] = r self._add_item(r.id, r.value) self.setModel(self._item_model) def clear_selection(self): """ Unchecks all items in the view. """ for i in range(self._item_model.rowCount()): value_item = self._item_model.item(i, 0) if value_item.checkState() == Qt.Checked: value_item.setCheckState(Qt.Unchecked) if value_item.rowCount() > 0: value_item.removeRow(0) def selection(self): """ :return: Returns a list of selected items. :rtype: list """ selection = [] for i in range(self._item_model.rowCount()): value_item = self._item_model.item(i, 0) if value_item.checkState() == Qt.Checked: id_item = self._item_model.item(i, 1) id = int(id_item.text()) # Get item from the lookup cache and append to selection if id in self._lookup_cache: lookup_rec = self._lookup_cache[id] selection.append(lookup_rec) return selection def set_selection(self, models): """ Checks items corresponding to the specified models. :param models: List containing model values in the view for selection. :type models: list """ for m in models: search_value = m.value v_items = self._item_model.findItems(search_value) # Loop through result and check items for vi in v_items: if vi.checkState() == Qt.Unchecked: vi.setCheckState(Qt.Checked)
class ResourceSharingDialog(QDialog, FORM_CLASS): TAB_ALL = 0 TAB_INSTALLED = 1 TAB_SETTINGS = 2 def __init__(self, parent=None, iface=None): """Constructor. :param parent: Optional widget to use as parent :type parent: QWidget :param iface: An instance of QGisInterface :type iface: QGisInterface """ super(ResourceSharingDialog, self).__init__(parent) self.setupUi(self) self.iface = iface # Reconfigure UI self.setModal(True) self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) self.button_install.setEnabled(False) self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Set QListWidgetItem # All icon_all = QIcon() icon_all.addFile( resources_path('img', 'plugin.svg'), QSize(), QIcon.Normal, QIcon.Off) item_all = QListWidgetItem() item_all.setIcon(icon_all) item_all.setText(self.tr('All')) # Installed icon_installed = QIcon() icon_installed.addFile( resources_path('img', 'plugin-installed.svg'), QSize(), QIcon.Normal, QIcon.Off) item_installed = QListWidgetItem() item_installed.setIcon(icon_installed) item_installed.setText(self.tr('Installed')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Settings icon_settings = QIcon() icon_settings.addFile( resources_path('img', 'settings.svg'), QSize(), QIcon.Normal, QIcon.Off) item_settings = QListWidgetItem() item_settings.setIcon(icon_settings) item_settings.setText(self.tr('Settings')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Add the list widget item to the widget self.menu_list_widget.addItem(item_all) self.menu_list_widget.addItem(item_installed) self.menu_list_widget.addItem(item_settings) # Init the message bar self.message_bar = QgsMessageBar(self) self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.vlayoutRightColumn.insertWidget(0, self.message_bar) # Progress dialog for any long running process self.progress_dialog = None # Init repository manager self.repository_manager = RepositoryManager() self.collection_manager = CollectionManager() # Collections list view self.collections_model = QStandardItemModel(0, 1) self.collections_model.sort(0, Qt.AscendingOrder) self.collection_proxy = CustomSortFilterProxyModel(self) self.collection_proxy.setSourceModel(self.collections_model) self.list_view_collections.setModel(self.collection_proxy) # Active selected collection self._selected_collection_id = None # Slots self.button_add.clicked.connect(self.add_repository) self.button_edit.clicked.connect(self.edit_repository) self.button_delete.clicked.connect(self.delete_repository) self.button_reload.clicked.connect(self.reload_repositories) self.menu_list_widget.currentRowChanged.connect(self.set_current_tab) self.list_view_collections.selectionModel().currentChanged.connect( self.on_list_view_collections_clicked) self.line_edit_filter.textChanged.connect(self.filter_collections) self.button_install.clicked.connect(self.install_collection) self.button_open.clicked.connect(self.open_collection) self.button_uninstall.clicked.connect(self.uninstall_collection) self.button_box.button(QDialogButtonBox.Help).clicked.connect( self.open_help) # Populate repositories widget and collections list view self.populate_repositories_widget() self.reload_collections_model() def set_current_tab(self, index): """Set stacked widget based on active tab. :param index: The index of the active list widget item. :type index: int """ # Clear message bar first self.message_bar.clearWidgets() if index == (self.menu_list_widget.count() - 1): # Switch to settings tab self.stacked_menu_widget.setCurrentIndex(1) else: # Switch to plugins tab if index == 1: # Installed self.collection_proxy.accepted_status = \ COLLECTION_INSTALLED_STATUS # Set the web view title = self.tr('Installed Collections') description = self.tr( 'On the left you see the list of all collections ' 'installed on your QGIS') else: # All self.collection_proxy.accepted_status = COLLECTION_ALL_STATUS # Set the web view title = self.tr('All Collections') description = self.tr( 'On the left you see the list of all collections ' 'available from the repositories registered in the ' 'settings.') context = { 'resources_path': resources_path(), 'title': title, 'description': description } self.web_view_details.setHtml( render_template('tab_description.html', context)) self.stacked_menu_widget.setCurrentIndex(0) def add_repository(self): """Open add repository dialog.""" dlg = ManageRepositoryDialog(self) if not dlg.exec_(): return for repo in self.repository_manager.directories.values(): if dlg.line_edit_url.text().strip() == repo['url']: self.message_bar.pushMessage( self.tr( 'Unable to add another repository with the same URL!'), Qgis.Critical, 5) return repo_name = dlg.line_edit_name.text() repo_url = dlg.line_edit_url.text().strip() repo_auth_cfg = dlg.line_edit_auth_id.text().strip() if repo_name in self.repository_manager.directories: repo_name += '(2)' # Show progress dialog self.show_progress_dialog("Fetching repository's metadata") # Add repository try: status, description = self.repository_manager.add_directory( repo_name, repo_url, repo_auth_cfg) if status: self.message_bar.pushMessage( self.tr( 'Repository is successfully added'), Qgis.Success, 5) else: self.message_bar.pushMessage( self.tr( 'Unable to add repository: %s') % description, Qgis.Critical, 5) except Exception as e: self.message_bar.pushMessage( self.tr('%s') % e, Qgis.Critical, 5) finally: self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() # Deactivate edit and delete button self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def edit_repository(self): """Open edit repository dialog.""" selected_item = self.tree_repositories.currentItem() if selected_item: repo_name = selected_item.text(0) if not repo_name: return # Check if it's the approved online dir repository settings = QSettings() settings.beginGroup(repo_settings_group()) if settings.value(repo_name + '/url') in \ self.repository_manager._online_directories.values(): self.message_bar.pushMessage( self.tr( 'You can not edit the official repositories!'), Qgis.Warning, 5) return dlg = ManageRepositoryDialog(self) dlg.line_edit_name.setText(repo_name) dlg.line_edit_url.setText( self.repository_manager.directories[repo_name]['url']) dlg.line_edit_auth_id.setText( self.repository_manager.directories[repo_name]['auth_cfg']) if not dlg.exec_(): return # Check if the changed URL is already there in the repo new_url = dlg.line_edit_url.text().strip() old_url = self.repository_manager.directories[repo_name]['url'] for repo in self.repository_manager.directories.values(): if new_url == repo['url'] and (old_url != new_url): self.message_bar.pushMessage( self.tr('Unable to add another repository with the same ' 'URL!'), Qgis.Critical, 5) return new_name = dlg.line_edit_name.text() if (new_name in self.repository_manager.directories) and ( new_name != repo_name): new_name += '(2)' new_auth_cfg = dlg.line_edit_auth_id.text() # Show progress dialog self.show_progress_dialog("Fetching repository's metadata") # Edit repository try: status, description = self.repository_manager.edit_directory( repo_name, new_name, old_url, new_url, new_auth_cfg ) if status: self.message_bar.pushMessage( self.tr('Repository is successfully updated'), Qgis.Success, 5) else: self.message_bar.pushMessage( self.tr('Unable to add repository: %s') % description, Qgis.Critical, 5) except Exception as e: self.message_bar.pushMessage( self.tr('%s') % e, Qgis.Critical, 5) finally: self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() # Deactivate edit and delete button self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def delete_repository(self): """Delete a repository in the tree widget.""" selected_item = self.tree_repositories.currentItem() if selected_item: repo_name = selected_item.text(0) if not repo_name: return # Check if it's the approved online dir repository repo_url = self.repository_manager.directories[repo_name]['url'] if repo_url in self.repository_manager._online_directories.values(): self.message_bar.pushMessage( self.tr( 'You can not remove the official repositories!'), Qgis.Warning, 5) return warning = self.tr('Are you sure you want to remove the following ' 'repository?') + '\n' + repo_name if QMessageBox.warning( self, self.tr('QGIS Resource Sharing'), warning, QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return # Remove repository installed_collections = \ self.collection_manager.get_installed_collections(repo_url) if installed_collections: message = ('You have some installed collections from this ' 'repository. Please uninstall them first!') self.message_bar.pushMessage(message, Qgis.Warning, 5) else: self.repository_manager.remove_directory(repo_name) # Reload data and widget self.reload_data_and_widget() # Deactivate edit and delete button self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def reload_repositories(self): """Slot for when user clicks reload repositories button.""" # Show progress dialog self.show_progress_dialog('Reloading all repositories') for repo_name in self.repository_manager.directories: directory = self.repository_manager.directories[repo_name] url = directory['url'] auth_cfg = directory['auth_cfg'] try: status, description = self.repository_manager.reload_directory( repo_name, url, auth_cfg) if status: self.message_bar.pushMessage( self.tr( 'Repository %s is successfully reloaded') % repo_name, Qgis.Info, 5) else: self.message_bar.pushMessage( self.tr( 'Unable to reload %s: %s') % ( repo_name, description), Qgis.Critical, 5) except Exception as e: self.message_bar.pushMessage( self.tr('%s') % e, Qgis.Critical, 5) self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() def install_collection(self): """Slot for when user clicks download button.""" self.show_progress_dialog('Starting installation process...') self.progress_dialog.canceled.connect(self.install_canceled) self.installer_thread = QThread() self.installer_worker = CollectionInstaller( self.collection_manager, self._selected_collection_id) self.installer_worker.moveToThread(self.installer_thread) self.installer_worker.finished.connect(self.install_finished) self.installer_worker.aborted.connect(self.install_aborted) self.installer_worker.progress.connect(self.install_progress) self.installer_thread.started.connect(self.installer_worker.run) self.installer_thread.start() def install_finished(self): # Process the result self.progress_dialog.hide() if self.installer_worker.install_status: self.reload_collections_model() message = '%s is installed successfully' % ( config.COLLECTIONS[self._selected_collection_id]['name']) else: message = self.installer_worker.error_message QMessageBox.information(self, 'Resource Sharing', message) # Clean up the worker and thread self.installer_worker.deleteLater() self.installer_thread.quit() self.installer_thread.wait() self.installer_thread.deleteLater() def install_canceled(self): self.progress_dialog.hide() self.show_progress_dialog('Cancelling installation...') self.installer_worker.abort() def install_aborted(self): if self.installer_thread.isRunning(): self.installer_thread.quit() self.installer_thread.finished.connect(self.progress_dialog.hide) def install_progress(self, text): self.progress_dialog.setLabelText(text) def uninstall_collection(self): """Slot called when user clicks uninstall button.""" try: self.collection_manager.uninstall(self._selected_collection_id) except Exception as e: raise self.reload_collections_model() QMessageBox.information( self, 'Resource Sharing', 'The collection is uninstalled succesfully!') def open_collection(self): """Slot for when user clicks 'Open' button.""" collection_path = local_collection_path(self._selected_collection_id) directory_url = QUrl.fromLocalFile(collection_path) QDesktopServices.openUrl(directory_url) def reload_data_and_widget(self): """Reload repositories and collections and update widgets related.""" self.reload_repositories_widget() self.reload_collections_model() def reload_repositories_widget(self): """Refresh tree repositories using new repositories data.""" self.repository_manager.load_directories() self.populate_repositories_widget() def populate_repositories_widget(self): """Populate the current dictionary repositories to the tree widget.""" # Clear the current tree widget self.tree_repositories.clear() # Export the updated ones from the repository manager for repo_name in self.repository_manager.directories: url = self.repository_manager.directories[repo_name]['url'] item = QTreeWidgetItem(self.tree_repositories) item.setText(0, repo_name) item.setText(1, url) self.tree_repositories.resizeColumnToContents(0) self.tree_repositories.resizeColumnToContents(1) self.tree_repositories.sortItems(1, Qt.AscendingOrder) def reload_collections_model(self): """Reload the collections model with the current collections.""" self.collections_model.clear() for id in config.COLLECTIONS: collection_name = config.COLLECTIONS[id]['name'] collection_author = config.COLLECTIONS[id]['author'] collection_tags = config.COLLECTIONS[id]['tags'] collection_description = config.COLLECTIONS[id]['description'] collection_status = config.COLLECTIONS[id]['status'] item = QStandardItem(collection_name) item.setEditable(False) item.setData(id, COLLECTION_ID_ROLE) item.setData(collection_name, COLLECTION_NAME_ROLE) item.setData(collection_description, COLLECTION_DESCRIPTION_ROLE) item.setData(collection_author, COLLECTION_AUTHOR_ROLE) item.setData(collection_tags, COLLECTION_TAGS_ROLE) item.setData(collection_status, COLLECTION_STATUS_ROLE) self.collections_model.appendRow(item) self.collections_model.sort(0, Qt.AscendingOrder) def on_tree_repositories_itemSelectionChanged(self): """Slot for when the itemSelectionChanged signal emitted.""" # Activate edit and delete button self.button_edit.setEnabled(True) self.button_delete.setEnabled(True) def on_list_view_collections_clicked(self, index): """Slot for when the list_view_collections is clicked.""" real_index = self.collection_proxy.mapToSource(index) if real_index.row() != -1: collection_item = self.collections_model.itemFromIndex(real_index) collection_id = collection_item.data(COLLECTION_ID_ROLE) self._selected_collection_id = collection_id # Enable/disable button status = config.COLLECTIONS[self._selected_collection_id]['status'] is_installed = status == COLLECTION_INSTALLED_STATUS if is_installed: self.button_install.setEnabled(True) self.button_install.setText('Reinstall') self.button_open.setEnabled(True) self.button_uninstall.setEnabled(True) else: self.button_install.setEnabled(True) self.button_install.setText('Install') self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Show metadata self.show_collection_metadata(collection_id) @pyqtSlot(str) def filter_collections(self, text): search = QRegExp( text, Qt.CaseInsensitive, QRegExp.RegExp) self.collection_proxy.setFilterRegExp(search) def show_collection_metadata(self, id): """Show the collection metadata given the id.""" html = self.collection_manager.get_html(id) self.web_view_details.setHtml(html) def reject(self): """Slot when the dialog is closed.""" # Serialize collections to settings self.repository_manager.serialize_repositories() self.done(0) def open_help(self): """Open help.""" doc_url = QUrl('http://www.akbargumbira.com/qgis_resources_sharing') QDesktopServices.openUrl(doc_url) def show_progress_dialog(self, text): """Show infinite progress dialog with given text. :param text: Text as the label of the progress dialog :type text: str """ if self.progress_dialog is None: self.progress_dialog = QProgressDialog(self) self.progress_dialog.setWindowModality(Qt.WindowModal) self.progress_dialog.setAutoClose(False) title = self.tr('Resource Sharing') self.progress_dialog.setWindowTitle(title) # Just use infinite progress bar here self.progress_dialog.setMaximum(0) self.progress_dialog.setMinimum(0) self.progress_dialog.setValue(0) self.progress_dialog.setLabelText(text) self.progress_dialog.show()