def _select_tags(self): """Select tags for the new plugin from the tags dialog""" tag_dialog = SelectTagsDialog() # if the user has their own taglist, use it user_tag_list = os.path.join(os.path.expanduser("~"), '.plugin_tags.txt') if os.path.exists(user_tag_list): tag_file = user_tag_list else: tag_file = os.path.join(str(self.plugin_builder_path), 'taglist.txt') with open(tag_file) as tf: tags = tf.readlines() model = QStandardItemModel() for tag in tags: item = QStandardItem(tag[:-1]) model.appendRow(item) tag_dialog.listView.setModel(model) tag_dialog.show() ok = tag_dialog.exec_() if ok: selected = tag_dialog.listView.selectedIndexes() seltags = [] for tag in selected: seltags.append(tag.data()) taglist = ", ".join(seltags) self.dialog.tags.setText(taglist)
def populateList(self): model = QStandardItemModel() for option in self.options: item = QStandardItem(option) model.appendRow(item) self.lstLayers.setModel(model)
def populateList(self): model = QStandardItemModel() for option in self.selectedoptions: item = QStandardItem(option) model.appendRow(item) self.lstLayers.setModel(model)
def populate_trv(self, trv_widget, result, expand=False): model = QStandardItemModel() trv_widget.setModel(model) trv_widget.setUniformRowHeights(False) main_parent = QStandardItem('{}'.format('Giswater')) font = main_parent.font() font.setPointSize(8) main_parent.setFont(font) self.icon_folder = self.plugin_dir + os.sep + 'icons' path_icon_blue = self.icon_folder + os.sep + '36.png' path_icon_red = self.icon_folder + os.sep + '100.png' if os.path.exists(path_icon_blue): icon = QIcon(path_icon_blue) main_parent.setIcon(icon) for group, functions in result['fields'].items(): parent1 = QStandardItem( f'{group} [{len(functions)} Giswater algorithm]') self.no_clickable_items.append( f'{group} [{len(functions)} Giswater algorithm]') functions.sort(key=self.sort_list, reverse=False) for function in functions: func_name = QStandardItem(str(function['functionname'])) label = QStandardItem(str(function['alias'])) font = label.font() font.setPointSize(8) label.setFont(font) row = self.controller.check_function(function['functionname']) if not row: if os.path.exists(path_icon_red): icon = QIcon(path_icon_red) label.setIcon(icon) label.setForeground(QColor(255, 0, 0)) msg = f"Function {function['functionname']}" \ f" configured on the table config_toolbox, but not found in the database" label.setToolTip(msg) self.no_clickable_items.append(str(function['alias'])) else: if os.path.exists(path_icon_blue): icon = QIcon(path_icon_blue) label.setIcon(icon) label.setToolTip(function['functionname']) enable_run = QStandardItem("True") if function['input_params'] is not None: if 'btnRunEnabled' in function['input_params']: bool_dict = {True: "True", False: "False"} enable_run = QStandardItem(bool_dict[ function['input_params']['btnRunEnabled']]) parent1.appendRow([label, func_name, enable_run]) main_parent.appendRow(parent1) model.appendRow(main_parent) index = model.indexFromItem(main_parent) trv_widget.expand(index) if expand: trv_widget.expandAll()
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 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 build_list_positionable_class(self): model = QStandardItemModel() for obj in self.data.getClassName(): item = QStandardItem(obj) item.setCheckState(Qt.Checked) item.setCheckable(True) model.appendRow(item) model.itemChanged.connect(self.on_positionable_class_list_changed) self.dlg.positionableClass.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 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)
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.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 CheckableComboBox: """Basic QCombobox with selectable items.""" def __init__(self, combobox, select_all=None): """Constructor.""" self.combo = combobox self.combo.setEditable(True) self.combo.lineEdit().setReadOnly(True) self.model = QStandardItemModel(self.combo) self.combo.setModel(self.model) self.combo.setItemDelegate(QStyledItemDelegate()) self.model.itemChanged.connect(self.combo_changed) self.combo.lineEdit().textChanged.connect(self.text_changed) if select_all: self.select_all = select_all self.select_all.clicked.connect(self.select_all_clicked) def select_all_clicked(self): for item in self.model.findItems("*", Qt.MatchWildcard): item.setCheckState(Qt.Checked) def append_row(self, item: QStandardItem): """Add an item to the combobox.""" item.setEnabled(True) item.setCheckable(True) item.setSelectable(False) self.model.appendRow(item) def combo_changed(self): """Slot when the combo has changed.""" self.text_changed(None) def selected_items(self) -> list: checked_items = [] for item in self.model.findItems("*", Qt.MatchWildcard): if item.checkState() == Qt.Checked: checked_items.append(item.data()) return checked_items def set_selected_items(self, items): for item in self.model.findItems("*", Qt.MatchWildcard): checked = item.data() in items item.setCheckState(Qt.Checked if checked else Qt.Unchecked) def text_changed(self, text): """Update the preview with all selected items, separated by a comma.""" label = ", ".join(self.selected_items()) if text != label: self.combo.setEditText(label)
def change_positionable_class(self, state): model = QStandardItemModel() for className in self.data.getClassName(): self.data.setSelected(className, state) item = QStandardItem(className) item.setCheckable(True) if state: self.data.setIds(className, "all") item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) model.appendRow(item) model.itemChanged.connect(self.on_positionable_class_list_changed) self.dlg.positionableClass.setModel(model)
def build_list_positionable(self): model = QStandardItemModel() lu = 25 / len(self.loadedPositionable) completed = 75 for pos in self.loadedPositionable: name = Utils.build_row_name_positionable(pos) item = QStandardItem(name) item.setCheckable(True) item.setCheckState(Qt.Checked) model.appendRow(item) completed = completed + lu self.dlg.progressBar.setValue(completed) model.itemChanged.connect(self.on_positionable_list_changed) self.dlg.positionable.setModel(model) self.dlg.progressBar.setValue(0)
def change_positionable(self, state): model = QStandardItemModel() for className in self.data.getClassName(): for id in self.data.getIds(className): self.data.setIdValue(className, id, state) for pos in self.loadedPositionable: name = Utils.build_row_name_positionable(pos) item = QStandardItem(name) item.setCheckable(True) if state: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) model.appendRow(item) model.itemChanged.connect(self.on_positionable_list_changed) self.dlg.positionable.setModel(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 build_list_attribute(self): model = QStandardItemModel() for attr in self.data.getAttributes(self.currentPositionableClass): item = QStandardItem(attr) if attr in self.alwaysSelectedAttribute: item.setCheckable(False) item.setCheckState(Qt.Checked) else: item.setCheckable(True) if self.data.getAttributes( self.currentPositionableClass)[attr]: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) model.appendRow(item) model.itemChanged.connect(self.on_attribute_list_changed) self.dlg.attribute.setModel(model)
class HTMLFieldSelectionDialog(QDialog, HTML_FIELDS_CLASS): def __init__(self, iface, feat): super(HTMLFieldSelectionDialog, self).__init__(iface.mainWindow()) self.setupUi(self) self.iface = iface self.feat = feat self.selected = [] self.selectAllButton.clicked.connect(self.selectAll) self.clearButton.clicked.connect(self.clearAll) self.checkBox.stateChanged.connect(self.initModel) self.initModel() def initModel(self): self.model = QStandardItemModel(self.listView) state = self.checkBox.isChecked() for key in list(self.feat.keys()): if state == False or self.feat[key] > 0: item = QStandardItem() item.setText(key) item.setCheckable(True) item.setSelectable(False) self.model.appendRow(item) self.listView.setModel(self.model) self.listView.show() def selectAll(self): cnt = self.model.rowCount() for i in range(0, cnt): item = self.model.item(i) item.setCheckState(Qt.Checked) def clearAll(self): cnt = self.model.rowCount() for i in range(0, cnt): item = self.model.item(i) item.setCheckState(Qt.Unchecked) def accept(self): self.selected = [] cnt = self.model.rowCount() for i in range(0, cnt): item = self.model.item(i) if item.checkState() == Qt.Checked: self.selected.append(item.text()) self.close()
class StartWidget(uicls_start_page, basecls_start_page): """Widget for the Start page.""" def __init__(self, parent_page): super().__init__() self.setupUi(self) self.parent_page = parent_page self.tv_revisions_model = QStandardItemModel() self.revisions_tv.setModel(self.tv_revisions_model) self.current_local_schematisation = self.parent_page.parent_wizard.current_local_schematisation self.schematisation = self.parent_page.parent_wizard.schematisation self.schematisation_sqlite = self.parent_page.parent_wizard.schematisation_sqlite self.available_revisions = self.parent_page.parent_wizard.available_revisions self.latest_revision = self.parent_page.parent_wizard.latest_revision organisation = self.parent_page.parent_wizard.plugin_dock.organisations[ self.schematisation.owner] wip_revision = self.current_local_schematisation.wip_revision self.lbl_schematisation.setText( f"{self.schematisation.name} ({organisation.name})") self.lbl_model_dir.setText(wip_revision.schematisation_dir) self.lbl_revision_number.setText(str(wip_revision.number)) self.populate_available_revisions() def populate_available_revisions(self): self.tv_revisions_model.clear() header = [ "Revision number", "Committed by", "Commit date", "Commit message" ] self.tv_revisions_model.setHorizontalHeaderLabels(header) for revision in sorted(self.available_revisions, key=attrgetter("number"), reverse=True): number_item = QStandardItem(str(revision.number)) commit_user_item = QStandardItem(revision.commit_user or "") commit_date = revision.commit_date.strftime( "%d-%m-%Y") if revision.commit_date else "" commit_date_item = QStandardItem(commit_date) commit_message_item = QStandardItem(revision.commit_message or "") self.tv_revisions_model.appendRow([ number_item, commit_user_item, commit_date_item, commit_message_item ]) for i in range(len(header)): self.revisions_tv.resizeColumnToContents(i)
def on_positionable_click(self, item): model = self.dlg.positionable.model() rowName = model.itemFromIndex(item) id = str(rowName.text()).split(' - ')[-1] selected = None for pos in self.loadedPositionable: if pos["_id"] == id: selected = pos break if selected is None: model.blockSignals(False) return model = QStandardItemModel() listStandardItem = [] for s in selected: Utils.complete_model_from_positionable(s, selected[s], listStandardItem) for row in listStandardItem: model.appendRow(row) self.dlg.detail.setModel(model)
def comboboxselecionado(self): #saco de aqui variables que estan en las cajitas global index_campo_seleccionado index_campo_seleccionado = self.dlg.mycomboBox.currentIndex() #para rellenar el texto de los puntos que tenemos layer = iface.activeLayer() features = layer.getFeatures() #idx2 = layer.fields().indexFromName("name") idx2 = index_campo_seleccionado todoslosnombres = [] todaslascuadriculas = [] for feature in features: print("feature") attrs = feature.attributes() # attrs is a list. It contains all the attribute values of this feature nombre = attrs[idx2] cuadric = nombre[:4] todoslosnombres.append(nombre) todaslascuadriculas.append(cuadric) frecuencia = [] for w in todaslascuadriculas: frecuencia.append(todaslascuadriculas.count(w)) print("Lista\n" + str(todaslascuadriculas) + "\n") print("Frecuencias\n" + str(frecuencia) + "\n") pares = list(zip(todaslascuadriculas, frecuencia)) final = [] for elemento in pares: if elemento not in final: final.append(elemento) print(final) model = QStandardItemModel() self.dlg.mylistview.setModel(model) for i in final: item = QStandardItem(str(i)) model.appendRow(item)
class TreeViewLogger(object): """Class with methods for handling messages using list view.""" def __init__(self, tree_view=None, header=None): self.tree_view = tree_view self.header = header self.model = QStandardItemModel() self.tree_view.setModel(self.model) self.levels_colors = { LogLevels.INFO.value: QColor(Qt.black), LogLevels.WARNING.value: QColor(229, 144, 80), LogLevels.ERROR.value: QColor(Qt.red), } self.initialize_view() def clear(self): """Clear list view model.""" self.tree_view.model().clear() def initialize_view(self): """Clear list view model and set header columns if available.""" self.tree_view.model().clear() if self.header: self.tree_view.model().setHorizontalHeaderLabels(self.header) def log_result_row(self, row, log_level): """Show row data with proper log level styling.""" text_color = self.levels_colors[log_level] if self.tree_view is not None: items = [] for value in row: item = QStandardItem(str(value)) item.setForeground(QBrush(text_color)) items.append(item) self.model.appendRow(items) for i in range(len(self.header)): self.tree_view.resizeColumnToContents(i) else: print(row)
class ListViewLogger(object): """Class with methods for handling messages using list view.""" def __init__(self, list_view=None): self.list_view = list_view self.model = QStandardItemModel() self.list_view.setModel(self.model) def clear(self): """Clear list view model.""" self.list_view.model().clear() def log_info(self, msg, log_text_color=QColor(Qt.darkGreen)): """Showing info message bar.""" if self.list_view is not None: item = QStandardItem(msg) item.setForeground(QBrush(log_text_color)) self.model.appendRow([item]) else: print(msg) def log_warn(self, msg, log_text_color=QColor(Qt.darkYellow)): """Showing warning message bar.""" if self.list_view is not None: item = QStandardItem(msg) item.setForeground(QBrush(log_text_color)) self.model.appendRow([item]) else: print(msg) def log_error(self, msg, log_text_color=QColor(Qt.red)): """Showing error message bar.""" if self.list_view is not None: item = QStandardItem(msg) item.setForeground(QBrush(log_text_color)) self.model.appendRow([item]) else: print(msg)
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)
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)
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)
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 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 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 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()
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
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)
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 ModelAtrributesView(QListView): """ Custom QListView implementation that displays checkable model attributes. """ def __init__(self, parent=None, dataModel=None): QListView.__init__(self, parent) self._dataModel = dataModel self._selectedDisplayMapping = OrderedDict() self._modelDisplayMapping = OrderedDict() self._attrModel = QStandardItemModel(self) def dataModel(self): """ Returns the data model instance. """ return self._dataModel def setDataModel(self, dataModel): """ Sets the data model. Should be a callable class rather than the class. instance. """ if callable(dataModel): self._dataModel = dataModel else: self._dataModel = dataModel.__class__ def modelDisplayMapping(self): """ Returns the column name and display name collection. """ return self._modelDisplayMapping def setModelDisplayMapping(self, dataMapping): """ Sets the mapping dictionary for the table object """ if dataMapping != None: self._modelDisplayMapping = dataMapping def load(self, sort=False): """ Load the model's attributes into the list view. """ if self._dataModel == None: return try: self._loadAttrs(self._dataModel.displayMapping(), sort) except AttributeError: # Ignore error if model does not contain # the displayMapping static method pass def load_mapping(self, mapping, sort=False): """ Load collection containing column name and corresponding display name. """ self._modelDisplayMapping = mapping self._loadAttrs(mapping, sort) def sort(self): """ Sorts display name in ascending order. """ self._attrModel.sort(0) def _loadAttrs(self, attrMapping, sort=False): """ Loads display mapping into the list view. Specify to sort display names in ascending order once items have been added to the model. """ self._attrModel.clear() self._attrModel.setColumnCount(2) for attrName, displayName in attrMapping.items(): # Exclude row ID in the list, other unique identifier attributes in the model can be used if attrName != "id": displayNameItem = QStandardItem(displayName) displayNameItem.setCheckable(True) attrNameItem = QStandardItem(attrName) self._attrModel.appendRow([displayNameItem, attrNameItem]) self.setModel(self._attrModel) if sort: self._attrModel.sort(0) def selectedMappings(self): """ Return a dictionary of field names and their corresponding display values. """ selectedAttrs = OrderedDict() for i in range(self._attrModel.rowCount()): displayNameItem = self._attrModel.item(i, 0) if displayNameItem.checkState() == Qt.Checked: attrNameItem = self._attrModel.item(i, 1) selectedAttrs[attrNameItem.text()] = displayNameItem.text() return selectedAttrs
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 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()
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 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)