class LoginSTDialog(QDialog, DIALOG_UI): active_role_changed = pyqtSignal() def __init__(self, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.session = STSession() self.logger = Logger() self.help_strings = HelpStrings() self.should_emit_role_changed = False self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.login) self.buttonBox.helpRequested.connect(self.show_help) self.txt_login_user.setFocus() self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def login(self): if not self.txt_login_user.text().strip( ) or not self.txt_login_password.text().strip(): msg = QCoreApplication.translate( "LoginSTDialog", "First enter user and password data.") self.show_message(msg, Qgis.Warning) return msg = self.logger.status( QCoreApplication.translate("LoginSTDialog", "Connecting to login service...")) with ProcessWithStatus(msg): res, msg, change_role = self.session.login( self.txt_login_user.text(), self.txt_login_password.text()) if res: self.should_emit_role_changed = change_role self.logger.info(__name__, msg, EnumLogHandler.MESSAGE_BAR, 15) self.close() else: self.show_message(msg, Qgis.Warning, 0) def show_message(self, message, level, duration=15): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, duration) def show_help(self): show_plugin_help('transitional_system_login') def reject(self): if self.should_emit_role_changed: self.logger.info(__name__, "Emit active_role_changed.") self.active_role_changed.emit() self.logger.info(__name__, "Dialog closed.") self.done(QDialog.Accepted) # Any code, we don't use it anyways
class LoginSTDialog(QDialog, DIALOG_UI): def __init__(self, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.session = STSession() self.logger = Logger() self.help_strings = HelpStrings() #self.txt_help_page.setHtml(self.help_strings.DLG_WELCOME_SCREEN) #self.txt_help_page.anchorClicked.connect(self.save_template) self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.login) self.buttonBox.helpRequested.connect(self.show_help) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def login(self): if not self.txt_login_user.text().strip( ) or not self.txt_login_password.text().strip(): msg = QCoreApplication.translate( "LoginSTDialog", "First enter user and password data.") self.show_message(msg, Qgis.Warning) return msg = self.logger.status( QCoreApplication.translate("LoginSTDialog", "Connecting to login service...")) with ProcessWithStatus(msg): res, msg = self.session.login(self.txt_login_user.text(), self.txt_login_password.text()) if res: self.logger.info(__name__, msg, LogHandlerEnum.MESSAGE_BAR, 15) self.close() else: self.show_message(msg, Qgis.Warning, 0) def show_message(self, message, level, duration=15): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, duration) def show_help(self): self.qgis_utils.show_help("import_from_excel")
class STCancelTaskDialog(QDialog, DIALOG_TRANSITION_SYSTEM_UI): def __init__(self, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.comments = '' self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.validate_and_accept) self.buttonBox.helpRequested.connect(self.show_help) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def validate_and_accept(self): self.bar.clearWidgets() if not self.txt_comments.toPlainText(): res = False res_msg = QCoreApplication.translate( "STUploadFileDialog", "Comments are required to cancel a task.") self.show_message(res_msg, Qgis.Success if res else Qgis.Warning) return # Do not close the dialog self.comments = self.txt_comments.toPlainText() QDialog.accept(self) def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 0) def show_help(self): # self.qgis_utils.show_help("settings") pass
class geopunt4QgisPoidialog(QDialog): def __init__(self, iface): QDialog.__init__(self, None) self.setWindowFlags( self.windowFlags() & ~Qt.WindowContextHelpButtonHint ) #self.setWindowFlags( self.windowFlags() |Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QSettings().value("locale/userLocale", "nl") if not locale: locale == 'nl' else: locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): 'Set up the user interface from Designer.' self.ui = Ui_geopunt4QgisPoiDlg() self.ui.setupUi(self) #get settings self.s = QSettings() self.loadSettings() #setup geopunt and geometryHelper objects self.gh = geometryHelper(self.iface) self.ph = poiHelper( self.iface) #create the graphicsLayer self.graphicsLayer = [] #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed ) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton( QPushButton("Sluiten"), QDialogButtonBox.RejectRole ) for btn in self.ui.buttonBox.buttons(): btn.setAutoDefault(0) #table ui self.ui.resultLijst.hideColumn(0) # filters self.firstShow = True self.poiTypes = {} self.poiCategories = {} self.poiThemes = {} #actions self.ui.resultLijst.addAction( self.ui.actionZoomtoSelection ) self.ui.actionZoomtoSelection.triggered.connect( self.onZoomSelClicked) self.ui.resultLijst.addAction( self.ui.actionAddTSeltoMap ) self.ui.actionAddTSeltoMap.triggered.connect( self.onAddSelClicked) #event handlers self.ui.zoekKnop.clicked.connect(self.onZoekActivated) self.ui.zoomSelKnop.clicked.connect(self.onZoomSelClicked) self.ui.resultLijst.itemDoubleClicked.connect(self.onZoomSelClicked ) self.ui.resultLijst.itemSelectionChanged.connect(self.onSelectionChanged) self.ui.addToMapKnop.clicked.connect(self.onAddSelClicked) self.ui.addMinModelBtn.clicked.connect( self.addMinModel ) self.ui.poiText.textChanged.connect( self.searchTxtChanged ) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean ) def loadSettings(self): self.saveToFile = int( self.s.value("geopunt4qgis/poiSavetoFile" , 1)) layerName = self.s.value("geopunt4qgis/poilayerText", "") if layerName: self.layerName= layerName self.timeout = int( self.s.value("geopunt4qgis/timeout" ,15)) if settings().proxyUrl: self.proxy = settings().proxyUrl else: self.proxy = "" self.startDir = self.s.value("geopunt4qgis/startDir", os.path.expanduser("~") ) self.poi = Poi(self.timeout, self.proxy) def show(self): QDialog.show(self) self.setWindowModality(0) if self.firstShow: inet = internet_on(proxyUrl=self.proxy, timeout=self.timeout) #filters if inet: self.poiThemes = dict( self.poi.listPoiThemes() ) poiThemes = [""] + list(self.poiThemes.keys()) poiThemes.sort() self.ui.filterPoiThemeCombo.addItems( poiThemes ) self.poiCategories = dict( self.poi.listPoiCategories() ) poiCategories = [""] + list(self.poiCategories.keys()) poiCategories.sort() self.ui.filterPoiCategoryCombo.addItems( poiCategories ) self.poiTypes = dict( self.poi.listPoitypes() ) poiTypes = [""] + list(self.poiTypes.keys()) poiTypes.sort() self.ui.filterPoiTypeCombo.addItems( poiTypes ) gemeentes = json.load( open(os.path.join(os.path.dirname(__file__), "data/gemeentenVL.json")) ) self.NIScodes= { n["Naam"] : n["Niscode"] for n in gemeentes } gemeenteNamen = [n["Naam"] for n in gemeentes] gemeenteNamen.sort() self.ui.filterPoiNIS.addItems( gemeenteNamen ) #connect when inet on self.ui.filterPoiThemeCombo.activated.connect(self.onThemeFilterChange) self.ui.filterPoiCategoryCombo.activated.connect(self.onCategorieFilterChange) self.firstShow = False else: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), QCoreApplication.translate("geopunt4QgisPoidialog", "Kan geen verbing maken met het internet."), level=Qgis.Warning, duration=3) def openHelp(self): webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/poi") def onZoekActivated(self): txt = self.ui.poiText.text() self.ui.resultLijst.clearContents() self.ui.resultLijst.setRowCount(0) self.ui.msgLbl.setText("") ##filters: poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poitheme = self.poiThemes[ poithemeText ] else: poitheme = "" poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "": poiCategorie = self.poiCategories[ poiCategorieText ] else: poiCategorie = "" poiTypeText = self.ui.filterPoiTypeCombo.currentText() if poiTypeText!= "": poiType = self.poiTypes[ poiTypeText ] else: poiType = "" NISText= self.ui.filterPoiNIS.currentText() if NISText != "" and not self.ui.currentBoundsVink.isChecked(): Niscode = self.NIScodes[NISText] else: Niscode = "" if self.ui.currentBoundsVink.isChecked(): bbox = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs([bbox.xMinimum(),bbox.yMinimum()], 4326) maxX, maxY = self.gh.prjPtFromMapCrs([bbox.xMaximum(),bbox.yMaximum()], 4326) xyBox = [minX, minY, maxX, maxY] self.poi.fetchPoi( txt, c=32, srs=4326 , maxModel=True, updateResults=True, bbox=xyBox, theme=poitheme , category=poiCategorie, POItype=poiType ) else: self.poi.fetchPoi( txt, c=32, srs=4326 , maxModel=True, updateResults=True, bbox=None, theme=poitheme , category=poiCategorie, POItype=poiType, region=Niscode ) suggesties = self.poi.poiSuggestion() if type(suggesties) is list: #prevent sorting every time an item is added self.ui.resultLijst.setSortingEnabled(False) row =0 for sug in suggesties: id = QTableWidgetItem( sug[0], 0 ) theme = QTableWidgetItem( sug[1], 0 ) categorie = QTableWidgetItem( sug[2], 0 ) PoiType = QTableWidgetItem( sug[3], 0 ) naam = QTableWidgetItem( sug[4], 0 ) straat = QTableWidgetItem( sug[5], 0) huisnr = QTableWidgetItem( sug[6], 0) busnr = QTableWidgetItem( sug[7], 0) postcode = QTableWidgetItem( sug[8], 0) gemeente = QTableWidgetItem( sug[9], 0) self.ui.resultLijst.insertRow(row) self.ui.resultLijst.setItem(row, 0, id) self.ui.resultLijst.setItem(row, 1, theme) self.ui.resultLijst.setItem(row, 2, categorie) self.ui.resultLijst.setItem(row, 3, PoiType) self.ui.resultLijst.setItem(row, 4, naam) self.ui.resultLijst.setItem(row, 5, straat) self.ui.resultLijst.setItem(row, 6, huisnr) self.ui.resultLijst.setItem(row, 7, busnr) self.ui.resultLijst.setItem(row, 8, postcode) self.ui.resultLijst.setItem(row, 9, gemeente) row += 1 self.ui.resultLijst.setSortingEnabled(True) if self.poi.resultCount > 0: self.ui.msgLbl.setText(QCoreApplication.translate("geopunt4QgisPoidialog", "Aantal getoond: %s gevonden: %s" % ( self.ui.resultLijst.rowCount() , self.poi.resultCount ) )) elif self.poi.resultCount == 0: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Geen resultaten gevonden voor deze zoekopdracht"), "", level=Qgis.Info, duration=10) elif self.poi.resultCount < 0: self.bar.pushMessage(QCoreApplication.translate("geopunt4QgisPoidialog", "Het aantal gevonden kon niet worden bepaald, te complexe zoekopdracht"), "", level=Qgis.Info, duration=10) self.ui.msgLbl.setText(QCoreApplication.translate("geopunt4QgisPoidialog", "Aantal getoond: %s, aantal gevonden niet bepaald" % self.ui.resultLijst.rowCount() ) ) elif type( suggesties ) is str: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog","Waarschuwing"), suggesties, level=Qgis.Warning) else: self.bar.pushMessage("Error", QCoreApplication.translate("geopunt4QgisPoidialog","onbekende fout"), level=Qgis.Critical) def onZoomSelClicked(self): if not len( self.ui.resultLijst.selectedIndexes() ): self.bar.pushMessage("", QCoreApplication.translate("geopunt4QgisPoidialog", "Er zijn geen records geselecteerd"), level=Qgis.Warning ) return selPois = self._getSelectedPois() if len(selPois) <= 0 : self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Merk op"), QCoreApplication.translate("geopunt4QgisPoidialog", "Er niets om naar te zoomen"), level=Qgis.Info, duration=3) elif len(selPois) >= 2: pts = [n['location']['points'][0]['Point']['coordinates'] for n in selPois ] bounds = self.gh.getBoundsOfPointArray(pts) self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326) elif len(selPois) == 1: x, y = selPois[0]['location']['points'][0]['Point']['coordinates'] bounds = self.gh.getBoundsOfPoint(x,y) self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326) def onSelectionChanged(self): selPois = self._getSelectedPois() canvas = self.iface.mapCanvas() self.clearGraphicsLayer() pts = [ self.gh.prjPtToMapCrs( n['location']['points'][0]['Point']['coordinates'], 4326) for n in selPois ] for pt in pts: x,y = pt m = QgsVertexMarker(canvas) self.graphicsLayer.append(m) m.setCenter(QgsPointXY(x,y)) m.setColor(QColor(255,255,0)) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(10) def onAddSelClicked(self): if not len( self.ui.resultLijst.selectedIndexes() ): self.bar.pushMessage("", QCoreApplication.translate("geopunt4QgisPoidialog", "Er zijn geen records geselecteerd"), level=Qgis.Warning ) return if not self.layernameValid(): return self.clearGraphicsLayer() pts = self._getSelectedPois() self.ph.save_pois_points( pts , layername=self.layerName, startFolder= os.path.join(self.startDir, self.layerName), saveToFile=self.saveToFile, sender=self ) def onThemeFilterChange(self): poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poithemeID = self.poiThemes[ poithemeText ] poiCategories = [""] + [n[0] for n in self.poi.listPoiCategories(poithemeID)] poiCategories.sort() self.ui.filterPoiCategoryCombo.clear() self.ui.filterPoiTypeCombo.clear() self.ui.filterPoiCategoryCombo.addItems( poiCategories ) else: self.ui.filterPoiCategoryCombo.addItems([""] + list(self.poiCategories.keys())) self.ui.filterPoiTypeCombo.addItems([""] + list(self.poiTypes.keys())) def onCategorieFilterChange(self): poithemeText = self.ui.filterPoiThemeCombo.currentText() poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "" and poithemeText != "": poiCategorieID = self.poiCategories[ poiCategorieText ] poithemeID = self.poiThemes[ poithemeText ] poiTypes = [""] + [n[0] for n in self.poi.listPoitypes(poithemeID, poiCategorieID)] poiTypes.sort() self.ui.filterPoiTypeCombo.clear() self.ui.filterPoiTypeCombo.addItems( poiTypes ) def addMinModel(self): if not self.layernameValid(): return self.clearGraphicsLayer() txt = self.ui.poiText.text() poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poitheme = self.poiThemes[ poithemeText ] else: poitheme = "" poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "": poiCategorie = self.poiCategories[ poiCategorieText ] else: poiCategorie = "" poiTypeText = self.ui.filterPoiTypeCombo.currentText() if poiTypeText!= "": poiType = self.poiTypes[ poiTypeText ] else: poiType = "" NISText= self.ui.filterPoiNIS.currentText() if NISText != "" and not self.ui.currentBoundsVink.isChecked(): Niscode = self.NIScodes[NISText] else: Niscode = "" cluster = self.ui.clusterCheck.isChecked() if self.ui.currentBoundsVink.isChecked(): bbox = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs([bbox.xMinimum(),bbox.yMinimum()], 4326) maxX, maxY = self.gh.prjPtFromMapCrs([bbox.xMaximum(),bbox.yMaximum()], 4326) xyBox = [minX, minY, maxX, maxY] pts= self.poi.fetchPoi( txt, c=1024, srs=4326 , maxModel=0, updateResults=0, bbox= xyBox, theme= poitheme , category= poiCategorie, POItype= poiType, clustering= cluster ) else: pts= self.poi.fetchPoi( txt, c=1024, srs=4326 , maxModel=0, updateResults=0, bbox=None, theme= poitheme , category= poiCategorie, POItype= poiType, region= Niscode, clustering= cluster ) if type( pts ) == str: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog","Waarschuwing"), pts, level=Qgis.Warning, duration=5) elif type( pts ) == list or type( pts ) == dict: self.ph.save_minPois_points(pts, layername=self.layerName, startFolder= os.path.join(self.startDir, self.layerName), saveToFile=self.saveToFile, sender=self ) self.close() def searchTxtChanged(self): txt= self.ui.poiText.text() if txt != "": msg = QCoreApplication.translate("geopunt4QgisPoidialog", "Voeg meer punten toe") else: msg = QCoreApplication.translate("geopunt4QgisPoidialog", "Voeg alle punten toe" ) self.ui.addMinModelBtn.setText(msg) def _getSelectedPois(self): pois = self.poi.PoiResult selPois = [] selRows = set( [sel.row() for sel in self.ui.resultLijst.selectedIndexes()] ) for row in selRows: itemID = self.ui.resultLijst.item(row,0).text() selPois += [i for i in pois if i["id"] == itemID] return selPois def clearGraphicsLayer(self): for graphic in self.graphicsLayer: self.iface.mapCanvas().scene().removeItem(graphic) self.graphicsLayer = [] def layernameValid(self): if not hasattr(self, 'layerName'): layerName, accept = QInputDialog.getText(None, QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return False else: self.layerName = layerName return True def clean(self): self.bar.clearWidgets() self.ui.poiText.setText("") self.ui.msgLbl.setText("") self.ui.resultLijst.clearContents() self.ui.resultLijst.setRowCount(0) self.clearGraphicsLayer()
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 AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(self.getParametersPanel(alg, self)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.runAsBatchButton = QPushButton( QCoreApplication.translate("AlgorithmDialog", "Run as Batch Process…")) self.runAsBatchButton.clicked.connect(self.runAsBatch) self.buttonBox.addButton( self.runAsBatchButton, QDialogButtonBox.ResetRole) # reset role to ensure left alignment def getParametersPanel(self, alg, parent): return ParametersPanel(parent, alg) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def getParamValues(self): parameters = {} if not hasattr(self, 'mainWidget') or self.mainWidget is None: return parameters for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if not param.isDestination(): wrapper = self.mainWidget.wrappers[param.name()] value = None if wrapper.widget: value = wrapper.value() parameters[param.name()] = value if not param.checkValueIsAcceptable(value): raise AlgorithmDialogBase.InvalidParameterValue( param, wrapper.widget) else: dest_project = None if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \ isinstance(param, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)): if self.mainWidget.checkBoxes[param.name()].isChecked(): dest_project = QgsProject.instance() value = self.mainWidget.outputWidgets[param.name()].getValue() if value and isinstance(value, QgsProcessingOutputLayerDefinition): value.destinationProject = dest_project if value: parameters[param.name()] = value return parameters def checkExtentCRS(self): unmatchingCRS = False hasExtent = False context = dataobjects.createContext() projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance()) for param in self.alg.parameterDefinitions(): if isinstance( param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = QgsProcessingUtils.mapLayerFromString( inputlayer, context) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): if param.skip_crs_check: continue value = self.mainWidget.wrappers[ param.name()].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): super(AlgorithmDialog, self)._saveGeometry() feedback = self.createFeedback() context = dataobjects.createContext(feedback) checkCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_CRS) try: parameters = self.getParamValues() if checkCRS and not self.alg.validateInputCrs(parameters, context): reply = QMessageBox.question( self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) # TODO if False and checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question( self, self.tr("Extent CRS"), self. tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.alg.checkParameterValues(parameters, context) if msg: QMessageBox.warning(self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass self.setInfo( self.tr('<b>Algorithm \'{0}\' starting...</b>').format( self.alg.displayName()), escape_html=False) feedback.pushInfo(self.tr('Input parameters:')) feedback.pushCommandInfo(pformat(parameters)) feedback.pushInfo('') start_time = time.time() if self.iterateParam: self.buttonCancel.setEnabled( self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) if executeIterating(self.alg, parameters, self.iterateParam, context, feedback): feedback.pushInfo( self.tr( 'Execution completed in {0:0.2f} seconds'.format( time.time() - start_time))) self.buttonCancel.setEnabled(False) self.finish(True, parameters, context, feedback) else: self.buttonCancel.setEnabled(False) self.resetGUI() else: command = self.alg.asPythonCommand(parameters, context) if command: ProcessingLog.addToLog(command) self.buttonCancel.setEnabled( self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) def on_complete(ok, results): if ok: feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'. format(time.time() - start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(results)) else: feedback.reportError( self.tr('Execution failed after {0:0.2f} seconds'. format(time.time() - start_time))) feedback.pushInfo('') self.buttonCancel.setEnabled(False) self.finish(ok, results, context, feedback) task = QgsProcessingAlgRunnerTask(self.alg, parameters, context, feedback) task.executed.connect(on_complete) QgsApplication.taskManager().addTask(task) except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect( lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage( "", self.tr("Wrong or missing parameter value: {0}").format( e.parameter.description()), level=QgsMessageBar.WARNING, duration=5) def finish(self, successful, result, context, feedback): keepOpen = not successful or ProcessingConfig.getSetting( ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: # add html results to results dock for out in self.alg.outputDefinitions(): if isinstance(out, QgsProcessingOutputHtml) and out.name( ) in result and result[out.name()]: resultsList.addResult(icon=self.alg.icon(), name=out.description(), result=result[out.name()]) if not handleAlgorithmResults(self.alg, context, feedback, not keepOpen): self.resetGUI() return self.executed = True self.setInfo(self.tr('Algorithm \'{0}\' finished').format( self.alg.displayName()), escape_html=False) if not keepOpen: self.close() else: self.resetGUI() if self.alg.hasHtmlOutputs(): self.setInfo(self.tr( 'HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.'), escape_html=False)
class geopunt4QgisAdresDialog(QDialog): def __init__(self, iface): QDialog.__init__(self, None) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.iface = iface # initialize locale locale = QSettings().value("locale/userLocale", "nl") if not locale: locale == 'nl' else: locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_geopunt4Qgis() self.ui.setupUi(self) #get settings self.s = QSettings() self.loadSettings() #setup geometryHelper object self.gh = geometryHelper(self.iface) #create graphicsLayer self.graphicsLayer = [] self.firstShow = True self.completer = QCompleter(self) self.completerModel = QStringListModel(self) self.ui.gemeenteBox.setCompleter(self.completer) self.completer.setModel(self.completerModel) self.completer.setCaseSensitivity(False) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton(QPushButton("Sluiten"), QDialogButtonBox.RejectRole) for btn in self.ui.buttonBox.buttons(): btn.setAutoDefault(0) #event handlers if self.adresSearchOnEnter: self.ui.zoekText.returnPressed.connect(self.onZoekActivated) else: self.ui.zoekText.textEdited.connect(self.onZoekActivated) self.ui.gemeenteBox.currentIndexChanged.connect(self.onZoekActivated) self.ui.resultLijst.itemDoubleClicked.connect(self.onItemActivated) self.ui.resultLijst.itemClicked.connect(self.onItemClick) self.ui.ZoomKnop.clicked.connect(self.onZoomKnopClick) self.ui.Add2mapKnop.clicked.connect(self.onAdd2mapKnopClick) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean) def loadSettings(self): self.saveToFile = int(self.s.value("geopunt4qgis/adresSavetoFile", 1)) layerName = self.s.value("geopunt4qgis/adreslayerText", "") if layerName: self.layerName = layerName self.adresSearchOnEnter = int( self.s.value("geopunt4qgis/adresSearchOnEnter", 0)) self.timeout = int(self.s.value("geopunt4qgis/timeout", 15)) if settings().proxyUrl: self.proxy = settings().proxyUrl else: self.proxy = "" self.startDir = self.s.value("geopunt4qgis/startDir", os.path.expanduser("~")) self.gp = Adres(self.timeout, self.proxy) # overwrite def show(self): QDialog.show(self) self.setWindowModality(0) arUrl = "http://loc.api.geopunt.be/" inet = internet_on(proxyUrl=self.proxy, timeout=self.timeout, testSite=arUrl) if not inet: msg = "Kan geen verbing maken met de site van Geopunt: {} \nMogelijke is deze site niet bereikbaar, dan zal deze tool ook niet werken.\nProbeer later opnieuw. Indien dit probleem zich blijft voordoen contacteer informatie Vlaanderen.".format( arUrl) QMessageBox.warning(self.iface.mainWindow(), "Waarschuwing: kan geen verbinding maken", msg) self.bar.pushMessage(QCoreApplication.translate( "geopunt4QgisPoidialog", "Waarschuwing"), msg, level=Qgis.Warning, duration=3) return if self.firstShow: self.am = basisregisters.adresMatch(self.timeout, self.proxy) gemeenteNamen = [n["Naam"] for n in self.am.gemeenten()] self.ui.gemeenteBox.addItems(gemeenteNamen) self.completerModel.setStringList(gemeenteNamen) self.ui.gemeenteBox.setEditText( QCoreApplication.translate("geopunt4QgisAdresDialog", "gemeente")) self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}') self.ui.gemeenteBox.setFocus() self.firstShow = False def openHelp(self): webbrowser.open_new_tab( "http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/zoek-een-adres" ) def onZoekActivated(self): self._clearGraphicsLayer() self.bar.clearWidgets() gemeente = self.ui.gemeenteBox.currentText() if gemeente != QCoreApplication.translate("geopunt4QgisAdresDialog", "gemeente"): self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #000000}') txt = self.ui.zoekText.text() + ", " + gemeente suggesties = self.gp.fetchSuggestion(txt, 25) self.ui.resultLijst.clear() if type(suggesties) is list and len(suggesties) != 0: self.ui.resultLijst.addItems(suggesties) if len(suggesties) == 1: self.ui.resultLijst.setCurrentRow(0) def onItemActivated(self, item): txt = item.text() self._zoomLoc(txt) def onItemClick(self, item): txt = item.text() streetNr = txt.split(",")[:-1] self.ui.zoekText.setText(",".join(streetNr)) def onZoomKnopClick(self): item = self.ui.resultLijst.currentItem() if item: self._zoomLoc(item.text()) def onAdd2mapKnopClick(self): item = self.ui.resultLijst.currentItem() if item: self._addToMap(item.text()) def _clearGraphicsLayer(self): for graphic in self.graphicsLayer: self.iface.mapCanvas().scene().removeItem(graphic) self.graphicsLayer = [] def _zoomLoc(self, txt): self._clearGraphicsLayer() locations = self.gp.fetchLocation(txt) if type(locations) is list and len(locations): loc = locations[0] LowerLeftX = loc['BoundingBox']['LowerLeft']['X_Lambert72'] LowerLeftY = loc['BoundingBox']['LowerLeft']['Y_Lambert72'] UpperRightX = loc['BoundingBox']['UpperRight']['X_Lambert72'] UpperRightY = loc['BoundingBox']['UpperRight']['Y_Lambert72'] self.gh.zoomtoRec(QgsPointXY(LowerLeftX, LowerLeftY), QgsPointXY(UpperRightX, UpperRightY), 31370) xlb, ylb = loc["Location"]["X_Lambert72"], loc["Location"][ "Y_Lambert72"] x, y = self.gh.prjPtToMapCrs(QgsPointXY(xlb, ylb), 31370) m = QgsVertexMarker(self.iface.mapCanvas()) self.graphicsLayer.append(m) m.setCenter(QgsPointXY(x, y)) m.setColor(QColor(255, 255, 0)) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(9) elif type(locations) is str: self.bar.pushMessage(QCoreApplication.translate( "geopunt4QgisAdresDialog", "Waarschuwing"), locations, level=Qgis.Warning, duration=3) else: self.bar.pushMessage("Error", QCoreApplication.translate( "geopunt4QgisAdresDialog", "onbekende fout"), level=Qgis.Critical, duration=3) def _addToMap(self, txt): if not self.layernameValid(): return locations = self.gp.fetchLocation(txt) if type(locations) is list and len(locations): loc = locations[0] x, y = loc["Location"]["X_Lambert72"], loc["Location"][ "Y_Lambert72"] adres = loc["FormattedAddress"] LocationType = loc["LocationType"] pt = self.gh.prjPtToMapCrs(QgsPointXY(x, y), 31370) self.gh.save_adres_point(pt, adres, typeAddress=LocationType, layername=self.layerName, saveToFile=self.saveToFile, sender=self, startFolder=os.path.join( self.startDir, self.layerName)) def layernameValid(self): if not hasattr(self, 'layerName'): layerName, accept = QInputDialog.getText( None, QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:')) if accept == False: return False else: self.layerName = layerName return True def clean(self): self.bar.clearWidgets() self.ui.resultLijst.clear() self.ui.zoekText.setText("") self.ui.gemeenteBox.setEditText( QCoreApplication.translate("geopunt4QgisAdresDialog", "gemeente")) self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}') self._clearGraphicsLayer()
class S3Manager(S3Connection): def __init__(self, access_key_id, secret_access_key, bucket_name, filenames, upload_options, wizard_page, parent=None): S3Connection.__init__(self, access_key_id, secret_access_key) self.upload_options = upload_options self.bucket_name = bucket_name self.bucket = None self.filenames = filenames self.s3Uploaders = [] self.threads = [] self.count_uploaded_images = 0 self.num_uploading_images = 0 #For GUI (messages and progress bars) self.wizard_page = wizard_page self.uploader_widget = QtGui.QWidget() self.uploader_widget.setWindowTitle("Upload Progress Bars") self.uploader_widget.setWindowFlags(Qt.WindowStaysOnTopHint) self.uploader_v_box = QtGui.QVBoxLayout() self.uploader_widget.setLayout(self.uploader_v_box) self.msg_bar_main = None self.msg_bar_main_content = None self.cancel_button_main = None self.msg_bars = [] self.msg_bars_content = [] self.progress_bars = [] self.cancel_buttons = [] def getBucket(self): for trial in xrange(3): if self.bucket: break try: self.bucket = super(S3Manager, self).get_bucket(self.bucket_name) QgsMessageLog.logMessage('Connection established' % trial, 'OAM', level=QgsMessageLog.INFO) except: if trial == 2: QgsMessageLog.logMessage( 'Failed to connect after 3 attempts', 'OAM', level=QgsMessageLog.CRITICAL) return self.bucket """for testing purpose""" """ rsKeys = [] for key in self.bucket.list(): rsKeys.append(repr(key)) return rsKeys """ #functions for threading purpose def uploadFiles(self): """ Testing purpose only """ if "notify_oam" in self.upload_options: print "notify_oam" if "trigger_tiling" in self.upload_options: print "trigger_tiling" # configure the msg_bar_main (including Cancel button and its container) self.msg_bar_main = QgsMessageBar() self.msg_bar_main.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.wizard_page.layout().addWidget(self.msg_bar_main) self.msg_bar_main_content = self.msg_bar_main.createMessage( 'Performing upload...', ) self.cancel_button_main = QPushButton() self.cancel_button_main.setText('Cancel') self.cancel_button_main.clicked.connect(self.cancelAllUploads) self.msg_bar_main_content.layout().addWidget(self.cancel_button_main) self.msg_bar_main.clearWidgets() self.msg_bar_main.pushWidget(self.msg_bar_main_content, level=QgsMessageBar.INFO) self.num_uploading_images = len(self.filenames) for i in range(0, self.num_uploading_images): filename = self.filenames[i] # create a new S3Uploader instance self.s3Uploaders.append( S3Uploader(filename, self.bucket, self.upload_options, i)) try: # start the worker in a new thread self.threads.append(QThread()) self.s3Uploaders[i].moveToThread(self.threads[i]) self.s3Uploaders[i].finished.connect(self.finishUpload) self.s3Uploaders[i].error.connect(self.displayUploadError) self.threads[i].started.connect(self.s3Uploaders[i].run) self.threads[i].start() print repr(self.threads[i]) # configure the msg_bars for progress bar self.msg_bars.append(QgsMessageBar()) self.msg_bars[i].setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) #self.wizard_page.layout().addWidget(self.msg_bars[i]) self.uploader_v_box.addWidget(self.msg_bars[i]) #set the texts of uploading files file_basename = str(os.path.basename(str(filename))) self.msg_bars_content.append(self.msg_bars[i].createMessage( file_basename, )) self.msg_bars[i].clearWidgets() self.msg_bars[i].pushWidget(self.msg_bars_content[i], level=QgsMessageBar.INFO) #add progress bars in the message bar self.progress_bars.append(QProgressBar()) self.progress_bars[i].setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.msg_bars_content[i].layout().addWidget( self.progress_bars[i]) self.s3Uploaders[i].progress.connect(self.updateProgressBar) #set cancel button in the message bar for each upload file """ self.cancel_buttons.append(QPushButton()) self.cancel_buttons[i].setText('Cancel') self.cancel_buttons[i].clicked.connect(self.cancelAllUploads) self.msg_bars_content[i].layout().addWidget(self.cancel_buttons[i]) #self.cancel_buttons[i].clicked.connect(self.cancelUpload) """ except Exception, e: return repr(e) #Display upload progress bars in a separate widget self.uploader_widget.show() screenShape = QtGui.QDesktopWidget().screenGeometry() width, height = screenShape.width(), screenShape.height() winW, winH = self.uploader_widget.frameGeometry().width( ), self.uploader_widget.frameGeometry().height() left = width - (winW + 10) top = height - (winH + 50) self.uploader_widget.move(left, top) #self.uploader_widget.activateWindow() return True
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 S3Manager(S3Connection): def __init__(self, access_key_id, secret_access_key, bucket_name, filenames, upload_options, wizard_page, parent=None): S3Connection.__init__(self, access_key_id, secret_access_key) self.upload_options = upload_options self.bucket_name = bucket_name self.bucket = None self.filenames = filenames self.s3Uploaders = [] self.threads = [] self.count_uploaded_images = 0 self.num_uploading_images = 0 #For GUI (messages and progress bars) self.wizard_page = wizard_page self.uploader_widget = QtGui.QWidget() self.uploader_widget.setWindowTitle("Upload Progress Bars") self.uploader_widget.setWindowFlags(Qt.WindowStaysOnTopHint) self.uploader_v_box = QtGui.QVBoxLayout() self.uploader_widget.setLayout(self.uploader_v_box) self.msg_bar_main = None self.msg_bar_main_content = None self.cancel_button_main = None self.msg_bars = [] self.msg_bars_content = [] self.progress_bars = [] self.cancel_buttons = [] def getBucket(self): for trial in xrange(3): if self.bucket: break try: self.bucket = super(S3Manager,self).get_bucket(self.bucket_name) QgsMessageLog.logMessage( 'Connection established' % trial, 'OAM', level=QgsMessageLog.INFO) except: if trial == 2: QgsMessageLog.logMessage( 'Failed to connect after 3 attempts', 'OAM', level=QgsMessageLog.CRITICAL) return self.bucket """for testing purpose""" """ rsKeys = [] for key in self.bucket.list(): rsKeys.append(repr(key)) return rsKeys """ #functions for threading purpose def uploadFiles(self): """ Testing purpose only """ if "notify_oam" in self.upload_options: print "notify_oam" if "trigger_tiling" in self.upload_options: print "trigger_tiling" # configure the msg_bar_main (including Cancel button and its container) self.msg_bar_main = QgsMessageBar() self.msg_bar_main.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.wizard_page.layout().addWidget(self.msg_bar_main) self.msg_bar_main_content = self.msg_bar_main.createMessage('Performing upload...', ) self.cancel_button_main = QPushButton() self.cancel_button_main.setText('Cancel') self.cancel_button_main.clicked.connect(self.cancelAllUploads) self.msg_bar_main_content.layout().addWidget(self.cancel_button_main) self.msg_bar_main.clearWidgets() self.msg_bar_main.pushWidget(self.msg_bar_main_content, level=QgsMessageBar.INFO) self.num_uploading_images = len(self.filenames) for i in range(0, self.num_uploading_images): filename = self.filenames[i] # create a new S3Uploader instance self.s3Uploaders.append(S3Uploader(filename, self.bucket, self.upload_options, i)) try: # start the worker in a new thread self.threads.append(QThread()) self.s3Uploaders[i].moveToThread(self.threads[i]) self.s3Uploaders[i].finished.connect(self.finishUpload) self.s3Uploaders[i].error.connect(self.displayUploadError) self.threads[i].started.connect(self.s3Uploaders[i].run) self.threads[i].start() print repr(self.threads[i]) # configure the msg_bars for progress bar self.msg_bars.append(QgsMessageBar()) self.msg_bars[i].setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) #self.wizard_page.layout().addWidget(self.msg_bars[i]) self.uploader_v_box.addWidget(self.msg_bars[i]) #set the texts of uploading files file_basename = str(os.path.basename(str(filename))) self.msg_bars_content.append(self.msg_bars[i].createMessage(file_basename, )) self.msg_bars[i].clearWidgets() self.msg_bars[i].pushWidget(self.msg_bars_content[i], level=QgsMessageBar.INFO) #add progress bars in the message bar self.progress_bars.append(QProgressBar()) self.progress_bars[i].setAlignment(Qt.AlignLeft|Qt.AlignVCenter) self.msg_bars_content[i].layout().addWidget(self.progress_bars[i]) self.s3Uploaders[i].progress.connect(self.updateProgressBar) #set cancel button in the message bar for each upload file """ self.cancel_buttons.append(QPushButton()) self.cancel_buttons[i].setText('Cancel') self.cancel_buttons[i].clicked.connect(self.cancelAllUploads) self.msg_bars_content[i].layout().addWidget(self.cancel_buttons[i]) #self.cancel_buttons[i].clicked.connect(self.cancelUpload) """ except Exception, e: return repr(e) #Display upload progress bars in a separate widget self.uploader_widget.show() screenShape = QtGui.QDesktopWidget().screenGeometry() width, height = screenShape.width(), screenShape.height() winW, winH = self.uploader_widget.frameGeometry().width(), self.uploader_widget.frameGeometry().height() left = width - (winW + 10) top = height - (winH + 50) self.uploader_widget.move(left,top) #self.uploader_widget.activateWindow() return True
class geopunt4QgisPoidialog(QtGui.QDialog): def __init__(self, iface): QtGui.QDialog.__init__(self, None) self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QtCore.QSettings().value("locale/userLocale", "nl") if not locale: locale == 'nl' else: locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QtCore.QTranslator() self.translator.load(localePath) if QtCore.qVersion() > '4.3.3': QtCore.QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): 'Set up the user interface from Designer.' self.ui = Ui_geopunt4QgisPoiDlg() self.ui.setupUi(self) #get settings self.s = QtCore.QSettings() self.loadSettings() #setup geopunt and geometryHelper objects self.gh = gh.geometryHelper(self.iface) self.ph = poiHelper(self.iface) #create the graphicsLayer self.graphicsLayer = [] #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton(QtGui.QPushButton("Sluiten"), QtGui.QDialogButtonBox.RejectRole) for btn in self.ui.buttonBox.buttons(): btn.setAutoDefault(0) #table ui self.ui.resultLijst.hideColumn(0) # filters self.firstShow = True self.poiTypes = {} self.poiCategories = {} self.poiThemes = {} #actions self.ui.resultLijst.addAction(self.ui.actionZoomtoSelection) self.ui.actionZoomtoSelection.triggered.connect(self.onZoomSelClicked) self.ui.resultLijst.addAction(self.ui.actionAddTSeltoMap) self.ui.actionAddTSeltoMap.triggered.connect(self.onAddSelClicked) #event handlers self.ui.zoekKnop.clicked.connect(self.onZoekActivated) self.ui.zoomSelKnop.clicked.connect(self.onZoomSelClicked) self.ui.resultLijst.itemDoubleClicked.connect(self.onZoomSelClicked) self.ui.resultLijst.itemSelectionChanged.connect( self.onSelectionChanged) self.ui.addToMapKnop.clicked.connect(self.onAddSelClicked) self.ui.addMinModelBtn.clicked.connect(self.addMinModel) self.ui.poiText.textChanged.connect(self.searchTxtChanged) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean) def loadSettings(self): self.saveToFile = int(self.s.value("geopunt4qgis/poiSavetoFile", 1)) layerName = self.s.value("geopunt4qgis/poilayerText", "") if layerName: self.layerName = layerName self.timeout = int(self.s.value("geopunt4qgis/timeout", 15)) if int(self.s.value("geopunt4qgis/useProxy", 0)): self.proxy = self.s.value("geopunt4qgis/proxyHost", "") self.port = self.s.value("geopunt4qgis/proxyPort", "") else: self.proxy = "" self.port = "" self.startDir = self.s.value("geopunt4qgis/startDir", os.path.dirname(__file__)) self.poi = geopunt.Poi(self.timeout, self.proxy, self.port) def show(self): QtGui.QDialog.show(self) self.setWindowModality(0) if self.firstShow: inet = geopunt.internet_on(proxyUrl=self.proxy, port=self.port, timeout=self.timeout) #filters if inet: self.poiThemes = dict(self.poi.listPoiThemes()) poiThemes = [""] + self.poiThemes.keys() poiThemes.sort() self.ui.filterPoiThemeCombo.addItems(poiThemes) self.poiCategories = dict(self.poi.listPoiCategories()) poiCategories = [""] + self.poiCategories.keys() poiCategories.sort() self.ui.filterPoiCategoryCombo.addItems(poiCategories) self.poiTypes = dict(self.poi.listPoitypes()) poiTypes = [""] + self.poiTypes.keys() poiTypes.sort() self.ui.filterPoiTypeCombo.addItems(poiTypes) gemeentes = json.load( open( os.path.join(os.path.dirname(__file__), "data/gemeentenVL.json"))) self.NIScodes = {n["Naam"]: n["Niscode"] for n in gemeentes} gemeenteNamen = [n["Naam"] for n in gemeentes] gemeenteNamen.sort() self.ui.filterPoiNIS.addItems(gemeenteNamen) #connect when inet on self.ui.filterPoiThemeCombo.activated.connect( self.onThemeFilterChange) self.ui.filterPoiCategoryCombo.activated.connect( self.onCategorieFilterChange) self.firstShow = False else: self.bar.pushMessage( QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "Kan geen verbing maken met het internet."), level=QgsMessageBar.WARNING, duration=3) def openHelp(self): webbrowser.open_new_tab( "http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/poi" ) def onZoekActivated(self): txt = self.ui.poiText.text() self.ui.resultLijst.clearContents() self.ui.resultLijst.setRowCount(0) self.ui.msgLbl.setText("") ##filters: poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poitheme = self.poiThemes[poithemeText] else: poitheme = "" poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "": poiCategorie = self.poiCategories[poiCategorieText] else: poiCategorie = "" poiTypeText = self.ui.filterPoiTypeCombo.currentText() if poiTypeText != "": poiType = self.poiTypes[poiTypeText] else: poiType = "" NISText = self.ui.filterPoiNIS.currentText() if NISText != "" and not self.ui.currentBoundsVink.isChecked(): Niscode = self.NIScodes[NISText] else: Niscode = "" if self.ui.currentBoundsVink.isChecked(): bbox = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs( [bbox.xMinimum(), bbox.yMinimum()], 4326) maxX, maxY = self.gh.prjPtFromMapCrs( [bbox.xMaximum(), bbox.yMaximum()], 4326) xyBox = [minX, minY, maxX, maxY] self.poi.fetchPoi(txt, c=32, srs=4326, maxModel=True, updateResults=True, bbox=xyBox, theme=poitheme, category=poiCategorie, POItype=poiType) else: self.poi.fetchPoi(txt, c=32, srs=4326, maxModel=True, updateResults=True, bbox=None, theme=poitheme, category=poiCategorie, POItype=poiType, region=Niscode) suggesties = self.poi.poiSuggestion() if type(suggesties) is list and len(suggesties) > 0: #prevent sorting every time an item is added self.ui.resultLijst.setSortingEnabled(False) row = 0 for sug in suggesties: id = QtGui.QTableWidgetItem(sug[0], 0) theme = QtGui.QTableWidgetItem(sug[1], 0) categorie = QtGui.QTableWidgetItem(sug[2], 0) PoiType = QtGui.QTableWidgetItem(sug[3], 0) naam = QtGui.QTableWidgetItem(sug[4], 0) adres = QtGui.QTableWidgetItem( sug[5].replace("<br />", ", ").replace("<br/>", ", "), 0) self.ui.resultLijst.insertRow(row) self.ui.resultLijst.setItem(row, 0, id) self.ui.resultLijst.setItem(row, 1, theme) self.ui.resultLijst.setItem(row, 2, categorie) self.ui.resultLijst.setItem(row, 3, PoiType) self.ui.resultLijst.setItem(row, 4, naam) self.ui.resultLijst.setItem(row, 5, adres) row += 1 self.ui.resultLijst.setSortingEnabled(True) if txt == "": self.ui.msgLbl.setText( QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "Aantal getoond: %s gevonden: %s" % (self.ui.resultLijst.rowCount(), self.poi.resultCount))) else: self.ui.msgLbl.setText( QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "Aantal getoond: %s" % (self.ui.resultLijst.rowCount()))) elif len(suggesties) == 0: self.bar.pushMessage(QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "Geen resultaten gevonden"), txt, level=QgsMessageBar.INFO, duration=5) elif type(suggesties) is str: self.bar.pushMessage(QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "Waarschuwing"), suggesties, level=QgsMessageBar.WARNING) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "onbekende fout"), level=QgsMessageBar.CRITICAL) def onZoomSelClicked(self): selPois = self._getSelectedPois() if len(selPois) <= 0: self.bar.pushMessage(QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "Merk op"), QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "Er niets om naar te zoomen"), level=QgsMessageBar.INFO, duration=3) elif len(selPois) >= 2: pts = [ n['location']['points'][0]['Point']['coordinates'] for n in selPois ] bounds = self.gh.getBoundsOfPointArray(pts) self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326) elif len(selPois) == 1: x, y = selPois[0]['location']['points'][0]['Point']['coordinates'] bounds = self.gh.getBoundsOfPoint(x, y) self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326) def onSelectionChanged(self): selPois = self._getSelectedPois() canvas = self.iface.mapCanvas() self.clearGraphicsLayer() pts = [ self.gh.prjPtToMapCrs( n['location']['points'][0]['Point']['coordinates'], 4326) for n in selPois ] for pt in pts: x, y = pt m = QgsVertexMarker(canvas) self.graphicsLayer.append(m) m.setCenter(QgsPoint(x, y)) m.setColor(QtGui.QColor(255, 255, 0)) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(10) def onAddSelClicked(self): if not self.layernameValid(): return self.clearGraphicsLayer() pts = self._getSelectedPois() self.ph.save_pois_points(pts, layername=self.layerName, startFolder=os.path.join( self.startDir, self.layerName), saveToFile=self.saveToFile, sender=self) def onThemeFilterChange(self): poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poithemeID = self.poiThemes[poithemeText] poiCategories = [""] + [ n[0] for n in self.poi.listPoiCategories(poithemeID) ] poiCategories.sort() self.ui.filterPoiCategoryCombo.clear() self.ui.filterPoiTypeCombo.clear() self.ui.filterPoiCategoryCombo.addItems(poiCategories) else: self.ui.filterPoiCategoryCombo.addItems([""] + self.poiCategories.keys()) self.ui.filterPoiTypeCombo.addItems([""] + self.poiTypes.keys()) def onCategorieFilterChange(self): poithemeText = self.ui.filterPoiThemeCombo.currentText() poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "" and poithemeText != "": poiCategorieID = self.poiCategories[poiCategorieText] poithemeID = self.poiThemes[poithemeText] poiTypes = [""] + [ n[0] for n in self.poi.listPoitypes(poithemeID, poiCategorieID) ] poiTypes.sort() self.ui.filterPoiTypeCombo.clear() self.ui.filterPoiTypeCombo.addItems(poiTypes) def addMinModel(self): if not self.layernameValid(): return self.clearGraphicsLayer() txt = self.ui.poiText.text() poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poitheme = self.poiThemes[poithemeText] else: poitheme = "" poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "": poiCategorie = self.poiCategories[poiCategorieText] else: poiCategorie = "" poiTypeText = self.ui.filterPoiTypeCombo.currentText() if poiTypeText != "": poiType = self.poiTypes[poiTypeText] else: poiType = "" NISText = self.ui.filterPoiNIS.currentText() if NISText != "" and not self.ui.currentBoundsVink.isChecked(): Niscode = self.NIScodes[NISText] else: Niscode = "" if self.ui.currentBoundsVink.isChecked(): bbox = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs( [bbox.xMinimum(), bbox.yMinimum()], 4326) maxX, maxY = self.gh.prjPtFromMapCrs( [bbox.xMaximum(), bbox.yMaximum()], 4326) xyBox = [minX, minY, maxX, maxY] pts = self.poi.fetchPoi(txt, c=1024, srs=4326, maxModel=0, updateResults=0, bbox=xyBox, theme=poitheme, category=poiCategorie, POItype=poiType) else: pts = self.poi.fetchPoi(txt, c=1024, srs=4326, maxModel=0, updateResults=0, bbox=None, theme=poitheme, category=poiCategorie, POItype=poiType, region=Niscode) if type(pts) == str: self.bar.pushMessage(QtCore.QCoreApplication.translate( "geopunt4QgisPoidialog", "Waarschuwing"), pts, level=QgsMessageBar.WARNING, duration=5) elif type(pts) == list or type(pts) == dict: self.ph.save_minPois_points(pts, layername=self.layerName, startFolder=os.path.join( self.startDir, self.layerName), saveToFile=self.saveToFile, sender=self) self.close() def searchTxtChanged(self): txt = self.ui.poiText.text() if txt != "": msg = QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Voeg meer punten toe") else: msg = QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Voeg alle punten toe") self.ui.addMinModelBtn.setText(msg) def _getSelectedPois(self): pois = self.poi.PoiResult selPois = [] selRows = set( [sel.row() for sel in self.ui.resultLijst.selectedIndexes()]) for row in selRows: itemID = self.ui.resultLijst.item(row, 0).text() selPois += [i for i in pois if i["id"] == itemID] return selPois def clearGraphicsLayer(self): for graphic in self.graphicsLayer: self.iface.mapCanvas().scene().removeItem(graphic) self.graphicsLayer = [] def layernameValid(self): if not hasattr(self, 'layerName'): layerName, accept = QtGui.QInputDialog.getText( None, QtCore.QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QtCore.QCoreApplication.translate( "geopunt4Qgis", 'Geef een naam voor de laag op:')) if accept == False: return False else: self.layerName = layerName return True def clean(self): self.bar.clearWidgets() self.ui.poiText.setText("") self.ui.msgLbl.setText("") self.ui.resultLijst.clearContents() self.ui.resultLijst.setRowCount(0) self.clearGraphicsLayer()
class tTestAnalysisDialog(QDialog, FORM_CLASS): """Extract statistics from a list of rasters at set locations.""" toolKey = 'tTestAnalysisDialog' def __init__(self, iface, parent=None): super(tTestAnalysisDialog, self).__init__(parent) # Set up the user interface from Designer. self.setupUi(self) self.iface = iface self.DISP_TEMP_LAYERS = read_setting(PLUGIN_NAME + '/DISP_TEMP_LAYERS', bool) self.DEBUG = config.get_debug_mode() # Catch and redirect python errors directed at the log messages python error tab. QgsApplication.messageLog().messageReceived.connect(errorCatcher) if not os.path.exists(TEMPDIR): os.mkdir(TEMPDIR) # Setup for validation messagebar on gui----------------------------- self.messageBar = QgsMessageBar( self) # leave this message bar for bailouts self.validationLayout = QtWidgets.QFormLayout( self) # new layout to gui self.raster_filter_message = self.lblLayerFilter.text() self.pixel_size = ['0', 'm', ''] self.layer_table = build_layer_table() if isinstance(self.layout(), (QtWidgets.QFormLayout, QtWidgets.QGridLayout)): # create a validation layout so multiple messages can be added and cleaned up. self.layout().insertRow(0, self.validationLayout) self.layout().insertRow(0, self.messageBar) else: # for use with Vertical/horizontal layout box self.layout().insertWidget(0, self.messageBar) for cbo in [ self.mcboRasterLayer, self.mcboCtrlRasterLayer, self.mcboZoneRasterLyr ]: cbo.setFilters(QgsMapLayerProxyModel.RasterLayer) cbo.setExcludedProviders(['wms']) cbo.setAllowEmptyLayer(True) cbo.setCurrentIndex(0) self.mcboPointsLayer.setFilters(QgsMapLayerProxyModel.PointLayer) self.mcboPointsLayer.setExcludedProviders(['wms']) self.mcboPointsLayer.setAllowEmptyLayer(True) self.mcboPointsLayer.setCurrentIndex(0) self.setMapLayers() self.updateUseSelected() # GUI Runtime Customisation ----------------------------------------------- self.setWindowIcon(QtGui.QIcon(':/plugins/pat/icons/icon_t-test.svg')) def cleanMessageBars(self, AllBars=True): """Clean Messages from the validation layout. Args: AllBars (bool): Remove All bars including those which haven't timed-out. Default - True """ layout = self.validationLayout for i in reversed(list(range(layout.count()))): # when it timed out the row becomes empty.... if layout.itemAt(i).isEmpty(): # .removeItem doesn't always work. so takeAt(pop) it instead item = layout.takeAt(i) elif AllBars: # ie remove all item = layout.takeAt(i) # also have to remove any widgets associated with it. if item.widget() is not None: item.widget().deleteLater() def send_to_messagebar(self, message, title='', level=Qgis.Info, duration=5, exc_info=None, core_QGIS=False, addToLog=False, showLogPanel=False): """ Add a message to the forms message bar. Args: message (str): Message to display title (str): Title of message. Will appear in bold. Defaults to '' level (QgsMessageBarLevel): The level of message to log. Defaults to Qgis.Info duration (int): Number of seconds to display message for. 0 is no timeout. Defaults to 5 core_QGIS (bool): Add to QGIS interface rather than the dialog addToLog (bool): Also add message to Log. Defaults to False showLogPanel (bool): Display the log panel exc_info () : Information to be used as a traceback if required """ if core_QGIS: newMessageBar = self.iface.messageBar() else: newMessageBar = QgsMessageBar(self) widget = newMessageBar.createMessage(title, message) if showLogPanel: """check out C:\data\GIS_Tools\Reference_Code\QGIS_Reference_Plugins\QGIS-master\python\plugins\db_manager\db_tree.py to try and hyperlink""" button = QPushButton(widget) button.setText('View') button.setContentsMargins(0, 0, 0, 0) button.setFixedWidth(35) button.pressed.connect(openLogPanel) widget.layout().addWidget(button) newMessageBar.pushWidget(widget, level, duration=duration) if not core_QGIS: rowCount = self.validationLayout.count() self.validationLayout.insertRow(rowCount + 1, newMessageBar) if addToLog: if level == 1: # 'WARNING': LOGGER.warning(message) elif level == 2: # 'CRITICAL': # Add a traceback to log only for bailouts only if exc_info is not None: exc_type, exc_value, exc_traceback = sys.exc_info() mess = str(traceback.format_exc()) message = message + '\n' + mess LOGGER.critical(message) else: # INFO = 0 LOGGER.info(message) def setMapLayers(self): """ Run through all loaded layers to find ones which should be excluded. In this case exclude services.""" if len(self.layer_table) == 0: return cbo_list = [ self.mcboRasterLayer, self.mcboCtrlRasterLayer, self.mcboZoneRasterLyr ] if self.mcboPointsLayer.currentLayer() is None: for cbo in cbo_list: cbo.setExceptedLayerList([]) return df_pts = self.layer_table[self.layer_table['layer_id'] == self.mcboPointsLayer.currentLayer().id()] used_layers = [ cbo.currentLayer().id() for cbo in cbo_list if cbo.currentLayer() is not None ] df_rastlyrs = self.layer_table[ (self.layer_table['provider'] == 'gdal') & (self.layer_table['layer_type'] == 'RasterLayer')] if self.chkUseSelected.isChecked(): layer = self.mcboPointsLayer.currentLayer() transform = QgsCoordinateTransform( QgsCoordinateReferenceSystem(df_pts['epsg'].values[0]), QgsProject.instance().crs(), QgsProject.instance()) prj_ext = transform.transformBoundingBox( layer.boundingBoxOfSelected()) df_pts['geometry'] = wkt.loads(prj_ext.asWktPolygon()) # recreate the geodataframe or unary_union wont work df_pts = gpd.GeoDataFrame(df_pts, geometry='geometry', crs=df_pts.crs) if self.pixel_size[0] == '0': # Find layers that overlap. if len(df_pts) > 0: df_keep = df_rastlyrs[df_rastlyrs.intersects( df_pts.unary_union)] else: # Find layers that overlap and have the same pixel size. if len(df_pts) > 0: df_keep = df_rastlyrs[ (df_rastlyrs['pixel_size'] == self.pixel_size[0]) & (df_rastlyrs.intersects(df_pts.unary_union))] # process for each raster layer cbo for cbo in cbo_list: df_cbo = df_keep.copy() if cbo.currentLayer() is None: loop_used_layers = used_layers else: loop_used_layers = [ ea for ea in used_layers if cbo.currentLayer().id() != ea ] # add it back the current one. df_cbo = df_cbo[~df_cbo['layer_id'].isin(loop_used_layers)] # find those we don't want to keep; df_remove = pd.concat([df_rastlyrs, df_cbo]).drop_duplicates(keep=False) cbo.setExceptedLayerList(df_remove['layer'].tolist()) def updateUseSelected(self): """Update use selected checkbox if active layer has a feature selection""" self.chkUseSelected.setChecked(False) if self.mcboPointsLayer.count( ) == 0 or self.mcboPointsLayer.currentLayer() is None: return point_lyr = self.mcboPointsLayer.currentLayer() if point_lyr.selectedFeatureCount() > 0: self.chkUseSelected.setText( 'Use the {} selected feature(s) ?'.format( point_lyr.selectedFeatureCount())) self.chkUseSelected.setEnabled(True) else: self.chkUseSelected.setText('No features selected') self.chkUseSelected.setEnabled(False) def on_mcboRasterLayer_layerChanged(self): if self.mcboRasterLayer.currentLayer() is None: self.pixel_size = ['0', 'm', ''] self.lblLayerFilter.setText(self.raster_filter_message) return else: self.pixel_size = get_pixel_size( self.mcboRasterLayer.currentLayer()) self.lblLayerFilter.setText( 'Raster lists below have been <b>filtered</b> to only show rasters with <br/>' + '- a pixelsize of <b>{} {}</b><br/>'.format( self.pixel_size[0], self.pixel_size[1]) + '- and <b>overlaps</b> with the points layer') self.mcboCtrlRasterLayer.setEnabled( self.mcboCtrlRasterLayer.count() > 1) self.mcboZoneRasterLyr.setEnabled(self.mcboCtrlRasterLayer.count() > 1) self.setMapLayers() def on_chkUseSelected_stateChanged(self, state): self.setMapLayers() def on_mcboCtrlRasterLayer_layerChanged(self): self.setMapLayers() def on_mcboZoneRasterLyr_layerChanged(self): self.setMapLayers() def on_mcboPointsLayer_layerChanged(self): self.updateUseSelected() self.setMapLayers() @QtCore.pyqtSlot(name='on_cmdOutputFolder_clicked') def on_cmdOutputFolder_clicked(self): self.messageBar.clearWidgets() if self.lneOutputFolder.text() is None: outFolder = '' else: outFolder = self.lneOutputFolder.text() if outFolder == '': outFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastOutFolder") if outFolder is None or not os.path.exists(outFolder): outFolder = read_setting(PLUGIN_NAME + '/BASE_OUT_FOLDER') s = QFileDialog.getExistingDirectory( self, self. tr("Save output files to a folder. A sub-folder will be created from the image name" ), outFolder, QFileDialog.ShowDirsOnly) self.cleanMessageBars(self) if s == '' or s is None: return s = os.path.normpath(s) self.lblOutputFolder.setStyleSheet('color:black') self.lneOutputFolder.setStyleSheet('color:black') self.lneOutputFolder.setText(s) write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastOutFolder", s) def validate(self): """Check to see that all required gui elements have been entered and are valid.""" self.messageBar.clearWidgets() self.cleanMessageBars(AllBars=True) try: errorList = [] if self.mcboPointsLayer.currentLayer() is None: self.lblPointsLayer.setStyleSheet('color:red') errorList.append(self.tr("Input points layer required.")) else: self.lblPointsLayer.setStyleSheet('color:black') if self.mcboRasterLayer.currentLayer() is None: self.lblRasterLayer.setStyleSheet('color:red') errorList.append( self.tr("Input strips raster layer required.")) else: self.lblRasterLayer.setStyleSheet('color:black') if self.dsbMovingWinSize.value() <= 0: self.lblMovingWinSize.setStyleSheet('color:red') errorList.append(self.tr("Pixel size must be greater than 0.")) else: self.lblMovingWinSize.setStyleSheet('color:black') if self.lneOutputFolder.text() == '': self.lblOutputFolder.setStyleSheet('color:red') errorList.append(self.tr("Select output data folder")) elif not os.path.exists(self.lneOutputFolder.text()): self.lneOutputFolder.setStyleSheet('color:red') self.lblOutputFolder.setStyleSheet('color:red') errorList.append(self.tr("Output data folder does not exist")) else: self.lblOutputFolder.setStyleSheet('color:black') self.lneOutputFolder.setStyleSheet('color:black') if len(errorList) > 0: raise ValueError(errorList) except ValueError as e: self.cleanMessageBars(True) if len(errorList) > 0: for i, ea in enumerate(errorList): self.send_to_messagebar(str(ea), level=Qgis.Warning, duration=(i + 1) * 5) return False return True def accept(self, *args, **kwargs): try: if not self.validate(): return False # disable form via a frame, this will still allow interaction with the message bar # self.fraMain.setDisabled(True) # clean gui and Qgis messagebars self.cleanMessageBars(True) # Change cursor to Wait cursor QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.iface.mainWindow().statusBar().showMessage( 'Processing {}'.format(self.windowTitle())) LOGGER.info('{st}\nProcessing {}'.format(self.windowTitle(), st='*' * 50)) self.send_to_messagebar( "Please wait. QGIS will be locked. See log panel for progress.", level=Qgis.Warning, duration=0, addToLog=False, core_QGIS=False, showLogPanel=True) # Add settings to log settingsStr = 'Parameters:---------------------------------------' if self.chkUseSelected.isChecked(): settingsStr += '\n {:20}\t{} with {} selected features'.format( 'Strip points layer:', self.mcboPointsLayer.currentLayer().name(), self.mcboPointsLayer.currentLayer().selectedFeatureCount()) else: settingsStr += '\n {:20}\t{}'.format( 'Strip points layer:', self.mcboPointsLayer.currentLayer().name()) settingsStr += '\n {:20}\t{}'.format( 'Strip values raster:', self.mcboRasterLayer.currentLayer().name()) control_file, zone_file = ['', ''] if self.mcboCtrlRasterLayer.currentLayer() is not None: settingsStr += '\n {:20}\t{}'.format( 'Control values raster:', self.mcboCtrlRasterLayer.currentLayer().name()) control_file = get_layer_source( self.mcboCtrlRasterLayer.currentLayer()) if self.mcboZoneRasterLyr.currentLayer() is not None: settingsStr += '\n {:20}\t{}'.format( 'Control values raster:', self.mcboZoneRasterLyr.currentLayer().name()) zone_file = get_layer_source( self.mcboZoneRasterLyr.currentLayer()) settingsStr += '\n {:20}\t{}'.format( 'Moving window size: ', self.dsbMovingWinSize.value()) settingsStr += '\n {:30}\t{}\n'.format( 'Output folder:', self.lneOutputFolder.text()) LOGGER.info(settingsStr) lyrPoints = self.mcboPointsLayer.currentLayer() if self.chkUseSelected.isChecked() or lyrPoints.providerType( ) == 'delimitedtext': savePtsName = lyrPoints.name() + '_strippts.shp' fileStripPts = os.path.join(TEMPDIR, savePtsName) if os.path.exists(fileStripPts): removeFileFromQGIS(fileStripPts) QgsVectorFileWriter.writeAsVectorFormat( lyrPoints, fileStripPts, "utf-8", lyrPoints.crs(), driverName="ESRI Shapefile", onlySelected=self.chkUseSelected.isChecked()) if self.DISP_TEMP_LAYERS: addVectorFileToQGIS(fileStripPts, layer_name=os.path.splitext( os.path.basename(fileStripPts))[0], group_layer_name='DEBUG', atTop=True) else: fileStripPts = get_layer_source(lyrPoints) points_desc = describe.VectorDescribe(fileStripPts) gdf_pts = points_desc.open_geo_dataframe() df_table = ttest_analysis(gdf_pts, points_desc.crs, get_layer_source( self.mcboRasterLayer.currentLayer()), self.lneOutputFolder.text(), zone_file, control_file, size=self.dsbMovingWinSize.value()) self.cleanMessageBars(True) self.fraMain.setDisabled(False) self.iface.mainWindow().statusBar().clearMessage() self.iface.messageBar().popWidget() QApplication.restoreOverrideCursor() return super(tTestAnalysisDialog, self).accept(*args, **kwargs) except Exception as err: QApplication.restoreOverrideCursor() self.iface.mainWindow().statusBar().clearMessage() self.cleanMessageBars(True) self.fraMain.setDisabled(False) self.send_to_messagebar(str(err), level=Qgis.Critical, duration=0, addToLog=True, core_QGIS=False, showLogPanel=True, exc_info=sys.exc_info()) return False # leave dialog open
class PreVesperDialog(QtGui.QDialog, FORM_CLASS): """Dialog to prepare data and run vesper kriging""" toolKey = 'PreVesperDialog' def __init__(self, iface, parent=None): super(PreVesperDialog, self).__init__(iface.mainWindow()) # Set up the user interface from Designer. self.setupUi(self) # The qgis interface self.iface = iface self.DISP_TEMP_LAYERS = read_setting( PLUGIN_NAME + '/DISP_TEMP_LAYERS', bool) self.DEBUG = config.get_debug_mode() # Catch and redirect python errors directed at the log messages python # error tab. QgsMessageLog.instance().messageReceived.connect(errorCatcher) if not os.path.exists(TEMPDIR): os.mkdir(TEMPDIR) # Setup for validation messagebar on gui----------------------------- self.setWindowIcon(QtGui.QIcon( ':/plugins/pat/icons/icon_vesperKriging.svg')) self.validationLayout = QtGui.QFormLayout(self) # source: https://nathanw.net/2013/08/02/death-to-the-message-box-use-the-qgis-messagebar/ # Add the error messages to top of form via a message bar. # leave this message bar for bailouts self.messageBar = QgsMessageBar(self) if isinstance(self.layout(), (QtGui.QFormLayout, QtGui.QGridLayout)): # create a validation layout so multiple messages can be added and # cleaned up. self.layout().insertRow(0, self.validationLayout) self.layout().insertRow(0, self.messageBar) else: # for use with Vertical/horizontal layout box self.layout().insertWidget(0, self.messageBar) # Set Class default variables ------------------------------------- self.vesp_dict = None self.in_qgscrs = None self.dfCSV = None # this is a validation flag self.OverwriteCtrlFile = False self.cboMethod.addItems( ['High Density Kriging', 'Low Density Kriging (Advanced)']) # To allow only integers for the min number of pts. self.onlyInt = QIntValidator() self.lneMinPoint.setValidator(self.onlyInt) self.vesper_exe = check_vesper_dependency() if self.vesper_exe is None or self.vesper_exe == '': self.gbRunVesper.setTitle( 'WARNING:Vesper not found please configure using the about dialog.') self.gbRunVesper.setChecked(False) self.gbRunVesper.setCheckable(False) self.gbRunVesper.setEnabled(False) def cleanMessageBars(self, AllBars=True): """Clean Messages from the validation layout. Args: AllBars (bool): Remove All bars including. Defaults to True """ layout = self.validationLayout for i in reversed(range(layout.count())): # when it timed out the row becomes empty.... if layout.itemAt(i).isEmpty(): # .removeItem doesn't always work. so takeAt(pop) it instead item = layout.takeAt(i) elif AllBars: # ie remove all item = layout.takeAt(i) # also have to remove any widgets associated with it. if item.widget() is not None: item.widget().deleteLater() def send_to_messagebar(self, message, title='', level=QgsMessageBar.INFO, duration=5, exc_info=None, core_QGIS=False, addToLog=False, showLogPanel=False): """ Add a message to the forms message bar. Args: message (str): Message to display title (str): Title of message. Will appear in bold. Defaults to '' level (QgsMessageBarLevel): The level of message to log. Defaults to QgsMessageBar.INFO duration (int): Number of seconds to display message for. 0 is no timeout. Defaults to 5 core_QGIS (bool): Add to QGIS interface rather than the dialog addToLog (bool): Also add message to Log. Defaults to False showLogPanel (bool): Display the log panel exc_info () : Information to be used as a traceback if required """ if core_QGIS: newMessageBar = self.iface.messageBar() else: newMessageBar = QgsMessageBar(self) widget = newMessageBar.createMessage(title, message) if showLogPanel: button = QPushButton(widget) button.setText('View') button.setContentsMargins(0, 0, 0, 0) button.setFixedWidth(35) button.pressed.connect(openLogPanel) widget.layout().addWidget(button) newMessageBar.pushWidget(widget, level, duration=duration) if not core_QGIS: rowCount = self.validationLayout.count() self.validationLayout.insertRow(rowCount + 1, newMessageBar) if addToLog: if level == 1: # 'WARNING': LOGGER.warning(message) elif level == 2: # 'CRITICAL': # Add a traceback to log only for bailouts only if exc_info is not None: exc_type, exc_value, exc_traceback = sys.exc_info() mess = str(traceback.format_exc()) message = message + '\n' + mess LOGGER.critical(message) else: # INFO = 0 LOGGER.info(message) def updateCtrlFileName(self): if self.chkAutoCtrlFileName.isChecked(): ctrl_name = os.path.splitext( os.path.basename(self.lneInCSVFile.text()))[0] ctrl_name = ctrl_name.replace('_normtrimmed', '') fld = '' if self.cboKrigColumn.currentText() != '': # convert field name to something meaningful if it contains # invalid chars, ie degC fld = unidecode(self.cboKrigColumn.currentText()) # remove field from filename, then add it according to the naming # convention to avoid duplications. # flags=re.I is for a case insensitive find and replace ctrl_name = re.sub(fld, '', ctrl_name, flags=re.I) # and again with invalid characters removed. Only allow # alpha-numeric Underscores and hyphens fld = re.sub('[^A-Za-z0-9_-]+', '', fld) ctrl_name = re.sub(fld, '', ctrl_name, flags=re.I) # and again with the field truncated to 10 chars fld = fld[:10] ctrl_name = re.sub(fld, '', ctrl_name, flags=re.I) if self.cboMethod.currentText() == 'High Density Kriging': krig_type = 'HighDensity' else: krig_type = 'LowDensity' # add the chosen field name to the control filename ctrl_name = '{}_{}_{}_control'.format(ctrl_name[:20], krig_type, fld) # only allow alpha-numeric Underscores and hyphens ctrl_name = re.sub('[^A-Za-z0-9_-]+', '', ctrl_name) # replace more than one instance of underscore with a single one. # ie'file____norm__control___yield_h__' to # 'file_norm_control_yield_h_' ctrl_name = re.sub(r"_+", "_", ctrl_name) self.lneCtrlFile.setText(ctrl_name + '.txt') @QtCore.pyqtSlot(int) def on_cboKrigColumn_currentIndexChanged(self, index): if self.cboKrigColumn.currentText() != '': self.lblKrigColumn.setStyleSheet('color:black') if self.chkAutoCtrlFileName.isChecked(): self.updateCtrlFileName() @QtCore.pyqtSlot(name='on_cmdInCSVFile_clicked') def on_cmdInCSVFile_clicked(self): self.lneInCSVFile.clear() self.messageBar.clearWidgets() self.cboMethod.setCurrentIndex(0) self.dfCSV = None inFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastCSVFolder") if inFolder is None or not os.path.exists(inFolder): inFolder = read_setting(PLUGIN_NAME + '/BASE_IN_FOLDER') s = QtGui.QFileDialog.getOpenFileName(self, caption=self.tr("Select a CSV file to krige"), directory=inFolder, filter='{} (*.csv);;{} (*.*);;'.format( self.tr("Comma delimited files"), self.tr("All Files")) ) self.cleanMessageBars(self) self.lneInCSVFile.clear() # validate files first overlaps, message = self.validate_csv_grid_files(s, self.lneInGridFile.text()) if not overlaps or message is not None: self.lblInCSVFile.setStyleSheet('color:red') self.lneInCSVFile.setStyleSheet('color:red') self.send_to_messagebar(message, level=QgsMessageBar.CRITICAL, duration=0, addToLog=True, showLogPanel=True, exc_info=sys.exc_info()) return s = os.path.normpath(s) self.lblInCSVFile.setStyleSheet('color:black') self.lneInCSVFile.setStyleSheet('color:black') self.lneInCSVFile.setText(s) descCSV = describe.CsvDescribe(s) self.dfCSV = descCSV.open_pandas_dataframe(nrows=150) if len(self.dfCSV) <= 100: self.lneMinPoint.clear() QMessageBox.warning(self, 'Cannot Krige', 'Kriging is not advised for less ' 'than 100 points') self.lneMinPoint.clear() self.cboKrigColumn.clear() coord_cols = predictCoordinateColumnNames(self.dfCSV.columns) epsgcols = [col for col in self.dfCSV.columns if 'EPSG' in col.upper()] field_names = list(self.dfCSV.drop(coord_cols + epsgcols, axis=1) .select_dtypes(include=[np.number]).columns.values) self.cboKrigColumn.addItems([''] + field_names) # To set a coordinate system for vesper2raster, try and get it from # a column in the data. epsg = 0 if len(epsgcols) > 0: for col in epsgcols: if self.dfCSV.iloc[0][col] > 0: epsg = int(self.dfCSV.iloc[0][col]) break if epsg > 0: self.in_qgscrs = QgsCoordinateReferenceSystem("EPSG:{}".format(epsg)) self.lblInCRS.setText('{} - {}'.format(self.in_qgscrs.description(), self.in_qgscrs.authid())) else: self.lblInCRS.setText('Unspecified') write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastCSVFolder", os.path.dirname(s)) del descCSV self.updateCtrlFileName() @QtCore.pyqtSlot(name='on_cmdInGridFile_clicked') def on_cmdInGridFile_clicked(self): self.lneInGridFile.clear() self.messageBar.clearWidgets() inFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastVesperGridFolder") if inFolder is None or not os.path.exists(inFolder): inFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastCSVFolder") if inFolder is None or not os.path.exists(inFolder): inFolder = read_setting(PLUGIN_NAME + '/BASE_IN_FOLDER') s = QtGui.QFileDialog.getOpenFileName(self, caption=self.tr("Choose the Vesper Grid File"), directory=inFolder, filter='{} (*_v.txt);;{} (*.*);;'.format( self.tr("Vesper Grid File(s)"), self.tr("All Files")) ) self.cleanMessageBars(self) s = os.path.normpath(s) self.lneInGridFile.clear() self.lneInGridFile.setStyleSheet('color:red') self.lblInGridFile.setStyleSheet('color:red') overlaps, message = self.validate_csv_grid_files(self.lneInCSVFile.text(), s) if overlaps or message is None: self.lneInGridFile.setStyleSheet('color:black') self.lblInGridFile.setStyleSheet('color:black') if overlaps: self.lneInGridFile.setText(s) write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastVesperGridFolder", os.path.dirname(s)) else: self.send_to_messagebar(message, level=QgsMessageBar.CRITICAL, duration=0, addToLog=True, showLogPanel=True, exc_info=sys.exc_info()) @QtCore.pyqtSlot(name='on_cmdVariogramFile_clicked') def on_cmdVariogramFile_clicked(self): self.lneVariogramFile.clear() self.messageBar.clearWidgets() inFolder = read_setting( PLUGIN_NAME + "/" + self.toolKey + "/LastVariogramFolder") if inFolder is None or not os.path.exists(inFolder): inFolder = read_setting( PLUGIN_NAME + "/" + self.toolKey + "/LastVariogramFolder") if inFolder is None or not os.path.exists(inFolder): inFolder = read_setting(PLUGIN_NAME + '/BASE_IN_FOLDER') s = QtGui.QFileDialog.getOpenFileName(self, caption=self.tr("Choose the Vesper Variogram File"), directory=inFolder, filter='{} (*.txt);;{} (*.*);;'.format( self.tr("Variogram Text File(s)"), self.tr("All Files")) ) self.cleanMessageBars(self) if s == '': self.lneVariogramFile.setStyleSheet('color:red') self.lblVariogramFile.setStyleSheet('color:red') return if 'Variogram Model' not in open(s).read(): self.lneVariogramFile.setStyleSheet('color:red') self.lblVariogramFile.setStyleSheet('color:red') self.send_to_messagebar("Invalid Variogram File", level=QgsMessageBar.CRITICAL, duration=0, addToLog=True, showLogPanel=True, exc_info=sys.exc_info()) # self.lneVariogramFile.clear() return s = os.path.normpath(s) self.lblVariogramFile.setStyleSheet('color:black') self.lneVariogramFile.setStyleSheet('color:black') self.lneVariogramFile.setText(s) write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastVariogramFolder", os.path.dirname(s)) @QtCore.pyqtSlot(name='on_cmdVesperFold_clicked') def on_cmdVesperFold_clicked(self): self.messageBar.clearWidgets() if self.lneVesperFold.text() is None: outFolder = '' else: outFolder = self.lneVesperFold.text() if outFolder == '': outFolder = read_setting( PLUGIN_NAME + "/" + self.toolKey + "/LastVesperOutFolder") if outFolder is None or not os.path.exists(outFolder): outFolder = read_setting(PLUGIN_NAME + '/BASE_OUT_FOLDER') s = QtGui.QFileDialog.getExistingDirectory(self, self.tr( "Vesper processing folder. A Vesper sub-folder will be created."), outFolder, QtGui.QFileDialog.ShowDirsOnly) self.cleanMessageBars(self) if s == '' or s is None: return s = os.path.normpath(s) self.lblVesperFold.setStyleSheet('color:black') self.lneVesperFold.setStyleSheet('color:black') self.lneVesperFold.setText(s) write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastVesperOutFolder", s) @QtCore.pyqtSlot(int) def on_chkAutoCtrlFileName_stateChanged(self, state): self.updateCtrlFileName() @QtCore.pyqtSlot(name='on_cmdInCRS_clicked') def on_cmdInCRS_clicked(self): self.messageBar.clearWidgets() dlg = QgsGenericProjectionSelector(self) dlg.setMessage('Select coordinate system for the Vesper raster files') if dlg.exec_(): if dlg.selectedAuthId() != '': self.in_qgscrs = QgsCoordinateReferenceSystem(dlg.selectedAuthId()) if self.in_qgscrs == 'Unspecified' or self.in_qgscrs == '': self.lblInCRS.setText('Unspecified') self.lblOutCRS.setText('Unspecified') else: self.lblInCRS.setText('{} - {}'.format(self.in_qgscrs.description(), self.in_qgscrs.authid())) self.lblInCRS.setStyleSheet('color:black;background:transparent;') self.lblInCRSTitle.setStyleSheet('color:black') self.cleanMessageBars(self) @QtCore.pyqtSlot(int) def on_cboMethod_currentIndexChanged(self, index): self.stackedWidget.setCurrentIndex(index) self.updateCtrlFileName() if self.dfCSV is None: return if len(self.dfCSV) <= 100: self.cleanMessageBars(self) self.lneMinPoint.clear() QMessageBox.warning( self, 'Cannot Krige', 'Kriging is not advised for less than 100 points') if 'Low Density Kriging' in self.cboMethod.currentText(): if len(self.dfCSV) <= 150: # only partial file opened so open it all descCSV = describe.CsvDescribe(self.lneInCSVFile.text()) self.dfCSV = descCSV.open_pandas_dataframe() self.lblRowCount.setText("The maximum number of points is {}.".format(len(self.dfCSV))) self.lneMinPoint.setText(str(len(self.dfCSV) - 2)) else: self.lneMinPoint.clear() self.lblRowCount.setText('') def parse_variogram_file(self): vario_values = {} for line in open(self.lneVariogramFile.text()): # reset some text to control file tags line = line.replace('C0', 'CO') line = line.replace('Variogram Model', 'modtyp').strip() if set(':=.').intersection(set(line)): for ea in ['=', ':', ' ']: if ea in line: key, val = line.split(ea, 1) break # sort out the numerics from the strings try: key = int(float(key)) if int( float(key)) == float(key) else float(key) except ValueError: key = key.strip() try: val = int(float(val)) if int( float(val)) == float(val) else float(val) except ValueError: val = val.strip() # only return keys required for the control file. # and key in VESPER_OPTIONS.keys(): if isinstance(key, basestring): vario_values[key] = val return vario_values def validate_csv_grid_files(self, csv_file, grid_file, show_msgbox=True): """ validate the csv and grid files and check for overlap assuming that they are of the same coordinate system if True then message will be blank else a message will be generated """ overlaps = False if csv_file != '': if not os.path.exists(csv_file): return False, 'CSV file does not exist' else: try: df_csv = pd.read_csv(csv_file) csvX, csvY = predictCoordinateColumnNames(df_csv.columns) csv_bbox = box(df_csv[csvX].min(), df_csv[csvY].min(), df_csv[csvX].max(), df_csv[csvY].max()) except Exception as err: self.lblInCSVFile.setStyleSheet('color:red') self.lneInCSVFile.setStyleSheet('color:red') return False, 'Invalid CSV file' if grid_file != '': if not os.path.exists(grid_file): return False, 'Grid file does not exist' else: try: df_grid = pd.read_table(grid_file, names=['X', 'Y'], delimiter=' ', skipinitialspace=True) grid_bbox = box(df_grid['X'].min(), df_grid['Y'].min(), df_grid['X'].max(), df_grid['Y'].max()) except Exception as err: self.lblInGridFile.setStyleSheet('color:red') self.lneInGridFile.setStyleSheet('color:red') return False, 'Invalid VESPER grid file' # only continue if both inputs aren't blank if csv_file == '' or grid_file == '': # if one or the other is blank keep validate as true or it wont write to the GUI return True, None if csv_file == grid_file: return False, "VESPER grid file and CSV file cannot be the same file" # now we can check for overlap if self.in_qgscrs: epsg = self.in_qgscrs.authid() else: epsg = '' overlaps = check_for_overlap(csv_bbox.to_wkt(), grid_bbox.to_wkt(), epsg, epsg) if not overlaps: message = 'There is no overlap between the VESPER Grid file and the CSV file.\n' \ 'Please check input files and coordinate systems' if show_msgbox: QMessageBox.warning(self, 'No Overlap', message) self.lblInGridFile.setStyleSheet('color:red') self.lneInGridFile.setStyleSheet('color:red') self.lblInCSVFile.setStyleSheet('color:red') self.lneInCSVFile.setStyleSheet('color:red') else: message = None self.lblInGridFile.setStyleSheet('color:black') self.lneInGridFile.setStyleSheet('color:black') self.lblInCSVFile.setStyleSheet('color:black') self.lneInCSVFile.setStyleSheet('color:black') return overlaps, message def validate(self): """Check to see that all required gui elements have been entered and are valid.""" try: self.messageBar.clearWidgets() self.cleanMessageBars(AllBars=True) errorList = [] if self.lneInCSVFile.text() is None or self.lneInCSVFile.text() == '': self.lblInCSVFile.setStyleSheet('color:red') self.lneInCSVFile.setStyleSheet('color:red') errorList.append(self.tr("Select an input csv data file")) elif not os.path.exists(self.lneInCSVFile.text()): self.lblInCSVFile.setStyleSheet('color:red') self.lneInCSVFile.setStyleSheet('color:red') errorList.append(self.tr("Input csv data file does not exist")) else: self.lblInCSVFile.setStyleSheet('color:black') self.lneInCSVFile.setStyleSheet('color:black') if self.lneInGridFile.text() is None or self.lneInGridFile.text() == '': self.lblInGridFile.setStyleSheet('color:red') self.lneInGridFile.setStyleSheet('color:red') errorList.append(self.tr("Select a Vesper grid file")) elif not os.path.exists(self.lneInGridFile.text()): self.lblInGridFile.setStyleSheet('color:red') self.lneInGridFile.setStyleSheet('color:red') errorList.append(self.tr("Vesper grid file does not exists.")) else: self.lblInGridFile.setStyleSheet('color:black') self.lneInGridFile.setStyleSheet('color:black') if self.in_qgscrs is None: self.lblInCRSTitle.setStyleSheet('color:red') self.lblInCRS.setStyleSheet('color:red;background:transparent;') errorList.append(self.tr("Select a coordinate system")) else: self.lblInCRSTitle.setStyleSheet('color:black') self.lblInCRS.setStyleSheet('color:black;background:transparent;') pass_check, message = self.validate_csv_grid_files(self.lneInCSVFile.text(), self.lneInGridFile.text()) if not pass_check: errorList.append(self.tr(message)) # errorList.append(self.tr("Input csv file and grid file do not overlap. Could be " # "due to differing coordinate systems or invalid files")) if self.cboKrigColumn.currentText() == '': self.lblKrigColumn.setStyleSheet('color:red') errorList.append(self.tr("Select a column to krige")) else: self.lblKrigColumn.setStyleSheet('color:black') if self.cboMethod.currentText() != 'High Density Kriging': if self.lneVariogramFile.text() is None or self.lneVariogramFile.text() == '': self.lblVariogramFile.setStyleSheet('color:red') self.lneVariogramFile.setStyleSheet('color:red') errorList.append(self.tr("Select a variogram text file")) elif not os.path.exists(self.lneVariogramFile.text()): self.lblVariogramFile.setStyleSheet('color:red') self.lneVariogramFile.setStyleSheet('color:red') errorList.append(self.tr("Variogram text file does not exists.")) else: self.lblVariogramFile.setStyleSheet('color:black') self.lneVariogramFile.setStyleSheet('color:black') if int(self.lneMinPoint.text()) >= len(self.dfCSV): self.lneMinPoint.setStyleSheet('color:red') self.lneMinPoint.setStyleSheet('color:red') errorList.append( self.tr("Minimum number of points should be at least " "2 less than the dataset count")) else: self.lneMinPoint.setStyleSheet('color:black') self.lneMinPoint.setStyleSheet('color:black') if self.lneVesperFold.text() == '': self.lblVesperFold.setStyleSheet('color:red') errorList.append(self.tr("Select output Vesper data folder")) elif not os.path.exists(self.lneVesperFold.text()): self.lneVesperFold.setStyleSheet('color:red') errorList.append( self.tr("Output Vesper data folder does not exist")) else: self.lblVesperFold.setStyleSheet('color:black') self.lneVesperFold.setStyleSheet('color:black') ctrl_file = os.path.join(self.lneVesperFold.text(), self.lneCtrlFile.text()) if os.path.exists(ctrl_file): message = 'Vesper Control File {} already exists. Do you want to' \ ' overwrite?'.format(self.lneCtrlFile.text()) reply = QMessageBox.question( self, 'Control File', message, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: self.overwrite_ctrl_file = True self.lblVesperFold.setStyleSheet('color:black') else: self.overwrite_ctrl_file = False self.lblCtrlFile.setStyleSheet('color:red') errorList.append( self.tr("Output control file exists please choose a different name")) if len(errorList) > 0: raise ValueError(errorList) except ValueError as e: self.cleanMessageBars(True) if len(errorList) > 0: for i, ea in enumerate(errorList): self.send_to_messagebar( unicode(ea), level=QgsMessageBar.WARNING, duration=(i + 1) * 5) return False return True def accept(self, *args, **kwargs): if not self.validate(): return False try: self.cleanMessageBars(True) message = '- and run VESPER' if self.gbRunVesper.isChecked() else '' LOGGER.info('{st}\nProcessing {} {}'.format( self.windowTitle(), message, st='*' * 50)) # Add settings to log settingsStr = 'Parameters:---------------------------------------' settingsStr += '\n {:30}\t{}'.format('Data File:', self.lneInCSVFile.text()) settingsStr += '\n {:30}\t{}'.format('Krige Column:', self.cboKrigColumn.currentText()) settingsStr += '\n {:30}\t{}'.format('Grid File:', self.lneInGridFile.text()) settingsStr += '\n {:30}\t{}'.format('Output Vesper Folder:', self.lneVesperFold.text()) settingsStr += '\n {:30}\t{}'.format(self.cboMethod.currentText(), '') if self.cboMethod.currentText() == 'High Density Kriging': settingsStr += '\n {:30}\t{}'.format('Block Kriging Size:', int(self.dsbBlockKrigSize.value())) else: settingsStr += '\n {:30}\t{}'.format('Variogram File:', self.lneVariogramFile.text()) settingsStr += '\n {:30}\t{}'.format('Min Point Number:', self.lneMinPoint.text()) settingsStr += '\n {:30}\t{}'.format( 'Display Vesper Graphics:', self.chkDisplayGraphics.isChecked()) settingsStr += '\n {:30}\t{}'.format( 'Run Vesper Now:', self.gbRunVesper.isChecked()) if self.gbRunVesper.isChecked(): settingsStr += '\n {:30}\t{}'.format('Import Vesper Files to Rasters:', self.chkVesper2Raster.isChecked()) if self.chkVesper2Raster.isChecked(): settingsStr += '\n {:30}\t{}'.format( 'Vesper Files Coordinate System:', self.lblInCRS.text()) LOGGER.info(settingsStr) # get a fresh dataframe for the input csv file self.dfCSV = pd.read_csv(self.lneInCSVFile.text()) vc = VesperControl() if self.cboMethod.currentText() == 'High Density Kriging': vc.update(xside=int(self.dsbBlockKrigSize.value()), yside=int(self.dsbBlockKrigSize.value())) else: # from the variogram text file find and update the control file keys vario = self.parse_variogram_file() vesp_keys = {key: val for key, val in vario.items() if key in vc} vc.update(vesp_keys) # apply the other keys. vc.update({'jpntkrg': 1, 'jlockrg': 0, 'minpts': int(self.lneMinPoint.text()), 'maxpts': len(self.dfCSV), 'jcomvar': 0, }) epsg = int(self.in_qgscrs.authid().replace('EPSG:', '')) bat_file, ctrl_file = prepare_for_vesper_krige(self.dfCSV, self.cboKrigColumn.currentText(), self.lneInGridFile.text(), self.lneVesperFold.text(), control_textfile=self.lneCtrlFile.text(), coord_columns=[], epsg=epsg, display_graphics=self.chkDisplayGraphics.isChecked(), control_options=vc) epsg = 0 if self.in_qgscrs is not None and self.chkVesper2Raster.isChecked(): epsg = int(self.in_qgscrs.authid().replace('EPSG:', '')) if self.gbRunVesper.isChecked(): # Add to vesper queue self.vesp_dict = {'control_file': ctrl_file, 'epsg': epsg} else: message = 'Successfully created files for Vesper kriging. ' \ 'The control file is {}'.format(ctrl_file) self.send_to_messagebar(message, level=QgsMessageBar.SUCCESS, duration=0, addToLog=True, core_QGIS=True) LOGGER.info('Successfully created files for Vesper kriging') QtGui.qApp.restoreOverrideCursor() return super(PreVesperDialog, self).accept(*args, **kwargs) except Exception as err: QtGui.qApp.restoreOverrideCursor() self.cleanMessageBars(True) self.send_to_messagebar(str(err), level=QgsMessageBar.CRITICAL, duration=0, addToLog=True, showLogPanel=True, exc_info=sys.exc_info()) return False
class ImgUploaderWizard(QtGui.QWizard, FORM_CLASS): def __init__(self, iface, settings, parent=None): """Constructor.""" super(ImgUploaderWizard, 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.iface = iface self.setupUi(self) self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint) # Message bars need to be attached to pages, since the wizard object # does not have a layout. It doesn't work to attach the same bar # object to all pages (it is only shown in the last one). The only way # I could make it work was to create different QgsMessageBar objects, # one per page, but it is very to keep track of those references # along the code. It is messy, there should be a better solution. self.bar0 = QgsMessageBar() self.bar0.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(0).layout().addWidget(self.bar0) self.bar1 = QgsMessageBar() self.bar1.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(1).layout().addWidget(self.bar1) self.bar2 = QgsMessageBar() self.bar2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(2).layout().addWidget(self.bar2) self.setButtonText(QtGui.QWizard.CustomButton1, self.tr("&Start upload")) self.setOption(QtGui.QWizard.HaveCustomButton1, True) self.button(QWizard.CustomButton1).setVisible(False) self.settings = settings # Initialize the upload progressbar self.upPrgWin = None # Initialize layers and default settings self.loadLayers() self.loadMetadataSettings() self.loadStorageSettings() self.loadOptionsSettings() self.setStorageType() # register event handlers self.button(QWizard.BackButton).clicked.connect(self.previousPage) self.button(QWizard.NextButton).clicked.connect(self.nextPage) self.button(QWizard.FinishButton).clicked.connect(self.finishWizard) self.button(QWizard.CancelButton).clicked.connect(self.cancelWizard) # Imagery connections (wizard page 1) self.layers_tool_button.clicked.connect(self.loadLayers) self.file_tool_button.clicked.connect(self.selectFile) self.add_source_button.clicked.connect(self.addSources) self.remove_source_button.clicked.connect(self.removeSources) self.up_source_button.clicked.connect(self.upSource) self.down_source_button.clicked.connect(self.downSource) # Metadata connections (wizard page 2) self.sense_start_edit.setCalendarPopup(1) self.sense_start_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.sense_end_edit.setCalendarPopup(1) self.sense_end_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.default_button.clicked.connect(self.loadMetadataSettings) self.clean_button.clicked.connect(self.cleanMetadataSettings) self.save_button.clicked.connect(self.saveMetadata) # Temporarily hide the reload button self.reload_button.hide() self.reload_button.clicked.connect(self.loadSavedMetadata) # temporarily disable convert_format_check_box and combobox self.convert_format_check_box.setEnabled(False) self.format_combo_box.addItem('n.a.') self.format_combo_box.setEnabled(False) # Upload tab connections (wizard page 3) # self.storage_combo_box.currentIndexChanged.connect(self.enableSpecify) self.storage_type_combo_box.currentIndexChanged.connect(self.setStorageType) self.customButtonClicked.connect(self.startUpload) # self.button(QWizard.CustomButton1).clicked.connect(self.startUpload) self.toggleTokenRequestForm() self.notify_oam_check.stateChanged.connect(self.toggleTokenRequestForm) # temporarily disable trigger_tiling_check self.notify_oam_check.setEnabled(False) # handlers for navigation def nextPage(self): if self.currentId() == 1: # print "Page ID: " + str(self.currentId()) # self.bar1.clearWidgets() pass elif self.currentId() == 2: # print "Page ID: " + str(self.currentId()) # self.bar2.clearWidgets() self.loadMetadataReviewBox() self.button(QWizard.CustomButton1).setVisible(True) else: pass def previousPage(self): if self.currentId() == 0: # print "Page ID: " + str(self.currentId()) # self.bar0.clearWidgets() pass elif self.currentId() == 1: # print "Page ID: " + str(self.currentId()) # self.bar1.clearWidgets() self.button(QWizard.CustomButton1).setVisible(False) else: pass def finishWizard(self): # print "finish wizard button was clicked." pass def cancelWizard(self): # print "cancel wizard button was clicked." # need to display QMessageBox if self.upPrgWin is not None: self.upPrgWin.cancelAllUploads() # event handling for wizard page 1 def loadLayers(self): all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if not self.layers_list_widget.findItems( layer.name(), Qt.MatchExactly): if not self.sources_list_widget.findItems( layer.name(), Qt.MatchExactly): item = QListWidgetItem() item.setText(layer.name()) item.setData( Qt.UserRole, os.path.abspath(layer.dataProvider().dataSourceUri())) self.layers_list_widget.addItem(item) # print(item.data(Qt.UserRole)) def selectFile(self): selected_file = QFileDialog.getOpenFileName( self, 'Select imagery file', os.path.expanduser("~")) self.source_file_edit.setText(selected_file) def addSources(self): filename = self.source_file_edit.text() selected_layers = self.layers_list_widget.selectedItems() if not filename and not selected_layers: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'Either a layer or file should be selected to be added', level=QgsMessageBar.WARNING) else: added = False if filename: result_val = validate_file(filename) if result_val["val"]: if not self.sources_list_widget.findItems( filename, Qt.MatchExactly): item = QListWidgetItem() item.setText(os.path.basename(filename)) item.setData(Qt.UserRole, os.path.abspath(filename)) print(item.data(Qt.UserRole)) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) self.source_file_edit.setText('') added = True else: msg = result_val["msg"] self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", msg, level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage( "CRITICAL", msg, level=QgsMessageLog.INFO) if selected_layers: for item in selected_layers: if validate_layer(item.text(), self.iface): if not self.sources_list_widget.findItems( item.text(), Qt.MatchExactly): self.layers_list_widget.takeItem( self.layers_list_widget.row(item)) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) added = True else: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "Vector layers cannot be selected for upload", level=QgsMessageBar.CRITICAL) if added: self.bar0.clearWidgets() self.bar0.pushMessage( 'INFO', 'Source(s) added to the upload queue', level=QgsMessageBar.INFO) def removeSources(self): selected_sources = self.sources_list_widget.selectedItems() added_sources = self.added_sources_list_widget.selectedItems() if selected_sources: for item in selected_sources: self.sources_list_widget.takeItem( self.sources_list_widget.row(item)) added_item = self.added_sources_list_widget.findItems( item.text(), Qt.MatchExactly) if added_item: self.added_sources_list_widget.takeItem( self.added_sources_list_widget.row(added_item[0])) all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if item.text() == layer.name(): if not self.layers_list_widget.findItems( item.text(), Qt.MatchExactly): self.layers_list_widget.addItem(item) else: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'An imagery source must be selected to be removed', level=QgsMessageBar.WARNING) def upSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position > 0: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position - 1, item) item.setSelected(1) def downSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position < self.sources_list_widget.count() - 1: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position + 1, item) item.setSelected(1) # event handling for wizard page 2 # load default values def loadMetadataSettings(self): self.settings.beginGroup("Metadata") self.base_uuid_edit.setText(self.settings.value('BASE_UUID')) self.title_edit.setText(self.settings.value('TITLE')) if self.settings.value('PLATFORM') is None: self.platform_combo_box.setCurrentIndex(0) else: self.platform_combo_box.setCurrentIndex( int(self.settings.value('PLATFORM'))) self.sensor_edit.setText(self.settings.value('SENSOR')) self.sensor_edit.setCursorPosition(0) self.sense_start_edit.setDate(QDateTime.fromString( self.settings.value('SENSE_START'), Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_START'), Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).time()) self.provider_edit.setText(self.settings.value('PROVIDER')) self.provider_edit.setCursorPosition(0) self.contact_edit.setText(self.settings.value('CONTACT')) self.contact_edit.setCursorPosition(0) self.settings.endGroup() def cleanMetadataSettings(self): self.base_uuid_edit.setText('') self.title_edit.setText('') self.platform_combo_box.setCurrentIndex(0) self.sensor_edit.setText('') self.sense_start_edit.setDate( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).time()) self.provider_edit.setText('') self.contact_edit.setText('') self.license_check_box.setCheckState(0) self.reproject_check_box.setCheckState(0) def saveMetadata(self): selected_layers = self.added_sources_list_widget.selectedItems() if not selected_layers: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'One or more source imagery should be selected to have ' + 'the metadata saved', level=QgsMessageBar.WARNING) else: if not self.reproject_check_box.isChecked(): self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Metadata extraction is being processed...', level=QgsMessageBar.INFO) # num_selected_layers = len(selected_layers) for each_layer in selected_layers: file_abspath = each_layer.data(Qt.UserRole) # print("file name: " + str(file_abspath)) self.exportMetaAsTextFile(file_abspath) self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Metadata for the selected sources were saved', level=QgsMessageBar.INFO) else: qMsgBox = QMessageBox() qMsgBox.setWindowTitle("Confirmation") qMsgBox.setText("You checked the reprojection option, " + "which can require significant amount " + "of time. Are you sure to continue?") qMsgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) # qMsgBox.setDefaultButton(QMessageBox.Cancel) if qMsgBox.exec_() == QMessageBox.Ok: """Open python console""" """ pluginMenu = self.iface.pluginMenu() #print(repr(pluginMenu)) for action in pluginMenu.actions(): #print(action.text()) if 'Python Console' in action.text(): action.trigger() """ # num_selected_layers = len(selected_layers) self.reprojectionCmdWindows = [] self.numReprojectionCmdWindows = len(selected_layers) self.numReprojectionFinished = 0 index = 0 self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Reprojecting files: {0}th image '.format( self.numReprojectionFinished + 1 ) + 'out of {0} is being processed...'.format( self.numReprojectionCmdWindows ), level=QgsMessageBar.INFO) for each_layer in selected_layers: file_abspath = each_layer.data(Qt.UserRole) if "EPSG3857" not in file_abspath: reprojected_file_abspath = os.path.splitext( file_abspath)[0] + '_EPSG3857.tif' layerName = each_layer.text() cmd = "gdalwarp" optionsInList = ["-of", "GTiff", "-overwrite", "-t_srs", "epsg:3857"] self.reprojectionCmdWindows.append( ReprojectionCmdWindow('Reprojection', cmd, optionsInList, file_abspath, reprojected_file_abspath, index, layerName)) self.reprojectionCmdWindows[index].finished.connect( self.updateReprojectionProgress) # self.reprojectionCmdWindows[ # index].cancelled.connect( # self.cancelSingleReprojectionProcess) self.reprojectionCmdWindows[index].show() self.reprojectionCmdWindows[index].move( 20 + index * 20, 20 + index * 20) # self.reprojectionCmdWindows[ # index].startCommandThread() self.reprojectionCmdWindows[index].run() self.reprojectionCmdWindows[index].activateWindow() index += 1 else: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'Suffix _EPSG3857 is already included in ' + 'one (some) of the selected files...', level=QgsMessageBar.WARNING) # break def cancelSingleReprojectionProcess(self, index): # print("Reprojection was cancelled. Index: {0}".format(str(index))) pass def updateReprojectionProgress(self, index): """ consider the use of callback function here. """ reprojectedFileAbsPath = self.reprojectionCmdWindows[ index].getReprojectedFileAbsPath() reprojectedLayerName = self.reprojectionCmdWindows[ index].getReprojectedLayerName() fileAbsPath = self.reprojectionCmdWindows[ index].getFileAbsPath() layerName = self.reprojectionCmdWindows[ index].getLayerName() # print('Reprojection completed for:') # print('File Path: {0}'.format(fileAbsPath)) # print('Layer Name: {0}'.format(layerName)) """ # rlayer = QgsRasterLayer(reprojectedFileAbsPath, # reprojectedLayerName) # QgsMapLayerRegistry.instance().addMapLayer(rlayer) # print(str(rlayer.isValid())) """ self.iface.addRasterLayer( reprojectedFileAbsPath, reprojectedLayerName) # print('Insert reprojected raster layer. Layer name:') # print('File Path: {0}'.format(reprojectedFileAbsPath)) # print('Layer Name: {0}'.format(reprojectedLayerName)) self.exportMetaAsTextFile(reprojectedFileAbsPath) # print('Export Metadata into text file...') # self.activateWindow() items_for_remove = self.added_sources_list_widget.findItems( layerName, Qt.MatchExactly) self.added_sources_list_widget.takeItem( self.added_sources_list_widget.row(items_for_remove[0])) items_for_remove = self.sources_list_widget.findItems( layerName, Qt.MatchExactly) self.sources_list_widget.takeItem( self.sources_list_widget.row(items_for_remove[0])) # self.layers_list_widget.addItem(items_for_remove[0]) item = QListWidgetItem() item.setText(reprojectedLayerName) item.setData(Qt.UserRole, os.path.abspath(reprojectedFileAbsPath)) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) self.loadLayers() self.numReprojectionFinished += 1 if self.numReprojectionFinished < self.numReprojectionCmdWindows: self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Reprojecting files: {0}th image '.format( self.numReprojectionFinished + 1) + 'out of {0} is being processed...'.format( self.numReprojectionCmdWindows), level=QgsMessageBar.INFO) else: self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Metadata for the selected sources were saved', level=QgsMessageBar.INFO) def createFootPrint(self): # probably need to insert the codes to create and insert # the detailed footprint layer here pass def exportMetaAsTextFile(self, file_abspath): # get metadata from GUI, and store them in a dictionary metaInputInDict = {} temp_filename = os.path.basename(file_abspath) strUuid = '{0}{1}'.format(self.base_uuid_edit.text(), temp_filename) metaInputInDict['uuid'] = strUuid metaInputInDict['title'] = self.title_edit.text() metaInputInDict['platform'] = \ self.platform_combo_box.currentText() metaInputInDict['acquisition_start'] = \ self.sense_start_edit.dateTime().toString(Qt.ISODate) metaInputInDict['acquisition_end'] = \ self.sense_end_edit.dateTime().toString(Qt.ISODate) metaInputInDict['provider'] = \ self.provider_edit.text() metaInputInDict['contact'] = self.contact_edit.text() properties = {} properties['sensor'] = self.sensor_edit.text() properties['thumbnail'] = strUuid + ".thumb.png" metaInputInDict['properties'] = properties # extract metadata from GeoTiff, # and merge with the metadata from textbox imgMetaHdlr = ImgMetadataHandler(file_abspath) imgMetaHdlr.extractMetaInImagery() metaForUpload = dict( imgMetaHdlr.getMetaInImagery().items() + metaInputInDict.items()) strMetaForUpload = str(json.dumps(metaForUpload)) json_file_abspath = file_abspath + '_meta.json' # print json_file_abspath f = open(json_file_abspath, 'w') f.write(strMetaForUpload) f.close() return(True) def loadSavedMetadata(self): qMsgBox = QMessageBox() qMsgBox.setText('Under construction:\nMessage from ' + 'loadSavedMetadata function.') qMsgBox.exec_() # event handling for wizard page 3 # please also see startUpload function # for event handling of Start Upload button """ def enableSpecify(self): if self.storage_combo_box.currentIndex() == 1: self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) else: self.specify_label.setEnabled(0) self.specify_edit.setText('') self.specify_edit.setEnabled(0) """ """ need to simplify this part later """ def setAWSS3Edit(self): self.aws_bucket_name_edit.setVisible(True) self.aws_bucket_name_label.setVisible(True) self.aws_key_id_edit.setVisible(True) self.aws_key_id_label.setVisible(True) self.aws_secret_key_edit.setVisible(True) self.aws_secret_key_label.setVisible(True) self.google_client_secret_file_edit.setVisible(False) self.google_client_secret_file_label.setVisible(False) self.google_application_name_edit.setVisible(False) self.google_application_name_label.setVisible(False) self.dropbox_access_token_edit.setVisible(False) self.dropbox_access_token_label.setVisible(False) def setGoogleDriveEdit(self): self.aws_bucket_name_edit.setVisible(False) self.aws_bucket_name_label.setVisible(False) self.aws_key_id_edit.setVisible(False) self.aws_key_id_label.setVisible(False) self.aws_secret_key_edit.setVisible(False) self.aws_secret_key_label.setVisible(False) self.google_client_secret_file_edit.setVisible(True) self.google_client_secret_file_label.setVisible(True) self.google_application_name_edit.setVisible(True) self.google_application_name_label.setVisible(True) self.dropbox_access_token_edit.setVisible(False) self.dropbox_access_token_label.setVisible(False) def setDroboxEdit(self): self.aws_bucket_name_edit.setVisible(False) self.aws_bucket_name_label.setVisible(False) self.aws_key_id_edit.setVisible(False) self.aws_key_id_label.setVisible(False) self.aws_secret_key_edit.setVisible(False) self.aws_secret_key_label.setVisible(False) self.google_client_secret_file_edit.setVisible(False) self.google_client_secret_file_label.setVisible(False) self.google_application_name_edit.setVisible(False) self.google_application_name_label.setVisible(False) self.dropbox_access_token_edit.setVisible(True) self.dropbox_access_token_label.setVisible(True) def setStorageType(self): if self.storage_type_combo_box.currentIndex() == 0: self.setAWSS3Edit() elif self.storage_type_combo_box.currentIndex() == 1: self.setGoogleDriveEdit() elif self.storage_type_combo_box.currentIndex() == 2: self.setDroboxEdit() def toggleTokenRequestForm(self): state = False if self.notify_oam_check.isChecked(): state = True self.btn_request_token.setEnabled(state) self.token_edit.setEnabled(state) self.token_label.setEnabled(state) self.uploader_name_edit.setEnabled(state) self.uploader_name_label.setEnabled(state) self.uploader_email_edit.setEnabled(state) self.uploader_email_label.setEnabled(state) # temporarily disable textboxes self.token_edit.setText('n.a.') self.uploader_name_edit.setText('n.a.') self.uploader_email_edit.setText('n.a.') def loadStorageSettings(self): self.settings.beginGroup("Storage") """ bucket = self.settings.value('AWS_BUCKET_NAME') storage_index = self.storage_combo_box.findText( bucket, Qt.MatchExactly) if not storage_index == -1: self.storage_combo_box.setCurrentIndex(storage_index) else: self.storage_combo_box.setCurrentIndex( self.storage_combo_box.findText(self.tr('other...'))) self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) self.specify_edit.setText(self.settings.value('S3_BUCKET_NAME')) """ if self.settings.value('DEFAULT_STORAGE') != None: self.storage_type_combo_box.setCurrentIndex( int(self.settings.value('DEFAULT_STORAGE'))) else: self.storage_type_combo_box.setCurrentIndex(0) self.aws_bucket_name_edit.setText( self.settings.value('AWS_BUCKET_NAME')) self.aws_bucket_name_edit.setCursorPosition(0) self.aws_key_id_edit.setText( self.settings.value('AWS_ACCESS_KEY_ID')) self.aws_key_id_edit.setCursorPosition(0) self.aws_secret_key_edit.setText( self.settings.value('AWS_SECRET_ACCESS_KEY')) self.aws_secret_key_edit.setCursorPosition(0) self.google_client_secret_file_edit.setText( self.settings.value('GOOGLE_CLIENT_SECRET_FILE')) self.google_client_secret_file_edit.setCursorPosition(0) self.google_application_name_edit.setText( self.settings.value('GOOGLE_APPLICATION_NAME')) self.google_application_name_edit.setCursorPosition(0) self.dropbox_access_token_edit.setText( self.settings.value('DROPBOX_ACCESS_TOKEN')) self.dropbox_access_token_edit.setCursorPosition(0) self.settings.endGroup() # temporarily disable combobox self.storage_type_combo_box.setCurrentIndex(0) self.storage_type_combo_box.setEnabled(False) def loadOptionsSettings(self): self.settings.beginGroup("Options") """ Boolean values are converted into string and lower case for 'if' statement, since PyQt sometimes returns 'true', just like C++, instead of 'True', Python style. Maybe we can use integer values (0 or 1), instead of using string. """ if str(self.settings.value('LICENSE')).lower() == 'true': self.license_check_box.setCheckState(2) if str(self.settings.value('REPROJECT')).lower() == 'true': self.reproject_check_box.setCheckState(2) if str(self.settings.value('NOTIFY_OAM')).lower() == 'true': self.notify_oam_check.setCheckState(2) self.settings.endGroup() # temporarily disable notify_oam_check box self.notify_oam_check.setCheckState(0) self.notify_oam_check.setEnabled(False) def loadMetadataReviewBox(self): json_file_abspaths = [] for index in xrange(self.sources_list_widget.count()): file_abspath = str( self.sources_list_widget.item(index).data(Qt.UserRole)) json_file_abspath = '' json_file_abspath = file_abspath + '_meta.json' # print str(json_file_abspath) json_file_abspaths.append(json_file_abspath) temp = QTemporaryFile() temp.open() for json_file_abspath in json_file_abspaths: if os.path.exists(json_file_abspath): with open(json_file_abspath) as infile: temp.write(infile.read() + '\n\n') else: temp.write('%s was not found. Please save it in advance.\n\n' % str(json_file_abspath)) temp.flush() temp.seek(0) stream = QTextStream(temp) self.review_metadata_box.setText(stream.readAll()) # event handler for upload button def startUpload(self): upload_options = [] upload_file_abspaths = [] # get the information of upload options if self.notify_oam_check.isChecked(): upload_options.append("notify_oam") if not self.license_check_box.isChecked(): self.bar2.clearWidgets() self.bar2.pushMessage( 'CRITICAL', 'Please check the lisence term.', level=QgsMessageBar.WARNING) else: for index in xrange(self.sources_list_widget.count()): upload_file_abspath = str( self.added_sources_list_widget.item(index).data(Qt.UserRole)) # create thumbnail ThumbnailCreation.createThumbnail(upload_file_abspath) upload_file_abspaths.append(upload_file_abspath) if self.upPrgWin is None: self.upPrgWin = UploadProgressWindow() self.upPrgWin.connected.connect(self.displayConnectionResult) # self.upPrgWin.progress.connect(self.updateProgress) self.upPrgWin.started.connect(self.updateListWidgets) self.upPrgWin.finished.connect(self.finishUpload) if self.storage_type_combo_box.currentIndex() == 0: # get login information for bucket bucket_name = None bucket_key = None bucket_secret = None #if self.storage_combo_box.currentIndex() == 0: # bucket_name = 'oam-qgis-plugin-test' #else: bucket_name = str(self.aws_bucket_name_edit.text()) #if not bucket_name: # self.bar2.clearWidgets() # self.bar2.pushMessage( # 'WARNING', # 'The bucket for upload must be provided', # level=QgsMessageBar.WARNING) bucket_key = str(self.aws_key_id_edit.text()) bucket_secret = str(self.aws_secret_key_edit.text()) self.upPrgWin.startUpload('aws', upload_file_abspaths, upload_options, awsBucketKey=bucket_key, awsBucketSecret=bucket_secret, awsBucketName=bucket_name) elif self.storage_type_combo_box.currentIndex() == 1: qMsgBox = QMessageBox() qMsgBox.setText('Under construction:\nMessage from ' + 'Google drive module.') qMsgBox.exec_() elif self.storage_type_combo_box.currentIndex() == 2: qMsgBox = QMessageBox() qMsgBox.setText('Under construction:\nMessage from ' + 'Dropbox module.') qMsgBox.exec_() self.button(QWizard.FinishButton).setVisible(False) # print(self.isTopLevel()) def displayConnectionResult(self, didStart): if didStart: self.bar2.clearWidgets() self.bar2.pushMessage( 'INFO', 'Uploading imagery and metadata...', level=QgsMessageBar.INFO) else: self.bar2.clearWidgets() self.bar2.pushMessage( 'CRITICAL', 'Connection to S3 server failed.', level=QgsMessageBar.CRITICAL) def updateListWidgets(self, fileAbsPath): # print('fileAbsPath: ' + fileAbsPath) # print(str(self.added_sources_list_widget.count())) for index in xrange(0, self.added_sources_list_widget.count()): refFileAbsPath = str( self.added_sources_list_widget.item(index).data(Qt.UserRole)) # print('refFileAbsPath: ' + refFileAbsPath) if fileAbsPath == refFileAbsPath: self.added_sources_list_widget.takeItem(index) break # print(str(self.sources_list_widget.count())) for index in xrange(0, self.sources_list_widget.count()): refFileAbsPath = str( self.sources_list_widget.item(index).data(Qt.UserRole)) # print('refFileAbsPath: ' + refFileAbsPath) if fileAbsPath == refFileAbsPath: self.sources_list_widget.takeItem(index) break self.loadMetadataReviewBox() self.loadLayers() def finishUpload(self, numSuccess, numCancelled, numFailed): self.button(QWizard.FinishButton).setVisible(True) self.bar2.clearWidgets() self.bar2.pushMessage( 'Upload Result', 'Success:{0} Cancel:{1} Fail:{2}'.format( numSuccess, numCancelled, numFailed), level=QgsMessageBar.INFO)
class DialogExportData(QDialog, DIALOG_UI): on_result = pyqtSignal( bool) # whether the tool was run successfully or not ValidExtensions = ['xtf', 'itf', 'gml', 'xml'] current_row_schema = 0 def __init__(self, iface, qgis_utils, conn_manager, context): QDialog.__init__(self) self.setupUi(self) QgsGui.instance().enableAutoGeometryRestore(self) self.iface = iface self.conn_manager = conn_manager self.db_source = context.get_db_sources()[0] self.db = self.conn_manager.get_db_connector_from_source( self.db_source) self.qgis_utils = qgis_utils self.logger = Logger() self.java_utils = JavaUtils() self.java_utils.download_java_completed.connect( self.download_java_complete) self.java_utils.download_java_progress_changed.connect( self.download_java_progress_change) self.base_configuration = BaseConfiguration() self.ilicache = IliCache(self.base_configuration) self.ilicache.refresh() self._dbs_supported = ConfigDbSupported() self._running_tool = False # There may be 1 case where we need to emit a db_connection_changed from the Export 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 ED 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_save_file_selector( self.xtf_file_line_edit, title=QCoreApplication.translate("DialogExportData", "Save in XTF Transfer File"), file_filter=QCoreApplication.translate( "DialogExportData", "XTF Transfer File (*.xtf);;Interlis 1 Transfer File (*.itf);;XML (*.xml);;GML (*.gml)" ), extension='.xtf', extensions=['.' + ext for ext in self.ValidExtensions])) self.xtf_file_browse_button.clicked.connect( self.xtf_browser_opened_to_true) self.xtf_browser_was_opened = False self.validators = Validators() fileValidator = FileValidator( pattern=['*.' + ext for ext in self.ValidExtensions], allow_non_existing=True) self.xtf_file_line_edit.setPlaceholderText( QCoreApplication.translate("DialogExportData", "[Name of the XTF to be created]")) self.xtf_file_line_edit.setValidator(fileValidator) self.xtf_file_line_edit.textChanged.connect( self.validators.validate_line_edits) self.xtf_file_line_edit.textChanged.connect( self.xtf_browser_opened_to_false) self.xtf_file_line_edit.textChanged.emit( self.xtf_file_line_edit.text()) self.connection_setting_button.clicked.connect(self.show_settings) self.connection_setting_button.setText( QCoreApplication.translate("DialogExportData", "Connection Settings")) # LOG self.log_config.setTitle( QCoreApplication.translate("DialogExportData", "Show log")) self.log_config.setFlat(True) 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("DialogExportData", "Export data"), QDialogButtonBox.AcceptRole) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.show_help) self.update_connection_info() self.update_model_names() 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("DialogExportData", "The database is not defined!")) self.db_connect_label.setToolTip('') self._accept_button.setEnabled(False) def update_model_names(self): self.export_models_qmodel = QStandardItemModel() model_names = self.db.get_models() if model_names: for model_name in model_names: if model_name not in LADMNames.DEFAULT_HIDDEN_MODELS: item = QStandardItem(model_name) item.setCheckable(False) item.setEditable(False) self.export_models_qmodel.appendRow(item) self.export_models_list_view.setModel(self.export_models_qmodel) def reject(self): if self._running_tool: QMessageBox.information( self, QCoreApplication.translate("DialogExportData", "Warning"), QCoreApplication.translate( "DialogExportData", "The Export 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.qgis_utils.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.qgis_utils.cache_layers_and_relations) self.logger.info(__name__, "Dialog closed.") self.done(QDialog.Accepted) def get_ili_models(self): ili_models = list() for index in range(self.export_models_qmodel.rowCount()): item = self.export_models_qmodel.item(index) ili_models.append(item.text()) return ili_models def show_settings(self): # We only need those tabs related to Model Baker/ili2db operations dlg = SettingsDialog(qgis_utils=self.qgis_utils, conn_manager=self.conn_manager) dlg.set_db_source(self.db_source) dlg.set_tab_pages_list( [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX]) # Connect signals (DBUtils, QgisUtils) 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.EXPORT) if dlg.exec_(): self.db = dlg.get_db_connection() self.update_model_names() 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.bar.clearWidgets() java_home_set = self.java_utils.set_java_home() if not java_home_set: message_java = QCoreApplication.translate( "DialogExportData", """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(message_java) self.java_utils.get_java_on_demand() self.disable() return configuration = self.update_configuration() if not self.xtf_file_line_edit.validator().validate( configuration.xtffile, 0)[0] == QValidator.Acceptable: self._running_tool = False message_error = QCoreApplication.translate( "DialogExportData", "Please set a valid XTF file before exporting data.") self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.get_ili_models(): self._running_tool = False message_error = QCoreApplication.translate( "DialogExportData", "Please set a valid schema to export. This schema does not have information to export." ) self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.export_models_list_view.setFocus() return if not configuration.iliexportmodels: self._running_tool = False message_error = QCoreApplication.translate( "DialogExportData", "Please set a model before exporting data.") self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.export_models_list_view.setFocus() return # If xtf browser was opened and the file exists, the user already chose # to overwrite the file if os.path.isfile(self.xtf_file_line_edit.text().strip() ) and not self.xtf_browser_was_opened: self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Warning) self.msg.setText( QCoreApplication.translate( "DialogExportData", "{filename} already exists.\nDo you want to replace it?"). format(filename=os.path.basename( self.xtf_file_line_edit.text().strip()))) self.msg.setWindowTitle( QCoreApplication.translate("DialogExportData", "Save in XTF Transfer File")) self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box = self.msg.exec_() if msg_box == QMessageBox.No: self._running_tool = False return with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() exporter = iliexporter.Exporter() db_factory = self._dbs_supported.get_db_factory(self.db.engine) exporter.tool = db_factory.get_mbaker_db_ili_mode() exporter.configuration = configuration self.save_configuration(configuration) exporter.stdout.connect(self.print_info) exporter.stderr.connect(self.on_stderr) exporter.process_started.connect(self.on_process_started) exporter.process_finished.connect(self.on_process_finished) self.progress_bar.setValue(25) try: if exporter.run() != iliexporter.Exporter.SUCCESS: self._running_tool = False self.show_message( QCoreApplication.translate( "DialogExportData", "An error occurred when exporting the data. For more information see the log..." ), Qgis.Warning) self.on_result.emit( False ) # Inform other classes that the execution was not successful return except JavaNotFoundError: self._running_tool = False message_error_java = QCoreApplication.translate( "DialogExportData", "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(message_error_java) self.show_message(message_error_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( "DialogExportData", "Export of the data was successfully completed."), Qgis.Success) self.on_result.emit( True) # Inform other classes that the execution was successful 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 save_configuration(self, configuration): settings = QSettings() settings.setValue( 'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export', 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_export')) # 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 custom_model_is_checked = settings.value( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', DEFAULT_USE_CUSTOM_MODELS, type=bool) if custom_model_is_checked: 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 = ExportConfiguration() db_factory.set_ili2db_configuration_params(self.db.dict_conn_params, configuration) configuration.xtffile = self.xtf_file_line_edit.text().strip() full_java_exe_path = JavaUtils.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.iliexportmodels = ';'.join(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( QCoreApplication.translate("DialogExportData", "Finished ({})").format(exit_code)) if result == iliexporter.Exporter.SUCCESS: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: 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: process data...': self.progress_bar.setValue(55) QCoreApplication.processEvents() elif text.strip() == 'Info: first validation pass...': self.progress_bar.setValue(70) QCoreApplication.processEvents() elif text.strip() == 'Info: second validation pass...': self.progress_bar.setValue(85) 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): self.qgis_utils.show_help("export_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): 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) def xtf_browser_opened_to_true(self): """ Slot. Sets a flag to true to eventually avoid asking a user whether to overwrite a file. """ self.xtf_browser_was_opened = True def xtf_browser_opened_to_false(self): """ Slot. Sets a flag to false to eventually ask a user whether to overwrite a file. """ self.xtf_browser_was_opened = False self.clear_messages()
class NmapsEngine: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgisInterface """ # Save reference to the QGIS interface self.iface = iface self.visible = False # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'NmapsEngine_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Nmaps Engine') # TODO: We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar(u'NmapsEngine') self.toolbar.setObjectName(u'NmapsEngine') # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('NmapsEngine', message) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ # Create the dialog (after translation) and keep reference self.dlg = NmapsEngineDialog() icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/NmapsEngine/icon.png' self.add_action(icon_path, text=self.tr(u'NMap'), callback=self.run, parent=self.iface.mainWindow()) self.dlg.pushButton.clicked.connect(self.create_nmap) self.dlg.listWidget.itemDoubleClicked.connect(self.item_click) self.bar = QgsMessageBar(self.dlg) QObject.connect(self.iface.mapCanvas(), SIGNAL("layersChanged()"), self.nmap_connect) QgsMapLayerRegistry.instance( ).layersWillBeRemoved["QStringList"].connect(self.remove_layer) self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.bar.move(20, 1) self.bar.setFixedSize(692, 40) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu(self.tr(u'&Nmaps Engine'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def run(self): """Run method that performs all the real work""" # show the dialog self.iface.mainWindow().statusBar().showMessage("...") self.dlg.listWidget.clear() self.dlg.show() self.visible = True # Run the dialog event loop self.nmap_connect() result = self.dlg.exec_() # See if OK was pressed if result: # Do something useful here - delete the line containing pass and # substitute with your code. self.visible = False pass def remove_layer(self, lyr_id): if len(lyr_id) == 1: lyr = QgsMapLayerRegistry.instance().mapLayer( lyr_id[0]) # returns QgsMapLayer pointer lyr_name = lyr.name() tl = pickle_db.load_obj() for k, v in tl.items(): if v == lyr_name: del tl[k] pickle_db.save_obj(tl) def nmap_connect(self): if self.visible: self.iface.mainWindow().statusBar().clear() self.bar.pushInfo("Serwer NMap", "Łączenie z serwerem NMap".decode("utf-8")) try: token = Authentication(ApiKey(), 'qgis-plugin').authenticate() self.nm_token = Token(token) except: self.bar.pushCritical("Serwer NMap", "Nie udało się połączyć".decode("utf-8")) tkd = TokenDialog(self) tkd.dlg() return False try: tilesets = Tilesets(self.nm_token).list() except: self.bar.pushCritical("Serwer NMap", "Nie mogę pobrać danych".decode("utf-8")) return False self.bar.clearWidgets() self.bar.pushSuccess("Serwer NMap", "Połączono".decode('utf-8')) self.iface.mainWindow().statusBar().showMessage( "Połączono".decode("utf-8")) tl = pickle_db.load_obj() avialble_layers = [ layer.name() for layer in self.iface.legendInterface().layers() ] self.dlg.listWidget.clear() for tileset in tilesets: tileset_list_item = TilesetListItem() tileset_list_item.setTextUp(tileset.get('id')) tileset_list_item.setTextDown(tileset.get('name')) if tileset.get('name') in tl and tl[tileset.get( 'name')] in avialble_layers: tileset_list_item.setTextMiddle(tl[tileset.get('name')]) if tileset.get('id') in tl and tl[tileset.get( 'id')] in avialble_layers: tileset_list_item.setTextMiddle(tl[tileset.get('id')]) tileset_list_item.setIcon(None) tileset_list_item_widget = QListWidgetItem(self.dlg.listWidget) tileset_list_item_widget.setSizeHint( tileset_list_item.sizeHint()) self.dlg.listWidget.addItem(tileset_list_item_widget) self.dlg.listWidget.setItemWidget(tileset_list_item_widget, tileset_list_item) self.bar.clearWidgets() self.bar.pushSuccess( "Serwer NMap", "Połączono i pobrano listę zestawów".decode('utf-8')) self.timer = QTimer() self.timer.timeout.connect(self.add_notif) self.timer.start(4000) def item_click(self, item): current = item.listWidget().itemWidget(item) item_id = current.getTextUp() item_label = current.getTextDown() TilesetCommonDialog(item_id, self).dlg(item_label) def create_nmap(self): TilesetCreateDialog(self).dlg() def add_notif(self): tileset_req = Tilesets(self.nm_token) notifications = tileset_req.jobs() self.bar.clearWidgets() for notification in notifications: if notification.get('status') == 'waiting': self.bar.pushInfo( "Serwer NMap", "Oczekiwanie %s %d %%".decode('utf-8') % (notification.get('originalname'), notification.get('progress'))) if notification.get('status') == 'processing': self.bar.pushInfo( "Serwer NMap", "Kafelkowanie %s %d %%".decode('utf-8') % (notification.get('originalname'), notification.get('progress'))) if notification.get('status') == 'success': self.bar.pushSuccess( "Serwer NMap", "Ukończono %s".decode('utf-8') % (notification.get('originalname'))) self.nmap_connect() tileset_req.hide(notification.get('job_id')) if notification.get('status') == 'error': self.bar.pushCritical( "Serwer NMap", "Błąd %s".decode('utf-8') % (notification.get('originalname'))) tileset_req.hide(notification.get('job_id'))
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 GetDBOrSchemaNameDialog(QDialog, DIALOG_UI): db_or_schema_created = pyqtSignal(str) def __init__(self, db_connector, uri, type, parent=None): """ Constructor :param db: database connection instance :param type: type of parameter to capture (database or schema) :param parent: parent of dialog """ QDialog.__init__(self, parent) self.type = type self.db_connector = db_connector self.uri = uri self.parent = parent self.setupUi(self) if self.type == 'database': self.message_label.setText( QCoreApplication.translate("GetDBOrSchemaNameDialog", "Enter the name of the database:")) self.parameter_line_edit.setPlaceholderText( QCoreApplication.translate( "GetDBOrSchemaNameDialog", "[Name of the database to be created]")) else: self.message_label.setText( QCoreApplication.translate("GetDBOrSchemaNameDialog", "Enter the name of the schema:")) self.parameter_line_edit.setPlaceholderText( QCoreApplication.translate( "GetDBOrSchemaNameDialog", "[Name of the schema to be created]")) self.setWindowTitle( QCoreApplication.translate("GetDBOrSchemaNameDialog", "Create {type}").format(type=self.type)) self.validators = Validators() # schema name mustn't have special characters regex = QRegExp("[a-zA-Z0-9_]+") validator = QRegExpValidator(regex) self.parameter_line_edit.setValidator(validator) self.parameter_line_edit.setMaxLength(63) self.parameter_line_edit.textChanged.connect( self.validators.validate_line_edits_lower_case) self.parameter_line_edit.textChanged.emit( self.parameter_line_edit.text()) 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.buttonBox.addButton( QCoreApplication.translate("GetDBOrSchemaNameDialog", "Create {type}").format(type=self.type), QDialogButtonBox.AcceptRole) def accepted(self): parameter_value = self.parameter_line_edit.text().strip() if not parameter_value: if self.type == 'database': self.show_message( QCoreApplication.translate( "GetDBOrSchemaNameDialog", "The name of the database cannot be empty."), Qgis.Warning) else: self.show_message( QCoreApplication.translate( "GetDBOrSchemaNameDialog", "The name of the schema cannot be empty."), Qgis.Warning) return tmp_db_conn = self.db_connector self.buttonBox.setEnabled(False) self.parameter_line_edit.setEnabled(False) with OverrideCursor(Qt.WaitCursor): if self.type == 'database': db_name = parameter_value result = tmp_db_conn.create_database(self.uri, db_name) elif self.type == 'schema': schema_name = parameter_value result = tmp_db_conn.create_schema(self.uri, schema_name) if result[0]: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.show_message(result[1], Qgis.Success) # signal updating the list of databases or schemas self.db_or_schema_created.emit(parameter_value) else: self.show_message(result[1], Qgis.Warning) self.buttonBox.setEnabled(True) self.parameter_line_edit.setEnabled(True) def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)
class STUploadFileDialog(QDialog, DIALOG_TRANSITION_SYSTEM_UI): on_result = pyqtSignal( bool) # whether the tool was run successfully or not def __init__(self, request_id, supply_type, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.request_id = request_id self.supply_type = supply_type self.st_utils = STUtils() self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.upload_file) self.buttonBox.helpRequested.connect(self.show_help) self.btn_browse_file.clicked.connect( make_file_selector( self.txt_file_path, QCoreApplication.translate( "STUploadFileDialog", "Select the file you want to upload to the transitional system" ), QCoreApplication.translate( "STUploadFileDialog", 'INTERLIS 2 transfer format (*.xtf)'))) self.initialize_progress() self.restore_settings() self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def upload_file(self): self.bar.clearWidgets() self.start_progress() self.enable_controls(False) file_path = self.txt_file_path.text().strip() if not self.txt_comments.toPlainText(): res = False res_msg = QCoreApplication.translate( "STUploadFileDialog", "File was not uploaded! Details: Comments are required.") elif os.path.isfile(file_path): zip_file_path = Utils.compress_file(file_path) with ProcessWithStatus( QCoreApplication.translate( "STUploadFileDialog", "Uploading file to ST server...")): res, res_msg = self.st_utils.upload_file( self.request_id, self.supply_type, zip_file_path, self.txt_comments.toPlainText()) else: res = False res_msg = QCoreApplication.translate( "STUploadFileDialog", "The file '{}' does not exist!").format(file_path) self.show_message(res_msg, Qgis.Success if res else Qgis.Warning) self.initialize_progress() if res: self.store_settings() else: self.enable_controls(True) # Prepare next run self.on_result.emit( res) # Inform other classes if the execution was successful return # Do not close dialog def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 0) def store_settings(self): settings = QSettings() settings.setValue( 'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_export', self.txt_file_path.text().strip()) def restore_settings(self): settings = QSettings() self.txt_file_path.setText( settings.value( 'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_export')) def start_progress(self): self.progress.setVisible(True) self.progress.setRange(0, 0) def initialize_progress(self): self.progress.setVisible(False) def enable_controls(self, enable): self.gbx_page_1.setEnabled(enable) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable) def show_help(self): show_plugin_help('transitional_system_upload_file')
class ImgUploaderWizard(QtGui.QWizard, FORM_CLASS): def __init__(self, iface, settings, parent=None): """Constructor.""" super(ImgUploaderWizard, 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.iface = iface self.setupUi(self) # Message bars need to be attached to pages, since the wizard object # does not have a layout. It doesn't work to attach the same bar # object to all pages (it is only shown in the last one). The only way # I could make it work was to create different QgsMessageBar objects, # one per page, but it is very to keep track of those references # along the code. It is messy, there should be a better solution. self.bar0 = QgsMessageBar() self.bar0.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(0).layout().addWidget(self.bar0) self.bar1 = QgsMessageBar() self.bar1.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(1).layout().addWidget(self.bar1) self.bar2 = QgsMessageBar() self.bar2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(2).layout().addWidget(self.bar2) self.setButtonText(QtGui.QWizard.CustomButton1, self.tr("&Start upload")); self.setOption(QtGui.QWizard.HaveCustomButton1, True); self.button(QWizard.CustomButton1).setVisible(False) self.settings = settings # Initialize the object for S3Manager and upload options and filenames #self.s3Mgr = None #self.upload_options = [] #self.upload_filenames = [] #self.upload_file_abspaths = [] self.s3UpPrgWin = None # Initialize layers and default settings self.loadLayers() self.loadMetadataSettings() self.loadStorageSettings() self.loadOptionsSettings() # register event handlers self.button(QWizard.BackButton).clicked.connect(self.previousPage) self.button(QWizard.NextButton).clicked.connect(self.nextPage) self.button(QWizard.FinishButton).clicked.connect(self.finishWizard) self.button(QWizard.CancelButton).clicked.connect(self.cancelWizard) # Imagery connections (wizard page 1) self.layers_tool_button.clicked.connect(self.loadLayers) self.file_tool_button.clicked.connect(self.selectFile) self.add_source_button.clicked.connect(self.addSources) self.remove_source_button.clicked.connect(self.removeSources) self.up_source_button.clicked.connect(self.upSource) self.down_source_button.clicked.connect(self.downSource) # Metadata connections (wizard page 2) self.sense_start_edit.setCalendarPopup(1) self.sense_start_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.sense_end_edit.setCalendarPopup(1) self.sense_end_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.default_button.clicked.connect(self.loadMetadataSettings) self.clean_button.clicked.connect(self.cleanMetadataSettings) self.save_button.clicked.connect(self.saveMetadata) # Temporarily hide the reload button self.reload_button.hide() self.reload_button.clicked.connect(self.loadSavedMetadata) # temporarily disable notify_oam_check and trigger_tiling_check self.notify_oam_check.setEnabled(False) self.trigger_tiling_check.setEnabled(False) # temporarily disable textEdit for website and tags # probably make textEdit for thumbnail later self.website_edit.setEnabled(False) self.tags_edit.setEnabled(False) # Upload tab connections (wizard page 3) self.storage_combo_box.currentIndexChanged.connect(self.enableSpecify) self.customButtonClicked.connect(self.startUpload) #self.button(QWizard.CustomButton1).clicked.connect(self.startUpload) # handlers for navigation def nextPage(self): if self.currentId() == 1: #print "Page ID: " + str(self.currentId()) #self.bar1.clearWidgets() pass elif self.currentId() == 2: #print "Page ID: " + str(self.currentId()) #self.bar2.clearWidgets() self.loadMetadataReviewBox() self.button(QWizard.CustomButton1).setVisible(True) else: pass def previousPage(self): if self.currentId() == 0: #print "Page ID: " + str(self.currentId()) #self.bar0.clearWidgets() pass elif self.currentId() == 1: #print "Page ID: " + str(self.currentId()) #self.bar1.clearWidgets() self.button(QWizard.CustomButton1).setVisible(False) else: pass def finishWizard(self): #print "finish wizard button was clicked." pass def cancelWizard(self): #print "cancel wizard button was clicked." # need to display QMessageBox if self.s3UpPrgWin != None: self.s3UpPrgWin.cancelAllUploads() # event handling for wizard page 1 def loadLayers(self): all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if not self.layers_list_widget.findItems(layer.name(),Qt.MatchExactly): if not self.sources_list_widget.findItems(layer.name(),Qt.MatchExactly): item = QListWidgetItem() item.setText(layer.name()) item.setData(Qt.UserRole, layer.dataProvider().dataSourceUri()) self.layers_list_widget.addItem(item) def selectFile(self): selected_file = QFileDialog.getOpenFileName( self, 'Select imagery file', os.path.expanduser("~")) self.source_file_edit.setText(selected_file) def addSources(self): filename = self.source_file_edit.text() selected_layers = self.layers_list_widget.selectedItems() if not filename and not selected_layers: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'Either a layer or file should be selected to be added', level=QgsMessageBar.WARNING) else: added = False if filename: result_val = validate_file(filename) if result_val["val"]: if not self.sources_list_widget.findItems(filename,Qt.MatchExactly): item = QListWidgetItem() item.setText(os.path.basename(filename)) item.setData(Qt.UserRole, filename) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) self.source_file_edit.setText('') added = True else: msg = result_val["msg"] self.bar0.clearWidgets() self.bar0.pushMessage("CRITICAL", msg, level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage("CRITICAL", msg, level=QgsMessageLog.INFO) if selected_layers: for item in selected_layers: if validate_layer(item.text(), self.iface): if not self.sources_list_widget.findItems(item.text(),Qt.MatchExactly): self.layers_list_widget.takeItem(self.layers_list_widget.row(item)) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) added = True else: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "Vector layers cannot be selected for upload", level=QgsMessageBar.CRITICAL) if added: self.bar0.clearWidgets() self.bar0.pushMessage( 'INFO', 'Source(s) added to the upload queue', level=QgsMessageBar.INFO) def removeSources(self): selected_sources = self.sources_list_widget.selectedItems() added_sources = self.added_sources_list_widget.selectedItems() if selected_sources: for item in selected_sources: self.sources_list_widget.takeItem(self.sources_list_widget.row(item)) added_item = self.added_sources_list_widget.findItems(item.text(),Qt.MatchExactly) if added_item: self.added_sources_list_widget.takeItem(self.added_sources_list_widget.row(added_item[0])) all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if item.text() == layer.name(): if not self.layers_list_widget.findItems(item.text(),Qt.MatchExactly): self.layers_list_widget.addItem(item) else: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'An imagery source must be selected to be removed', level=QgsMessageBar.WARNING) def upSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position > 0: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position-1,item) item.setSelected(1) def downSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position < self.sources_list_widget.count()-1: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position+1,item) item.setSelected(1) # event handling for wizard page 2 # load default values def loadMetadataSettings(self): self.settings.beginGroup("Metadata") self.base_uuid_edit.setText(self.settings.value('BASE_UUID')) self.title_edit.setText(self.settings.value('TITLE')) if self.settings.value('PLATFORM') == None: self.platform_combo_box.setCurrentIndex(0) else: self.platform_combo_box.setCurrentIndex( int(self.settings.value('PLATFORM'))) self.sensor_edit.setText(self.settings.value('SENSOR')) self.sensor_edit.setCursorPosition(0) self.sense_start_edit.setDate(QDateTime.fromString( self.settings.value('SENSE_START'), Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_START'), Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).time()) self.provider_edit.setText(self.settings.value('PROVIDER')) self.provider_edit.setCursorPosition(0) self.contact_edit.setText(self.settings.value('CONTACT')) self.contact_edit.setCursorPosition(0) #self.website_edit.setText(self.settings.value('WEBSITE')) #self.website_edit.setCursorPosition(0) #self.tags_edit.setText(self.settings.value('TAGS')) #self.tags_edit.setCursorPosition(0) self.settings.endGroup() def cleanMetadataSettings(self): self.base_uuid_edit.setText('') self.title_edit.setText('') self.platform_combo_box.setCurrentIndex(0) self.sensor_edit.setText('') self.sense_start_edit.setDate( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).time()) self.tags_edit.setText('') self.provider_edit.setText('') self.contact_edit.setText('') self.website_edit.setText('') self.license_check_box.setCheckState(0) self.reproject_check_box.setCheckState(0) def saveMetadata(self): flag = False if self.reproject_check_box.isChecked(): qMsgBox = QMessageBox() qMsgBox.setWindowTitle("Confirmation") qMsgBox.setText("You checked the reprojection option, which can require significant \ amount of time. Are you sure to continue?") qMsgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) #qMsgBox.setDefaultButton(QMessageBox.Cancel) if qMsgBox.exec_() == QMessageBox.Ok: flag = True else: flag = True if flag: num_selected_layers = 0 count = 0 """Open python console. I haven't indentified the exact reason, but messagebar doesn't work properly without opening python console and some print statements""" pluginMenu = self.iface.pluginMenu() #print(repr(pluginMenu)) for action in pluginMenu.actions(): if 'Python Console' in action.text(): action.trigger() self.activateWindow() selected_layers = self.added_sources_list_widget.selectedItems() if selected_layers: num_selected_layers = len(selected_layers) for each_layer in selected_layers: file_abspath = each_layer.data(Qt.UserRole) if self.reproject_check_box.isChecked(): self.bar1.clearWidgets() # this message doesn't show up without opening python console # need to identify the reason self.bar1.pushMessage( 'INFO', 'Reprojecting files: %sth image out of %s is being processed...'\ % (str(count+1), str(num_selected_layers)), level=QgsMessageBar.INFO) # Isn't it better to use thread? print('Reprojecting {0}'.format(str(os.path.basename(file_abspath)))) """ probably, it is better to create a class for reprojection """ file_abspath = reproject(file_abspath) print('Add the image as a raster layer...') original_file_name = each_layer.text() reprojected_file_name = '(EPSG3857) ' + original_file_name self.iface.addRasterLayer(file_abspath, reprojected_file_name) else: self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', '%sth image out of %s is processed.'\ % (str(count+1), str(num_selected_layers)), level=QgsMessageBar.INFO) """ probably need to insert the codes to create and insert the detailed footprint layer here""" # get metadata from GUI, and store them in a dictionary metaInputInDict = {} temp_filename = file_abspath.split('/')[-1] strUuid = '{0}{1}'.format(self.base_uuid_edit.text(), temp_filename) metaInputInDict['uuid'] = strUuid metaInputInDict['title'] = self.title_edit.text() metaInputInDict['platform'] = self.platform_combo_box.currentText() metaInputInDict['acquisition_start'] = self.sense_start_edit.dateTime().toString(Qt.ISODate) metaInputInDict['acquisition_end'] = self.sense_end_edit.dateTime().toString(Qt.ISODate) metaInputInDict['provider'] = self.provider_edit.text() metaInputInDict['contact'] = self.contact_edit.text() # temporarily disable two keys (website and tags) #metaInputInDict['website'] = self.website_edit.text() #metaInputInDict['tags'] = self.tags_edit.text() properties = {} properties['sensor'] = self.sensor_edit.text() """need to implement thumbnail creation, etc.""" properties['thumbnail'] = "currently not available" metaInputInDict['properties'] = properties # extract metadata from GeoTiff, and merge with the metadata from textbox imgMetaHdlr = ImgMetadataHandler(file_abspath) imgMetaHdlr.extractMetaInImagery() metaForUpload = dict(imgMetaHdlr.getMetaInImagery().items() + metaInputInDict.items()) strMetaForUpload = str(json.dumps(metaForUpload)) #json_file_abspath = os.path.splitext(file_abspath)[0] + '.tif_meta.json' json_file_abspath = file_abspath + '_meta.json' #print json_file_abspath f = open(json_file_abspath,'w') f.write(strMetaForUpload) f.close() count += 1 # refresh the list widget and selected items, if reprojection is done if self.reproject_check_box.isChecked(): print('update the listWidget to reflect the change...') items_for_remove = self.added_sources_list_widget.findItems(original_file_name, Qt.MatchExactly) self.added_sources_list_widget.takeItem(self.added_sources_list_widget.row(items_for_remove[0])) items_for_remove = self.sources_list_widget.findItems(original_file_name, Qt.MatchExactly) self.sources_list_widget.takeItem(self.sources_list_widget.row(items_for_remove[0])) #self.layers_list_widget.addItem(items_for_remove[0]) self.loadLayers() items_to_add = self.layers_list_widget.findItems(reprojected_file_name, Qt.MatchExactly) items_to_add[0].setSelected(True) self.addSources() self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Metadata for the selected sources were saved', level=QgsMessageBar.INFO) else: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'One or more source imagery should be selected to have the metadata saved', level=QgsMessageBar.WARNING) def loadSavedMetadata(self): qMsgBox = QMessageBox() qMsgBox.setText('Under construction:\nMessage from loadSavedMetadata function.') qMsgBox.exec_() # event handling for wizard page 3 # please also see startUpload function for event handling of Start Upload button def enableSpecify(self): if self.storage_combo_box.currentIndex() == 1: self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) else: self.specify_label.setEnabled(0) self.specify_edit.setText('') self.specify_edit.setEnabled(0) def loadStorageSettings(self): self.settings.beginGroup("Storage") bucket = self.settings.value('S3_BUCKET_NAME') storage_index = self.storage_combo_box.findText(bucket,Qt.MatchExactly) if not storage_index == -1: self.storage_combo_box.setCurrentIndex(storage_index) else: self.storage_combo_box.setCurrentIndex(self.storage_combo_box.findText(self.tr('other...'))) self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) self.specify_edit.setText(self.settings.value('S3_BUCKET_NAME')) self.key_id_edit.setText(self.settings.value('AWS_ACCESS_KEY_ID')) self.key_id_edit.setCursorPosition(0) self.secret_key_edit.setText(self.settings.value('AWS_SECRET_ACCESS_KEY')) self.secret_key_edit.setCursorPosition(0) self.settings.endGroup() def loadOptionsSettings(self): self.settings.beginGroup("Options") """ Boolean values are converted into string and lower case for 'if' statement, since PyQt sometimes returns 'true', just like C++, instead of 'True', Python style. Maybe we can use integer values (0 or 1), instead of using string. """ if str(self.settings.value('LICENSE')).lower() == 'true': self.license_check_box.setCheckState(2) if str(self.settings.value('REPROJECT')).lower() == 'true': self.reproject_check_box.setCheckState(2) """ if str(self.settings.value('NOTIFY_OAM')).lower() == 'true': self.notify_oam_check.setCheckState(2) if str(self.settings.value('TRIGGER_OAM_TS')).lower() == 'true': self.trigger_tiling_check.setCheckState(2) """ # This part is for temporal use. self.notify_oam_check.setCheckState(0) self.trigger_tiling_check.setCheckState(0) self.settings.endGroup() def loadMetadataReviewBox(self): json_file_abspaths = [] for index in xrange(self.sources_list_widget.count()): file_abspath = str(self.sources_list_widget.item(index).data(Qt.UserRole)) json_file_abspath = '' #if self.reproject_check_box.isChecked(): # json_file_abspath = os.path.splitext(file_abspath)[0]+'_EPSG3857.json' #else: #json_file_abspath = os.path.splitext(file_abspath)[0]+'.tif_meta.json' json_file_abspath = file_abspath + '_meta.json' #print str(json_file_abspath) json_file_abspaths.append(json_file_abspath) temp = QTemporaryFile() temp.open() for json_file_abspath in json_file_abspaths: if os.path.exists(json_file_abspath): with open(json_file_abspath) as infile: temp.write(infile.read() + '\n\n') else: temp.write('%s was not found. Please save it in advance.\n\n' % str(json_file_abspath)) temp.flush() temp.seek(0) stream = QTextStream(temp) self.review_metadata_box.setText(stream.readAll()) #event handler for upload button def startUpload(self): upload_options = [] upload_file_abspaths = [] #get the information of upload options if self.notify_oam_check.isChecked(): #self.upload_options.append("notify_oam") upload_options.append("notify_oam") if self.trigger_tiling_check.isChecked(): #self.upload_options.append("trigger_tiling") upload_options.append("trigger_tiling") if not self.license_check_box.isChecked(): self.bar2.clearWidgets() self.bar2.pushMessage( 'CRITICAL', 'Please check the lisence term.', level=QgsMessageBar.WARNING) else: for index in xrange(self.sources_list_widget.count()): upload_file_abspath = str(self.added_sources_list_widget.item(index).data(Qt.UserRole)) upload_file_abspaths.append(upload_file_abspath) # get login information for bucket bucket_name = None bucket_key = None bucket_secret = None if self.storage_combo_box.currentIndex() == 0: bucket_name = 'oam-qgis-plugin-test' else: bucket_name = str(self.specify_edit.text()) if not bucket_name: self.bar2.clearWidgets() self.bar2.pushMessage( 'WARNING', 'The bucket for upload must be provided', level=QgsMessageBar.WARNING) bucket_key = str(self.key_id_edit.text()) bucket_secret = str(self.secret_key_edit.text()) if self.s3UpPrgWin == None: self.s3UpPrgWin = S3UploadProgressWindow() self.s3UpPrgWin.started.connect(self.displayConnectionResult) #self.s3UpPrgWin.progress.connect(self.updateProgress) self.s3UpPrgWin.startConfirmed.connect(self.updateListWidgets) self.s3UpPrgWin.finished.connect(self.finishUpload) self.s3UpPrgWin.startUpload(bucket_key, bucket_secret, bucket_name, upload_options, upload_file_abspaths) self.button(QWizard.FinishButton).setVisible(False) #print(self.isTopLevel()) def displayConnectionResult(self, didStart): if didStart: self.bar2.clearWidgets() self.bar2.pushMessage( 'INFO', 'Uploading imagery and metadata...', level=QgsMessageBar.INFO) else: self.bar2.clearWidgets() self.bar2.pushMessage( 'CRITICAL', 'Connection to S3 server failed.', level=QgsMessageBar.CRITICAL) def updateListWidgets(self, fileAbsPath): #print('fileAbsPath: ' + fileAbsPath) #print(str(self.added_sources_list_widget.count())) for index in xrange(0, self.added_sources_list_widget.count()): refFileAbsPath = str(self.added_sources_list_widget.item(index).data(Qt.UserRole)) #print('refFileAbsPath: ' + refFileAbsPath) if fileAbsPath == refFileAbsPath: self.added_sources_list_widget.takeItem(index) break #print(str(self.sources_list_widget.count())) for index in xrange(0, self.sources_list_widget.count()): refFileAbsPath = str(self.sources_list_widget.item(index).data(Qt.UserRole)) #print('refFileAbsPath: ' + refFileAbsPath) if fileAbsPath == refFileAbsPath: self.sources_list_widget.takeItem(index) break self.loadMetadataReviewBox() self.loadLayers() def finishUpload(self, numSuccess, numCancelled, numFailed): self.button(QWizard.FinishButton).setVisible(True) self.bar2.clearWidgets() self.bar2.pushMessage( 'Upload Result', 'Success:{0} Cancel:{1} Fail:{2}'.format(numSuccess, numCancelled, numFailed), level=QgsMessageBar.INFO) # Probably, it is better to change this part into log file. print('') print('------------------------------------------------') print('Success:{0} Cancel:{1} Fail:{2}'.format(numSuccess, numCancelled, numFailed)) print('------------------------------------------------') print('')
class SuppliesETLWizard(QWizard, WIZARD_UI): on_result = pyqtSignal( bool) # whether the tool was run successfully or not def __init__(self, db, conn_manager, parent=None): QWizard.__init__(self, parent) self.setupUi(self) self._db = db self.conn_manager = conn_manager self.parent = parent self.logger = Logger() self.app = AppInterface() self.names = self._db.names self.help_strings = HelpStrings() self._data_source_widget = None self.db_source = SUPPLIES_DB_SOURCE self.tool_name = "" self._running_tool = False self._db_was_changed = False # To postpone calling refresh gui until we close this dialog instead of settings self.progress_configuration(0, 1) # start from: 0, number of steps: 1 self.wizardPage2.setButtonText( QWizard.CustomButton1, QCoreApplication.translate("SuppliesETLWizard", "Run ETL")) self.wizardPage1.setButtonText( QWizard.CancelButton, QCoreApplication.translate("SuppliesETLWizard", "Close")) self.wizardPage2.setButtonText( QWizard.CancelButton, QCoreApplication.translate("SuppliesETLWizard", "Close")) # Auxiliary data to set nonlinear next pages self.pages = [self.wizardPage1, self.wizardPage2, self.wizardPage3] self.dict_pages_ids = { self.pages[idx]: pid for idx, pid in enumerate(self.pageIds()) } # Set connections self.rad_snc_data.toggled.connect(self.etl_option_changed) self.etl_option_changed() # Initialize it self.button(QWizard.CustomButton1).clicked.connect( self.import_button_clicked) self.button(QWizard.HelpButton).clicked.connect(self.show_help) self.currentIdChanged.connect(self.current_page_changed) self.finished.connect(self.finished_slot) self.btn_browse_connection.clicked.connect(self.show_settings) # Initialize self.current_page_changed(1) self.update_connection_info() self.restore_settings() self.initialize_feedback() # Set MessageBar for QWizard self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def current_page_changed(self, id): """ Reset the Next button. Needed because Next might have been disabled by a condition in a another SLOT. """ #enable_next_wizard(self) button_list = [ QWizard.HelpButton, QWizard.Stretch, QWizard.BackButton, QWizard.CustomButton1, QWizard.NextButton, QWizard.FinishButton, QWizard.CancelButton ] not_visible = [] if id == self.dict_pages_ids[self.wizardPage1]: self.setWindowTitle( QCoreApplication.translate("SuppliesETLWizard", "Run supplies ETL")) button_list.remove(QWizard.BackButton) button_list.remove(QWizard.CustomButton1) button_list.remove(QWizard.FinishButton) elif id == self.dict_pages_ids[self.wizardPage2]: button_list.remove(QWizard.FinishButton) not_visible.append(self.NextButton) self.load_data_source_controls() if self.rad_snc_data.isChecked(): self.setWindowTitle( QCoreApplication.translate("SuppliesETLWizard", "ETL: SNC to Supplies model")) else: self.setWindowTitle( QCoreApplication.translate("SuppliesETLWizard", "ETL: Cobol to Supplies model")) elif id == self.dict_pages_ids[self.wizardPage3]: self.bar.clearWidgets() button_list.remove(QWizard.CustomButton1) button_list.remove(QWizard.NextButton) button_list.remove(QWizard.BackButton) self.wizardPage3.setFinalPage(True) self.setButtonLayout(button_list) for button in not_visible: self.button(button).setVisible(False) def etl_option_changed(self): """ Adjust help, names and titles according to the selected option """ if self.rad_snc_data.isChecked(): self.tool_name = QCoreApplication.translate( "SuppliesETLWizard", "ETL-SNC") self.txt_help_page_2.setHtml( self.help_strings.WIZ_SUPPLIES_ETL_PAGE_2.format("del SNC")) elif self.rad_cobol_data.isChecked(): # self.rad_cobol_data is checked self.tool_name = QCoreApplication.translate( "SuppliesETLWizard", "ETL-Cobol") self.txt_help_page_2.setHtml( self.help_strings.WIZ_SUPPLIES_ETL_PAGE_2.format("de Cobol")) def load_data_source_controls(self): self.clear_data_source_widget() if self.rad_snc_data.isChecked(): self._data_source_widget = SNCDataSourceWidget() else: # Cobol self._data_source_widget = CobolDataSourceWidget() self._data_source_widget.input_data_changed.connect( self.set_import_button_enabled) self._data_source_widget.emit_input_data_changed( ) # Initialize input validation self.data_source_layout.addWidget(self._data_source_widget) self._data_source_widget.setVisible(True) def clear_data_source_widget(self): while self.data_source_layout.count(): child = self.data_source_layout.takeAt(0) if child.widget(): child.widget().setVisible(False) def initialize_feedback(self): self.progress.setValue(0) self.progress.setVisible(False) self.custom_feedback = CustomFeedback() self.custom_feedback.progressChanged.connect(self.progress_changed) self.set_gui_controls_enabled(True) def progress_configuration(self, base, num_process): """ :param base: Where to start counting from :param num_process: Number of steps """ self.progress_base = base self.progress_maximum = 100 * num_process self.progress.setMaximum(self.progress_maximum) def progress_changed(self): QCoreApplication.processEvents() # Listen to cancel from the user self.progress.setValue(self.progress_base + self.custom_feedback.progress()) def set_gui_controls_enabled(self, enable): self.gbx_data_source.setEnabled(enable) self.target_data.setEnabled(enable) self.set_import_button_enabled(enable) def set_import_button_enabled(self, enable): self.button(QWizard.CustomButton1).setEnabled(enable) def import_button_clicked(self): self.bar.clearWidgets() self.save_settings() etl_result = False if self.rad_snc_data.isChecked(): etl = ETLSNC(self.names, self._data_source_widget) else: # Cobol etl = ETLCobol(self.names, self._data_source_widget) if self._db.test_connection()[0]: reply = QMessageBox.question( self, QCoreApplication.translate("SuppliesETLWizard", "Warning"), QCoreApplication.translate( "SuppliesETLWizard", "The database <i>{}</i> already has a valid LADM-COL structure.<br/><br/>If such database has any data, loading data into it might cause invalid data.<br/><br/>Do you still want to continue?" ).format(self._db.get_description_conn_string()), QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.set_gui_controls_enabled(False) self.button(self.BackButton).setEnabled(False) self.button(self.CustomButton1).setEnabled(False) self.button(self.CancelButton).setText( QCoreApplication.translate("SuppliesETLWizard", "Cancel")) with OverrideCursor(Qt.WaitCursor): res_alpha, msg_alpha = etl.load_alphanumeric_layers() if res_alpha: res_spatial, msg_spatial = etl.load_spatial_layers() if res_spatial: res_model, msg_model = self.load_model_layers( etl.layers) if res_model: layers_feature_count_before = { name: layer.featureCount() for name, layer in etl.layers.items() } self._running_tool = True self.progress.setVisible(True) res_etl_model = etl.run_etl_model( self.custom_feedback) if not self.custom_feedback.isCanceled( ) and res_etl_model: self.progress.setValue(100) self.button( self.NextButton).setVisible(True) self.button( self.CustomButton1).setVisible(False) self.button(self.CancelButton).setText( QCoreApplication.translate( "SuppliesETLWizard", "Close")) self.show_message( QCoreApplication.translate( "SuppliesETLWizard", "The {} has finished successfully!" ).format(self.tool_name), Qgis.Success, 0) self.logger.clear_status() self.fill_summary( layers_feature_count_before, etl.layers) etl_result = True else: self.initialize_feedback( ) # Get ready for an eventual new execution self.logger.clear_status() self._running_tool = False else: self.show_message(msg_model, Qgis.Warning) else: self.show_message(msg_spatial, Qgis.Warning) else: self.show_message(msg_alpha, Qgis.Warning) else: with OverrideCursor(Qt.WaitCursor): # TODO: if an empty schema was selected, do the magic under the hood # self.create_model_into_database() # Now execute "accepted()" msg = QCoreApplication.translate( "SuppliesETLWizard", "To run the ETL, the database (schema) should have the Supplies LADM-COL structure. Choose a proper database (schema) and try again." ) self.show_message(msg, Qgis.Warning) self.logger.warning(__name__, msg) self.on_result.emit( etl_result) # Inform other classes if the execution was successful def reject(self): if self._running_tool: reply = QMessageBox.question( self, QCoreApplication.translate("SuppliesETLWizard", "Warning"), QCoreApplication.translate( "SuppliesETLWizard", "The '{}' tool is still running. Do you want to cancel it? If you cancel, the data might be incomplete in the target database." ).format(self.tool_name), QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.custom_feedback.cancel() self._running_tool = False msg = QCoreApplication.translate( "SuppliesETLWizard", "The '{}' tool was cancelled.").format(self.tool_name) self.logger.info(__name__, msg) self.show_message(msg, Qgis.Info) else: if self._db_was_changed: self.conn_manager.db_connection_changed.emit( self._db, self._db.test_connection()[0], self.db_source) self.logger.info(__name__, "Dialog closed.") self.app.settings.set_setting( self.app.settings.COBOL_FILES_DIR_KEY, '') self.app.settings.set_setting(self.app.settings.SNC_FILES_DIR_KEY, '') self.done(1) def finished_slot(self, result): self.bar.clearWidgets() def show_message(self, message, level, duration=10): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, duration) def fill_summary(self, layers_feature_count_before, etl_layers): layers_feature_count_after = { name: layer.featureCount() for name, layer in etl_layers.items() } summary = """<html><head/><body><p>""" summary += QCoreApplication.translate( "SuppliesETLWizard", "<h4>{} report</h4>").format(self.tool_name) summary += QCoreApplication.translate( "SuppliesETLWizard", "Number of features loaded to the LADM-COL cadastral supplies model:<br/>" ) for name, before_count in layers_feature_count_before.items(): summary += QCoreApplication.translate( "SuppliesETLWizard", '<br/><b>{}</b> : {}'.format( name, layers_feature_count_after[name] - before_count)) summary += """<hr>""" summary += """</body></html>""" self.txt_log.setText(summary) def save_settings(self): settings = QSettings() etl_source = "snc" if self.rad_snc_data.isChecked(): etl_source = "snc" elif self.rad_cobol_data.isChecked(): etl_source = "cobol" settings.setValue('Asistente-LADM-COL/supplies/etl_source', etl_source) self._data_source_widget.save_settings() # In the main page (source-target configuration), save if splitter is closed self.app.settings.etl_splitter_collapsed = self.splitter_2.sizes( )[1] == 0 def restore_settings(self): settings = QSettings() etl_source = settings.value( 'Asistente-LADM-COL/supplies/etl_source') or 'snc' if etl_source == 'snc': self.rad_snc_data.setChecked(True) elif etl_source == 'cobol': self.rad_cobol_data.setChecked(True) # If splitter in the main page was closed before, set it as closed again if self.app.settings.etl_splitter_collapsed: sizes = self.splitter_2.sizes() self.splitter_2.setSizes([sizes[0], 0]) def show_help(self): show_plugin_help('supplies') def show_settings(self): dlg = SettingsDialog(self.conn_manager, parent=self) dlg.setWindowTitle( QCoreApplication.translate("SuppliesETLWizard", "Target DB Connection Settings")) dlg.show_tip( QCoreApplication.translate( "SuppliesETLWizard", "Configure where do you want the data to be imported.")) dlg.set_db_source(self.db_source) dlg.set_required_models([LADMNames.SUPPLIES_MODEL_KEY]) dlg.set_tab_pages_list( [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX]) dlg.set_action_type(EnumDbActionType.IMPORT_FROM_ETL) dlg.db_connection_changed.connect(self.db_connection_changed) if self.db_source == COLLECTED_DB_SOURCE: dlg.db_connection_changed.connect( self.app.core.cache_layers_and_relations) if dlg.exec_(): self._db = dlg.get_db_connection() self.update_connection_info() 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()) else: self.db_connect_label.setText( QCoreApplication.translate("SuppliesETLWizard", "The database is not defined!")) self.db_connect_label.setToolTip('') def db_connection_changed(self, db, ladm_col_db, db_source): # We dismiss parameters here, after all, we already have the db, and the ladm_col_db may change from this moment # until we close the supplies dialog (e.g., we might run an import schema before under the hood) self._db_was_changed = True def load_model_layers(self, layers): self.app.core.get_layers(self._db, layers, load=True) if not layers: return False, QCoreApplication.translate( "SuppliesETLWizard", "There was a problem loading layers from the 'Supplies' model!" ) return True, ''
class ResampleImageToBlockDialog(QtGui.QDialog, FORM_CLASS): """Extract statistics from a list of rasters at set locations.""" toolKey = 'ResampleImageBandDialog' def __init__(self, iface, parent=None): super(ResampleImageToBlockDialog, self).__init__(parent) # Set up the user interface from Designer. self.setupUi(self) self.iface = iface self.DISP_TEMP_LAYERS = read_setting(PLUGIN_NAME + '/DISP_TEMP_LAYERS', bool) self.DEBUG = config.get_debug_mode() # Catch and redirect python errors directed at the log messages python error tab. QgsMessageLog.instance().messageReceived.connect(errorCatcher) if not os.path.exists(TEMPDIR): os.mkdir(TEMPDIR) # Setup for validation messagebar on gui----------------------------- self.messageBar = QgsMessageBar( self) # leave this message bar for bailouts self.validationLayout = QtGui.QFormLayout(self) # new layout to gui if isinstance(self.layout(), (QtGui.QFormLayout, QtGui.QGridLayout)): # create a validation layout so multiple messages can be added and cleaned up. self.layout().insertRow(0, self.validationLayout) self.layout().insertRow(0, self.messageBar) else: self.layout().insertWidget( 0, self.messageBar) # for use with Vertical/horizontal layout box self.outQgsCRS = None self.exclude_map_layers() self.updateRaster() self.updateUseSelected() self.autoSetCoordinateSystem() # GUI Runtime Customisation ----------------------------------------------- self.setWindowIcon( QtGui.QIcon(':/plugins/pat/icons/icon_resampleToBlock.svg')) self.chkAddToDisplay.setChecked(False) self.add_blank_field_to_cbo() # self.chkAddToDisplay.hide() def cleanMessageBars(self, AllBars=True): """Clean Messages from the validation layout. Args: AllBars (bool): Remove All bars including those which haven't timed-out. Defaults to True """ layout = self.validationLayout for i in reversed(range(layout.count())): # when it timed out the row becomes empty.... if layout.itemAt(i).isEmpty(): # .removeItem doesn't always work. so takeAt(pop) it instead item = layout.takeAt(i) elif AllBars: # ie remove all item = layout.takeAt(i) # also have to remove any widgets associated with it. if item.widget() is not None: item.widget().deleteLater() def send_to_messagebar(self, message, title='', level=QgsMessageBar.INFO, duration=5, exc_info=None, core_QGIS=False, addToLog=False, showLogPanel=False): """ Add a message to the forms message bar. Args: message (str): Message to display title (str): Title of message. Will appear in bold. Defaults to '' level (QgsMessageBarLevel): The level of message to log. Defaults to QgsMessageBar.INFO duration (int): Number of seconds to display message for. 0 is no timeout. Defaults to 5 core_QGIS (bool): Add to QGIS interface rather than the dialog addToLog (bool): Also add message to Log. Defaults to False showLogPanel (bool): Display the log panel exc_info () : Information to be used as a traceback if required """ if core_QGIS: newMessageBar = self.iface.messageBar() else: newMessageBar = QgsMessageBar(self) widget = newMessageBar.createMessage(title, message) if showLogPanel: button = QPushButton(widget) button.setText('View') button.setContentsMargins(0, 0, 0, 0) button.setFixedWidth(35) button.pressed.connect(openLogPanel) widget.layout().addWidget(button) newMessageBar.pushWidget(widget, level, duration=duration) if not core_QGIS: rowCount = self.validationLayout.count() self.validationLayout.insertRow(rowCount + 1, newMessageBar) if addToLog: if level == 1: # 'WARNING': LOGGER.warning(message) elif level == 2: # 'CRITICAL': # Add a traceback to log only for bailouts only if exc_info is not None: exc_type, exc_value, exc_traceback = sys.exc_info() mess = str(traceback.format_exc()) message = message + '\n' + mess LOGGER.critical(message) else: # INFO = 0 LOGGER.info(message) def exclude_map_layers(self): """ Run through all loaded layers to find ones which should be excluded. In this case exclude services.""" exVlayer_list = [] exRlayer_list = [] for layer in self.iface.legendInterface().layers(): if layer.type() == QgsMapLayer.RasterLayer: if layer.providerType() != 'gdal': exRlayer_list.append(layer) elif layer.type() == QgsMapLayer.VectorLayer: if layer.providerType() != 'ogr': exVlayer_list.append(layer) self.mcboRasterLayer.setExceptedLayerList(exRlayer_list) if len(exRlayer_list) > 0: pass if len(exVlayer_list) > 0: self.mcboPolygonLayer.setExceptedLayerList(exVlayer_list) def updateRaster(self): if self.mcboRasterLayer.currentLayer() is None: return layer = self.mcboRasterLayer.currentLayer() provider = layer.dataProvider() if provider.srcHasNoDataValue(1): self.spnNoDataVal.setValue(provider.srcNoDataValue(1)) elif len(provider.userNoDataValues(1)) > 0: self.spnNoDataVal.setValue(provider.userNoDataValues(1)[0].min()) else: self.spnNoDataVal.setValue(0) # add a band list to the drop down box bandCount = self.mcboRasterLayer.currentLayer().bandCount() band_list = ['Band {: >2}'.format(i) for i in range(1, bandCount + 1)] self.cboBand.setMaxCount(bandCount + 1) self.cboBand.clear() self.cboBand.addItems([u''] + sorted(band_list)) def updateUseSelected(self): """Update use selected checkbox if active layer has a feature selection""" self.chkUseSelected.setChecked(False) if self.mcboPolygonLayer.count() == 0: return polygon_lyr = self.mcboPolygonLayer.currentLayer() self.mFieldComboBox.setLayer(polygon_lyr) if len(polygon_lyr.selectedFeatures()) > 0: self.chkUseSelected.setText( 'Use the {} selected feature(s) ?'.format( len(polygon_lyr.selectedFeatures()))) self.chkUseSelected.setEnabled(True) else: self.chkUseSelected.setText('No features selected') self.chkUseSelected.setEnabled(False) def autoSetCoordinateSystem(self): if self.mcboRasterLayer.count() == 0: return self.cleanMessageBars() raster_lyr = self.mcboRasterLayer.currentLayer() raster_utm_crs = crs.getProjectedCRSForXY( raster_lyr.extent().xMinimum(), raster_lyr.extent().yMinimum(), int(raster_lyr.crs().authid().replace('EPSG:', ''))) self.outQgsCRS = None if raster_utm_crs is not None: raster_crs = QgsCoordinateReferenceSystem('EPSG:{}'.format( raster_utm_crs.epsg_number)) self.outQgsCRS = raster_crs if self.outQgsCRS is not None: self.lblOutCRS.setText('{} - {}'.format( self.outQgsCRS.description(), self.outQgsCRS.authid())) self.lblOutCRSTitle.setStyleSheet('color:black') self.lblOutCRS.setStyleSheet('color:black') else: self.lblOutCRSTitle.setStyleSheet('color:red') self.lblOutCRS.setStyleSheet('color:red') self.lblOutCRS.setText('Unspecified') self.send_to_messagebar( 'Auto detect coordinate system Failed. Check coordinate system of input raster layer', level=QgsMessageBar.CRITICAL, duration=5) return def add_blank_field_to_cbo(self, set=True): """ Add a blank string to the field combo box. Fixed in qgis 3""" if self.mFieldComboBox.findText('', QtCore.Qt.MatchFixedString) == -1: self.mFieldComboBox.addItem(u'') if set == True: self.mFieldComboBox.setField(u'') def on_mcboRasterLayer_layerChanged(self): self.updateRaster() self.autoSetCoordinateSystem() def on_mcboPolygonLayer_layerChanged(self): self.updateUseSelected() self.autoSetCoordinateSystem() # ToDo: QGIS 3 implement QgsMapLayerComboBox.allowEmptyLayer() instead of chkUsePoly checkbox self.chkUsePoly.setChecked(True) self.add_blank_field_to_cbo() @QtCore.pyqtSlot(int) def on_chkUsePoly_stateChanged(self, state): self.add_blank_field_to_cbo() self.mFieldComboBox.setEnabled(state) self.lblGroupByField.setEnabled(state) # def on_mFieldComboBox_fieldChanged(self, field): # # Problem : after selecting a field, the blank is removed from the pick list. # # Solution: Add it again.... but this doesn't work. # if self.mFieldComboBox.findText('', QtCore.Qt.MatchFixedString) == -1: # # work around for not having a physical blank in the list. Fixed in qgis 3 # self.mFieldComboBox.addItem(u'') def on_chkUseSelected_stateChanged(self, state): if self.chkUseSelected.isChecked(): self.chkUsePoly.setChecked(True) @QtCore.pyqtSlot(name='on_cmdOutCRS_clicked') def on_cmdOutCRS_clicked(self): dlg = QgsGenericProjectionSelector(self) dlg.setMessage(self.tr('Select coordinate system')) if dlg.exec_(): if dlg.selectedAuthId() != '': self.outQgsCRS = QgsCoordinateReferenceSystem( dlg.selectedAuthId()) if self.outQgsCRS.geographicFlag(): self.outQgsCRS = None self.send_to_messagebar(unicode( self. tr("Geographic coordinate systems are not allowed. Resetting to default.." )), level=QgsMessageBar.WARNING, duration=5) else: self.outQgsCRS = None if self.outQgsCRS is None: self.autoSetCoordinateSystem() self.lblOutCRSTitle.setStyleSheet('color:black') self.lblOutCRS.setStyleSheet('color:black') self.lblOutCRS.setText( self.tr('{} - {}'.format(self.outQgsCRS.description(), self.outQgsCRS.authid()))) @QtCore.pyqtSlot(name='on_cmdOutputFolder_clicked') def on_cmdOutputFolder_clicked(self): self.messageBar.clearWidgets() if self.lneOutputFolder.text() is None: outFolder = '' else: outFolder = self.lneOutputFolder.text() if outFolder == '': outFolder = read_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastOutFolder") if outFolder is None or not os.path.exists(outFolder): outFolder = read_setting(PLUGIN_NAME + '/BASE_OUT_FOLDER') s = QtGui.QFileDialog.getExistingDirectory( self, self. tr("Save output files to a folder. A sub-folder will be created from the image name" ), outFolder, QtGui.QFileDialog.ShowDirsOnly) self.cleanMessageBars(self) if s == '' or s is None: return s = os.path.normpath(s) self.lblOutputFolder.setStyleSheet('color:black') self.lneOutputFolder.setStyleSheet('color:black') self.lneOutputFolder.setText(s) write_setting(PLUGIN_NAME + "/" + self.toolKey + "/LastOutFolder", s) def validate(self): """Check to see that all required gui elements have been entered and are valid.""" self.messageBar.clearWidgets() self.cleanMessageBars(AllBars=True) try: errorList = [] if self.mcboRasterLayer.currentLayer() is None: self.lblRasterLayer.setStyleSheet('color:red') errorList.append(self.tr("Input image layer required.")) else: self.lblRasterLayer.setStyleSheet('color:black') if self.cboBand.currentText() == '': self.lblBand.setStyleSheet('color:red') errorList.append(self.tr("Input band selection required.")) else: self.lblBand.setStyleSheet('color:black') if self.dsbPixelSize.value() <= 0: self.lblPixelSize.setStyleSheet('color:red') errorList.append(self.tr("Pixel size must be greater than 0.")) else: self.lblPixelSize.setStyleSheet('color:black') if self.outQgsCRS is None: self.lblOutCRSTitle.setStyleSheet('color:red') self.lblOutCRS.setStyleSheet('color:red') errorList.append( self.tr("Select output projected coordinate system")) else: if self.outQgsCRS.geographicFlag(): self.lblOutCRSTitle.setStyleSheet('color:red') self.lblOutCRS.setStyleSheet('color:red') errorList.append( self. tr("Output projected coordinate system (not geographic) required" )) else: self.lblOutCRSTitle.setStyleSheet('color:black') self.lblOutCRS.setStyleSheet('color:black') if self.lneOutputFolder.text() == '': self.lblOutputFolder.setStyleSheet('color:red') errorList.append(self.tr("Select output data folder")) elif not os.path.exists(self.lneOutputFolder.text()): self.lneOutputFolder.setStyleSheet('color:red') self.lblOutputFolder.setStyleSheet('color:red') errorList.append(self.tr("Output data folder does not exist")) else: self.lblOutputFolder.setStyleSheet('color:black') self.lneOutputFolder.setStyleSheet('color:black') if len(errorList) > 0: raise ValueError(errorList) except ValueError as e: self.cleanMessageBars(True) if len(errorList) > 0: for i, ea in enumerate(errorList): self.send_to_messagebar(unicode(ea), level=QgsMessageBar.WARNING, duration=(i + 1) * 5) return False return True def accept(self, *args, **kwargs): try: if not self.validate(): return False # disable form via a frame, this will still allow interaction with the message bar self.fraMain.setDisabled(True) # clean gui and Qgis messagebars self.cleanMessageBars(True) # Change cursor to Wait cursor QtGui.qApp.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) self.iface.mainWindow().statusBar().showMessage( 'Processing {}'.format(self.windowTitle())) LOGGER.info('{st}\nProcessing {}'.format(self.windowTitle(), st='*' * 50)) self.send_to_messagebar( "Please wait.. QGIS will be locked... See log panel for progress.", level=QgsMessageBar.WARNING, duration=0, addToLog=False, core_QGIS=False, showLogPanel=True) # Add settings to log settingsStr = 'Parameters:---------------------------------------' settingsStr += '\n {:20}\t{}'.format( 'Image layer:', self.mcboRasterLayer.currentLayer().name()) settingsStr += '\n {:20}\t{}'.format('Image Band:', self.cboBand.currentText()) settingsStr += '\n {:20}\t{}'.format('Image nodata value:', self.spnNoDataVal.value()) if self.chkUsePoly.isChecked(): if self.chkUseSelected.isChecked(): settingsStr += '\n {:20}\t{} with {} selected features'.format( 'Layer:', self.mcboPolygonLayer.currentLayer().name(), len(self.mcboPolygonLayer.currentLayer(). selectedFeatures())) else: settingsStr += '\n {:20}\t{}'.format( 'Boundary layer:', self.mcboPolygonLayer.currentLayer().name()) if self.mFieldComboBox.currentField(): settingsStr += '\n {:20}\t{}'.format( 'Block ID field:', self.mFieldComboBox.currentField()) else: settingsStr += '\n {:20}\t{}'.format('Boundary layer:', '') settingsStr += '\n {:20}\t{}'.format('Block ID field:', '') settingsStr += '\n {:20}\t{}'.format('Resample pixel size: ', self.dsbPixelSize.value()) settingsStr += '\n {:30}\t{}'.format( 'Output Coordinate System:', self.lblOutCRS.text()) settingsStr += '\n {:30}\t{}\n'.format( 'Output Folder:', self.lneOutputFolder.text()) LOGGER.info(settingsStr) lyrRaster = self.mcboRasterLayer.currentLayer() if self.chkUsePoly.isChecked(): lyrBoundary = self.mcboPolygonLayer.currentLayer() if self.chkUseSelected.isChecked(): savePlyName = lyrBoundary.name() + '_poly.shp' filePoly = os.path.join(TEMPDIR, savePlyName) if os.path.exists(filePoly): removeFileFromQGIS(filePoly) QgsVectorFileWriter.writeAsVectorFormat(lyrBoundary, filePoly, "utf-8", lyrBoundary.crs(), "ESRI Shapefile", onlySelected=True) if self.DISP_TEMP_LAYERS: addVectorFileToQGIS(filePoly, layer_name=os.path.splitext( os.path.basename(filePoly))[0], group_layer_name='DEBUG', atTop=True) else: filePoly = lyrBoundary.source() band_num = [int(self.cboBand.currentText().replace('Band ', ''))] files = resample_bands_to_block( lyrRaster.source(), self.dsbPixelSize.value(), self.lneOutputFolder.text(), band_nums=band_num, image_epsg=int(lyrRaster.crs().authid().replace('EPSG:', '')), image_nodata=self.spnNoDataVal.value(), polygon_shapefile=filePoly if self.chkUsePoly.isChecked() else None, groupby=self.mFieldComboBox.currentField() if self.mFieldComboBox.currentField() else None, out_epsg=int(self.outQgsCRS.authid().replace('EPSG:', ''))) if self.chkAddToDisplay.isChecked(): for ea_file in files: removeFileFromQGIS(ea_file) addRasterFileToQGIS(ea_file, group_layer_name=os.path.basename( os.path.dirname(ea_file)), atTop=False) self.cleanMessageBars(True) self.fraMain.setDisabled(False) self.iface.mainWindow().statusBar().clearMessage() self.iface.messageBar().popWidget() QtGui.qApp.restoreOverrideCursor() return super(ResampleImageToBlockDialog, self).accept(*args, **kwargs) except Exception as err: QtGui.qApp.restoreOverrideCursor() self.iface.mainWindow().statusBar().clearMessage() self.cleanMessageBars(True) self.fraMain.setDisabled(False) self.send_to_messagebar(str(err), level=QgsMessageBar.CRITICAL, duration=0, addToLog=True, core_QGIS=False, showLogPanel=True, exc_info=sys.exc_info()) return False # leave dialog open
class DialogImportData(QDialog, DIALOG_UI): open_dlg_import_schema = pyqtSignal(dict) # dict with key-value params BUTTON_NAME_IMPORT_DATA = QCoreApplication.translate( "DialogImportData", "Import data") BUTTON_NAME_GO_TO_CREATE_STRUCTURE = QCoreApplication.translate( "DialogImportData", "Go to Create Structure...") def __init__(self, iface, conn_manager, context, link_to_import_schema=True, parent=None): QDialog.__init__(self, parent) self.setupUi(self) QgsGui.instance().enableAutoGeometryRestore(self) self.iface = iface self.conn_manager = conn_manager self.db_source = context.get_db_sources()[0] self.link_to_import_schema = link_to_import_schema self.db = self.conn_manager.get_db_connector_from_source( self.db_source) self.base_configuration = BaseConfiguration() self.logger = Logger() self.app = AppInterface() self.__ladmcol_models = LADMColModelRegistry() self.java_dependency = JavaDependency() self.java_dependency.download_dependency_completed.connect( self.download_java_complete) self.java_dependency.download_dependency_progress_changed.connect( self.download_java_progress_change) self.ilicache = IliCache(self.base_configuration) self.ilicache.refresh() self._dbs_supported = ConfigDBsSupported() self._running_tool = False # There may be 1 case where we need to emit a db_connection_changed from the Import Data dialog: # 1) Connection Settings was opened and the DB conn was changed. self._db_was_changed = False # To postpone calling refresh gui until we close this dialog instead of settings # Similarly, we could call a refresh on layers and relations cache in 1 case: # 1) If the ID dialog was called for the COLLECTED source: opening Connection Settings and changing the DB # connection. self._schedule_layers_and_relations_refresh = False # We need bar definition above calling clear_messages self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.xtf_file_browse_button.clicked.connect( make_file_selector( self.xtf_file_line_edit, title=QCoreApplication.translate( "DialogImportData", "Open Transfer or Catalog File"), file_filter=QCoreApplication.translate( "DialogImportData", 'Transfer File (*.xtf *.itf);;Catalogue File (*.xml *.xls *.xlsx)' ))) self.validators = Validators() self.xtf_file_line_edit.setPlaceholderText( QCoreApplication.translate("DialogImportData", "[Name of the XTF to be imported]")) fileValidator = FileValidator(pattern=['*.xtf', '*.itf', '*.xml']) self.xtf_file_line_edit.setValidator(fileValidator) self.xtf_file_line_edit.textChanged.connect(self.update_import_models) self.xtf_file_line_edit.textChanged.emit( self.xtf_file_line_edit.text()) # db self.connection_setting_button.clicked.connect(self.show_settings) self.connection_setting_button.setText( QCoreApplication.translate("DialogImportData", "Connection Settings")) # LOG self.log_config.setTitle( QCoreApplication.translate("DialogImportData", "Show log")) self.buttonBox.accepted.disconnect() self.buttonBox.clicked.connect(self.accepted_import_data) self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) self._accept_button = self.buttonBox.addButton( self.BUTTON_NAME_IMPORT_DATA, QDialogButtonBox.AcceptRole) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.show_help) self.update_connection_info() self.restore_configuration() def accepted_import_data(self, button): if self.buttonBox.buttonRole(button) == QDialogButtonBox.AcceptRole: if button.text() == self.BUTTON_NAME_IMPORT_DATA: self.accepted() elif button.text() == self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE: self.close() # Close import data dialog self.open_dlg_import_schema.emit({ 'selected_models': self.get_ili_models() }) # Emit signal to open import schema dialog def reject(self): if self._running_tool: QMessageBox.information( self, QCoreApplication.translate("DialogImportData", "Warning"), QCoreApplication.translate( "DialogImportData", "The Import Data tool is still running. Please wait until it finishes." )) else: self.close_dialog() def close_dialog(self): """ We use this method to be safe when emitting the db_connection_changed, otherwise we could trigger slots that unload the plugin, destroying dialogs and thus, leading to crashes. """ if self._schedule_layers_and_relations_refresh: self.conn_manager.db_connection_changed.connect( self.app.core.cache_layers_and_relations) if self._db_was_changed: # If the db was changed, it implies it complies with ladm_col, hence the second parameter self.conn_manager.db_connection_changed.emit( self.db, True, self.db_source) if self._schedule_layers_and_relations_refresh: self.conn_manager.db_connection_changed.disconnect( self.app.core.cache_layers_and_relations) self.logger.info(__name__, "Dialog closed.") self.done(QDialog.Accepted) def update_connection_info(self): db_description = self.db.get_description_conn_string() if db_description: self.db_connect_label.setText(db_description) self.db_connect_label.setToolTip(self.db.get_display_conn_string()) self._accept_button.setEnabled(True) else: self.db_connect_label.setText( QCoreApplication.translate("DialogImportData", "The database is not defined!")) self.db_connect_label.setToolTip('') self._accept_button.setEnabled(False) def update_import_models(self): self.clear_messages() error_msg = None if not self.xtf_file_line_edit.text().strip(): color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel(self.import_models_qmodel) else: if os.path.isfile(self.xtf_file_line_edit.text().strip()): color = '#fff' # White self.import_models_qmodel = QStandardItemModel() model_names = get_models_from_xtf( self.xtf_file_line_edit.text().strip()) for model in self.__ladmcol_models.supported_models(): if not model.hidden() and model.full_name() in model_names: item = QStandardItem(model.full_alias()) item.setData(model.full_name(), Qt.UserRole) item.setCheckable(False) item.setEditable(False) self.import_models_qmodel.appendRow(item) if self.import_models_qmodel.rowCount() > 0: self.import_models_list_view.setModel( self.import_models_qmodel) else: error_msg = QCoreApplication.translate( "DialogImportData", "No models were found in the XTF. Is it a valid file?") color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel( self.import_models_qmodel) else: error_msg = QCoreApplication.translate( "DialogImportData", "Please set a valid XTF file") color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel( self.import_models_qmodel) self.xtf_file_line_edit.setStyleSheet( 'QLineEdit {{ background-color: {} }}'.format(color)) if error_msg: self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.import_models_list_view.setFocus() return def get_ili_models(self): ili_models = list() for index in range(self.import_models_qmodel.rowCount()): item = self.import_models_qmodel.item(index) ili_models.append(item.data(Qt.UserRole)) return ili_models def show_settings(self): # We only need those tabs related to Model Baker/ili2db operations dlg = SettingsDialog(self.conn_manager, parent=self) dlg.setWindowTitle( QCoreApplication.translate("DialogImportData", "Target DB Connection Settings")) dlg.show_tip( QCoreApplication.translate( "DialogImportData", "Configure where do you want the XTF data to be imported.")) dlg.set_db_source(self.db_source) dlg.set_tab_pages_list( [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX]) # Connect signals (DBUtils, Core) dlg.db_connection_changed.connect(self.db_connection_changed) if self.db_source == COLLECTED_DB_SOURCE: self._schedule_layers_and_relations_refresh = True dlg.set_action_type(EnumDbActionType.IMPORT) if dlg.exec_(): self.db = dlg.get_db_connection() self.update_connection_info() def db_connection_changed(self, db, ladm_col_db, db_source): self._db_was_changed = True self.clear_messages() def accepted(self): self._running_tool = True self.txtStdout.clear() self.progress_bar.setValue(0) self.bar.clearWidgets() if not os.path.isfile(self.xtf_file_line_edit.text().strip()): self._running_tool = False error_msg = QCoreApplication.translate( "DialogImportData", "Please set a valid XTF file before importing data. XTF file does not exist." ) self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.xtf_file_line_edit.setFocus() return java_home_set = self.java_dependency.set_java_home() if not java_home_set: message_java = QCoreApplication.translate( "DialogImportData", """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(message_java) self.java_dependency.get_java_on_demand() self.disable() return configuration = self.update_configuration() if configuration.disable_validation: # If data validation at import is disabled, we ask for confirmation self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Question) self.msg.setText( QCoreApplication.translate( "DialogImportData", "Are you sure you want to import your data without validation?" )) self.msg.setWindowTitle( QCoreApplication.translate("DialogImportData", "Import XTF without validation?")) self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) res = self.msg.exec_() if res == QMessageBox.No: self._running_tool = False return if not self.xtf_file_line_edit.validator().validate( configuration.xtffile, 0)[0] == QValidator.Acceptable: self._running_tool = False error_msg = QCoreApplication.translate( "DialogImportData", "Please set a valid XTF before importing data.") self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.get_ili_models(): self._running_tool = False error_msg = QCoreApplication.translate( "DialogImportData", "The selected XTF file does not have information according to the LADM-COL model to import." ) self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.import_models_list_view.setFocus() return # Get list of models present in the XTF file, in the DB and in the list of required models (by the plugin) ili_models = set([ili_model for ili_model in self.get_ili_models()]) supported_models_in_ili = set([ m.full_name() for m in self.__ladmcol_models.supported_models() ]).intersection(ili_models) if not supported_models_in_ili: self._running_tool = False error_msg = QCoreApplication.translate("DialogImportData", "The selected XTF file does not have data from any LADM-COL model supported by the LADM-COL Assistant. " \ "Therefore, you cannot import it! These are the models supported:\n\n * {}").format(" \n * ".join([m.full_alias() for m in self.__ladmcol_models.supported_models()])) self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.import_models_list_view.setFocus() return db_models = set(self.db.get_models()) suggested_models = sorted(ili_models.difference(db_models)) if not ili_models.issubset(db_models): self._running_tool = False error_msg = QCoreApplication.translate("DialogImportData", "IMPORT ERROR: The XTF file to import does not have the same models as the target database schema. " \ "Please create a schema that also includes the following missing modules:\n\n * {}").format( " \n * ".join(suggested_models)) self.txtStdout.clear() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.setText(error_msg) self.show_message(error_msg, Qgis.Warning) self.xtf_file_line_edit.setFocus() # button is removed to define order in GUI for button in self.buttonBox.buttons(): if button.text() == self.BUTTON_NAME_IMPORT_DATA: self.buttonBox.removeButton(button) # Check if button was previously added self.remove_create_structure_button() if self.link_to_import_schema: self.buttonBox.addButton( self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE, QDialogButtonBox.AcceptRole).setStyleSheet( "color: #aa2222;") self.buttonBox.addButton(self.BUTTON_NAME_IMPORT_DATA, QDialogButtonBox.AcceptRole) return with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() dataImporter = iliimporter.Importer(dataImport=True) db_factory = self._dbs_supported.get_db_factory(self.db.engine) dataImporter.tool = db_factory.get_model_baker_db_ili_mode() dataImporter.configuration = configuration self.save_configuration(configuration) dataImporter.stdout.connect(self.print_info) dataImporter.stderr.connect(self.on_stderr) dataImporter.process_started.connect(self.on_process_started) dataImporter.process_finished.connect(self.on_process_finished) self.progress_bar.setValue(25) try: if dataImporter.run() != iliimporter.Importer.SUCCESS: self._running_tool = False self.show_message( QCoreApplication.translate( "DialogImportData", "An error occurred when importing the data. For more information see the log..." ), Qgis.Warning) return except JavaNotFoundError: self._running_tool = False error_msg_java = QCoreApplication.translate( "DialogImportData", "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again." ).format(JAVA_REQUIRED_VERSION) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(error_msg_java) self.show_message(error_msg_java, Qgis.Warning) return self._running_tool = False self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) self.show_message( QCoreApplication.translate( "DialogImportData", "Import of the data was successfully completed"), Qgis.Success) def download_java_complete(self): self.accepted() def download_java_progress_change(self, progress): self.progress_bar.setValue(progress / 2) if (progress % 20) == 0: self.txtStdout.append('...') def remove_create_structure_button(self): for button in self.buttonBox.buttons(): if button.text() == self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE: self.buttonBox.removeButton(button) def save_configuration(self, configuration): settings = QSettings() settings.setValue( 'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_import', configuration.xtffile) settings.setValue('Asistente-LADM-COL/QgisModelBaker/show_log', not self.log_config.isCollapsed()) def restore_configuration(self): settings = QSettings() self.xtf_file_line_edit.setText( settings.value( 'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_import')) # Show log value_show_log = settings.value( 'Asistente-LADM-COL/QgisModelBaker/show_log', False, type=bool) self.log_config.setCollapsed(not value_show_log) # set model repository # if there is no option by default use online model repository self.use_local_models = settings.value( 'Asistente-LADM-COL/models/custom_model_directories_is_checked', DEFAULT_USE_CUSTOM_MODELS, type=bool) if self.use_local_models: self.custom_model_directories = settings.value( 'Asistente-LADM-COL/models/custom_models', DEFAULT_MODELS_DIR) def update_configuration(self): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ db_factory = self._dbs_supported.get_db_factory(self.db.engine) configuration = ImportDataConfiguration() db_factory.set_ili2db_configuration_params(self.db.dict_conn_params, configuration) configuration.xtffile = self.xtf_file_line_edit.text().strip() configuration.delete_data = False configuration.srs_auth = QSettings().value( 'Asistente-LADM-COL/QgisModelBaker/srs_auth', DEFAULT_SRS_AUTH, str) configuration.srs_code = QSettings().value( 'Asistente-LADM-COL/QgisModelBaker/srs_code', int(DEFAULT_SRS_CODE), int) configuration.inheritance = ILI2DBNames.DEFAULT_INHERITANCE configuration.create_basket_col = ILI2DBNames.CREATE_BASKET_COL configuration.create_import_tid = ILI2DBNames.CREATE_IMPORT_TID configuration.stroke_arcs = ILI2DBNames.STROKE_ARCS configuration.with_importtid = True full_java_exe_path = JavaDependency.get_full_java_exe_path() if full_java_exe_path: self.base_configuration.java_path = full_java_exe_path # User could have changed the default values self.use_local_models = QSettings().value( 'Asistente-LADM-COL/models/custom_model_directories_is_checked', DEFAULT_USE_CUSTOM_MODELS, type=bool) self.custom_model_directories = QSettings().value( 'Asistente-LADM-COL/models/custom_models', DEFAULT_MODELS_DIR) # Check custom model directories if self.use_local_models: if not self.custom_model_directories: self.base_configuration.custom_model_directories_enabled = False else: self.base_configuration.custom_model_directories = self.custom_model_directories self.base_configuration.custom_model_directories_enabled = True configuration.base_configuration = self.base_configuration if self.get_ili_models(): configuration.ilimodels = ';'.join(self.get_ili_models()) configuration.disable_validation = not QSettings().value( 'Asistente-LADM-COL/models/validate_data_importing_exporting', True, bool) return configuration def print_info(self, text, text_color='#000000', clear=False): self.txtStdout.setTextColor(QColor(text_color)) self.txtStdout.append(text) QCoreApplication.processEvents() def on_stderr(self, text): color_log_text(text, self.txtStdout) self.advance_progress_bar_by_text(text) def on_process_started(self, command): self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(command) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): color = '#004905' if exit_code == 0 else '#aa2222' self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append('Finished ({})'.format(exit_code)) if result == iliimporter.Importer.SUCCESS: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: self.show_message( QCoreApplication.translate("DialogImportData", "Error when importing data"), Qgis.Warning) # Open log if self.log_config.isCollapsed(): self.log_config.setCollapsed(False) def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models...': self.progress_bar.setValue(50) QCoreApplication.processEvents() elif text.strip() == 'Info: create table structure...': self.progress_bar.setValue(55) QCoreApplication.processEvents() elif text.strip() == 'Info: first validation pass...': self.progress_bar.setValue(60) QCoreApplication.processEvents() elif text.strip() == 'Info: second validation pass...': self.progress_bar.setValue(80) QCoreApplication.processEvents() def clear_messages(self): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.txtStdout.clear() # Clear previous log messages self.progress_bar.setValue(0) # Initialize progress bar def show_help(self): show_plugin_help("import_data") def disable(self): self.source_config.setEnabled(False) self.target_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.source_config.setEnabled(True) self.target_config.setEnabled(True) self.buttonBox.setEnabled(True) def show_message(self, message, level): if level == Qgis.Warning: self.enable() self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage("Asistente LADM-COL", message, level, duration=0)
class geopunt4QgisAdresDialog(QtGui.QDialog): def __init__(self, iface): QtGui.QDialog.__init__(self, None) self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QtCore.QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QtCore.QTranslator() self.translator.load(localePath) if QtCore.qVersion() > '4.3.3': QtCore.QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_geopunt4Qgis() self.ui.setupUi(self) #get settings self.s = QtCore.QSettings() self.loadSettings() #setup geometryHelper object self.gh = gh.geometryHelper(self.iface) #create graphicsLayer self.graphicsLayer = [] #populate gemeenteBox gemeentes = json.load( open( os.path.join(os.path.dirname(__file__), "data/gemeentenVL.json"))) gemeenteNamen = [str(n["Naam"]) for n in gemeentes] self.ui.gemeenteBox.addItems(gemeenteNamen) self.ui.gemeenteBox.setEditText( QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog", "gemeente")) self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}') self.ui.gemeenteBox.setFocus() self.completer = QtGui.QCompleter(self) self.completerModel = QtGui.QStringListModel(self) self.ui.gemeenteBox.setCompleter(self.completer) self.completer.setModel(self.completerModel) self.completer.setCaseSensitivity(False) self.completerModel.setStringList(gemeenteNamen) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton(QtGui.QPushButton("Sluiten"), QtGui.QDialogButtonBox.RejectRole) #event handlers if self.adresSearchOnEnter: self.ui.zoekText.returnPressed.connect(self.onZoekActivated) else: self.ui.zoekText.textEdited.connect(self.onZoekActivated) self.ui.gemeenteBox.currentIndexChanged.connect(self.onZoekActivated) self.ui.resultLijst.itemDoubleClicked.connect(self.onItemActivated) self.ui.resultLijst.itemClicked.connect(self.onItemClick) self.ui.ZoomKnop.clicked.connect(self.onZoomKnopClick) self.ui.Add2mapKnop.clicked.connect(self.onAdd2mapKnopClick) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean) def loadSettings(self): self.saveToFile = int(self.s.value("geopunt4qgis/adresSavetoFile", 1)) layerName = self.s.value("geopunt4qgis/adreslayerText", "") if layerName: self.layerName = layerName self.adresSearchOnEnter = int( self.s.value("geopunt4qgis/adresSearchOnEnter", 0)) self.timeout = int(self.s.value("geopunt4qgis/timeout", 15)) if int(self.s.value("geopunt4qgis/useProxy", 0)): self.proxy = self.s.value("geopunt4qgis/proxyHost", "") self.port = self.s.value("geopunt4qgis/proxyPort", "") else: self.proxy = "" self.port = "" self.startDir = self.s.value("geopunt4qgis/startDir", os.path.dirname(__file__)) self.gp = geopunt.Adres(self.timeout, self.proxy, self.port) def openHelp(self): webbrowser.open_new_tab("http://kgis.be/index.html#!geopuntAddress.md") def onZoekActivated(self): self._clearGraphicsLayer() self.bar.clearWidgets() gemeente = self.ui.gemeenteBox.currentText() if gemeente <> QtCore.QCoreApplication.translate( "geopunt4QgisAdresDialog", "gemeente"): self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #000000}') txt = self.ui.zoekText.text() + ", " + gemeente suggesties = self.gp.fetchSuggestion(txt, 25) self.ui.resultLijst.clear() if type(suggesties) is list and len(suggesties) <> 0: self.ui.resultLijst.addItems(suggesties) if len(suggesties) == 1: self.ui.resultLijst.setCurrentRow(0) elif type(suggesties) is str: self.bar.pushMessage(QtCore.QCoreApplication.translate( "geopunt4QgisAdresDialog", "Waarschuwing"), suggesties, level=QgsMessageBar.WARNING) def onItemActivated(self, item): txt = item.text() self._zoomLoc(txt) def onItemClick(self, item): txt = item.text() streetNr = txt.split(",")[:-1] self.ui.zoekText.setText(",".join(streetNr)) def onZoomKnopClick(self): item = self.ui.resultLijst.currentItem() if item: self._zoomLoc(item.text()) def onAdd2mapKnopClick(self): item = self.ui.resultLijst.currentItem() if item: self._addToMap(item.text()) def _clearGraphicsLayer(self): for graphic in self.graphicsLayer: self.iface.mapCanvas().scene().removeItem(graphic) self.graphicsLayer = [] def _zoomLoc(self, txt): self._clearGraphicsLayer() locations = self.gp.fetchLocation(txt) if type(locations) is list and len(locations): loc = locations[0] LowerLeftX = loc['BoundingBox']['LowerLeft']['X_Lambert72'] LowerLeftY = loc['BoundingBox']['LowerLeft']['Y_Lambert72'] UpperRightX = loc['BoundingBox']['UpperRight']['X_Lambert72'] UpperRightY = loc['BoundingBox']['UpperRight']['Y_Lambert72'] self.gh.zoomtoRec(QgsPoint(LowerLeftX, LowerLeftY), QgsPoint(UpperRightX, UpperRightY), 31370) xlb, ylb = loc["Location"]["X_Lambert72"], loc["Location"][ "Y_Lambert72"] x, y = self.gh.prjPtToMapCrs(QgsPoint(xlb, ylb), 31370) m = QgsVertexMarker(self.iface.mapCanvas()) self.graphicsLayer.append(m) m.setCenter(QgsPoint(x, y)) m.setColor(QtGui.QColor(255, 255, 0)) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(9) elif type(locations) is str: self.bar.pushMessage(QtCore.QCoreApplication.translate( "geopunt4QgisAdresDialog", "Waarschuwing"), locations, level=QgsMessageBar.WARNING, duration=3) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate( "geopunt4QgisAdresDialog", "onbekende fout"), level=QgsMessageBar.CRITICAL, duration=3) def _addToMap(self, txt): if not self.layernameValid(): return locations = self.gp.fetchLocation(txt) if type(locations) is list and len(locations): loc = locations[0] x, y = loc["Location"]["X_Lambert72"], loc["Location"][ "Y_Lambert72"] adres = loc["FormattedAddress"] LocationType = loc["LocationType"] pt = self.gh.prjPtToMapCrs(QgsPoint(x, y), 31370) self.gh.save_adres_point(pt, adres, typeAddress=LocationType, layername=self.layerName, saveToFile=self.saveToFile, sender=self, startFolder=os.path.join( self.startDir, self.layerName)) elif type(locations) is str: self.bar.pushMessage(QtCore.QCoreApplication.translate( "geopunt4QgisAdresDialog", "Waarschuwing"), locations, level=QgsMessageBar.WARNING) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate( "geopunt4QgisAdresDialog", "onbekende fout"), level=QgsMessageBar.CRITICAL) def layernameValid(self): if not hasattr(self, 'layerName'): layerName, accept = QtGui.QInputDialog.getText( None, QtCore.QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QtCore.QCoreApplication.translate( "geopunt4Qgis", 'Geef een naam voor de laag op:')) if accept == False: return False else: self.layerName = layerName return True def clean(self): self.bar.clearWidgets() self.ui.resultLijst.clear() self.ui.zoekText.setText("") self.ui.gemeenteBox.setEditText( QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog", "gemeente")) self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}') self._clearGraphicsLayer()
class AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(self.getParametersPanel(alg, self)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.cornerWidget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 5) self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }") self.runAsBatchButton = QPushButton(self.tr("Run as batch process...")) self.runAsBatchButton.clicked.connect(self.runAsBatch) layout.addWidget(self.runAsBatchButton) self.cornerWidget.setLayout(layout) self.tabWidget.setCornerWidget(self.cornerWidget) def getParametersPanel(self, alg, parent): return ParametersPanel(parent, alg) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def getParamValues(self): parameters = {} for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if not param.isDestination(): wrapper = self.mainWidget.wrappers[param.name()] value = None if wrapper.widget: value = wrapper.value() parameters[param.name()] = value if not param.checkValueIsAcceptable(value): raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget) else: dest_project = None if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \ isinstance(param, (QgsProcessingParameterRasterOutput, QgsProcessingParameterFeatureSink, OutputTable)): if self.mainWidget.checkBoxes[param.name()].isChecked(): dest_project = QgsProject.instance() value = self.mainWidget.outputWidgets[param.name()].getValue() if value: value.destinationProject = dest_project parameters[param.name()] = value return parameters def checkExtentCRS(self): unmatchingCRS = False hasExtent = False context = dataobjects.createContext() projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance()) for param in self.alg.parameterDefinitions(): if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = QgsProcessingUtils.mapLayerFromString(inputlayer, context) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): if param.skip_crs_check: continue value = self.mainWidget.wrappers[param.name()].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): super(AlgorithmDialog, self)._saveGeometry() context = dataobjects.createContext() checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS) try: feedback = self.createFeedback() parameters = self.getParamValues() if checkCRS and not self.alg.validateInputCrs(parameters, context): reply = QMessageBox.question(self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) #TODO if False and checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question(self, self.tr("Extent CRS"), self.tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.alg.checkParameterValues(parameters, context) if msg: QMessageBox.warning( self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.setInfo( self.tr('<b>Algorithm \'{0}\' starting...</b>').format(self.alg.displayName()), escape_html=False) feedback.pushInfo(self.tr('Input parameters:')) feedback.pushCommandInfo(pformat(parameters)) feedback.pushInfo('') start_time = time.time() if self.iterateParam: self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) if executeIterating(self.alg, parameters, self.iterateParam, context, feedback): feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time))) self.buttonCancel.setEnabled(False) self.finish(parameters, context, feedback) else: self.buttonCancel.setEnabled(False) QApplication.restoreOverrideCursor() self.resetGUI() else: command = self.alg.asPythonCommand(parameters, context) if command: ProcessingLog.addToLog(command) self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) result = executeAlgorithm(self.alg, parameters, context, feedback) feedback.pushInfo(self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(result)) feedback.pushInfo('') self.buttonCancel.setEnabled(False) self.finish(result, context, feedback) except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect(lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()), level=QgsMessageBar.WARNING, duration=5) def finish(self, result, context, feedback): keepOpen = ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: if not handleAlgorithmResults(self.alg, context, feedback, not keepOpen): self.resetGUI() return self.executed = True self.setInfo(self.tr('Algorithm \'{0}\' finished').format(self.alg.displayName()), escape_html=False) QApplication.restoreOverrideCursor() if not keepOpen: self.close() else: self.resetGUI() if self.alg.hasHtmlOutputs(): self.setInfo( self.tr('HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.'), escape_html=False)
class SettingsDialog(QDialog, DIALOG_UI): db_connection_changed = pyqtSignal(DBConnector, bool) # dbconn, ladm_col_db organization_tools_changed = pyqtSignal(str) def __init__(self, iface=None, parent=None, qgis_utils=None): QDialog.__init__(self, parent) self.setupUi(self) self.iface = iface self.log = QgsApplication.messageLog() self._db = None self.qgis_utils = qgis_utils self.ant_tools_initial_chk_value = None self._action_type = None self.conf_db = ConfigDbSupported() self.online_models_radio_button.setChecked(True) self.online_models_radio_button.toggled.connect( self.model_provider_toggle) self.custom_model_directories_line_edit.setText("") self.custom_models_dir_button.clicked.connect( self.show_custom_model_dir) self.custom_model_directories_line_edit.setVisible(False) self.custom_models_dir_button.setVisible(False) # Set connections self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.helpRequested.connect(self.show_help) self.finished.connect(self.finished_slot) self.btn_test_connection.clicked.connect(self.test_connection) self.btn_test_ladm_col_structure.clicked.connect( self.test_ladm_col_structure) self.btn_test_service.clicked.connect(self.test_service) self.chk_use_roads.toggled.connect(self.update_images_state) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.cbo_db_source.clear() self._lst_db = self.conf_db.get_db_items() self._lst_panel = dict() for key, value in self._lst_db.items(): self.cbo_db_source.addItem(value.get_name(), key) self._lst_panel[key] = value.get_config_panel() self._lst_panel[key].notify_message_requested.connect( self.show_message) self.db_layout.addWidget(self._lst_panel[key]) self.db_source_changed() # Trigger some default behaviours self.restore_settings() self.cbo_db_source.currentIndexChanged.connect(self.db_source_changed) def showEvent(self, event): # It is necessary to reload the variables # to load the database and schema name self.restore_settings() self.btn_test_ladm_col_structure.setVisible( self._action_type != EnumDbActionType.SCHEMA_IMPORT) def model_provider_toggle(self): if self.offline_models_radio_button.isChecked(): self.custom_model_directories_line_edit.setVisible(True) self.custom_models_dir_button.setVisible(True) else: self.custom_model_directories_line_edit.setVisible(False) self.custom_models_dir_button.setVisible(False) self.custom_model_directories_line_edit.setText("") def _get_db_connector_from_gui(self): current_db = self.cbo_db_source.currentData() params = self._lst_panel[current_db].read_connection_parameters() db = self._lst_db[current_db].get_db_connector(params) return db def get_db_connection(self): if self._db is not None: self.log.logMessage("Returning existing db connection...", PLUGIN_NAME, Qgis.Info) else: self.log.logMessage("Getting new db connection...", PLUGIN_NAME, Qgis.Info) self._db = self._get_db_connector_from_gui() return self._db def show_custom_model_dir(self): dlg = CustomModelDirDialog( self.custom_model_directories_line_edit.text(), self) dlg.exec_() def accepted(self): current_db = self.cbo_db_source.currentData() if self._lst_panel[current_db].state_changed(): valid_connection = True ladm_col_schema = False db = self._get_db_connector_from_gui() test_level = EnumTestLevel.DB_SCHEMA if self._action_type == EnumDbActionType.SCHEMA_IMPORT: # Limit the validation (used in GeoPackage) test_level |= EnumTestLevel.CREATE_SCHEMA res, msg = db.test_connection(test_level=test_level) if res: if self._action_type != EnumDbActionType.SCHEMA_IMPORT: # Don't check if it's a LADM schema, we expect it to be after the schema import ladm_col_schema, msg = db.test_connection( test_level=EnumTestLevel.LADM) else: self.show_message(msg, Qgis.Warning) valid_connection = False if valid_connection: if self._db is not None: self._db.close_connection() # FIXME is it overwriting itself? self._db = None self._db = self.get_db_connection() self.db_connection_changed.emit(self._db, ladm_col_schema) self.save_settings() QDialog.accept(self) else: return # Do not close the dialog else: # Save settings from tabs other than database connection self.save_settings() QDialog.accept(self) if self.chk_ant_tools.isChecked() != self.ant_tools_initial_chk_value: self.organization_tools_changed.emit(NATIONAL_LAND_AGENCY) def reject(self): self.done(0) def finished_slot(self, result): self.bar.clearWidgets() def set_db_connection(self, mode, dict_conn): """ To be used by external scripts and unit tests """ self.cbo_db_source.setCurrentIndex(self.cbo_db_source.findData(mode)) self.db_source_changed() current_db = self.cbo_db_source.currentData() self._lst_panel[current_db].write_connection_parameters(dict_conn) self.accepted() # Create/update the db object def save_settings(self): settings = QSettings() settings.setValue('Asistente-LADM_COL/db_connection_source', self.cbo_db_source.currentData()) # Save QSettings current_db = self.cbo_db_source.currentData() dict_conn = self._lst_panel[current_db].read_connection_parameters() for key, value in dict_conn.items(): settings.setValue('Asistente-LADM_COL/' + current_db + '/' + key, value) settings.setValue( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', self.offline_models_radio_button.isChecked()) if self.offline_models_radio_button.isChecked(): settings.setValue('Asistente-LADM_COL/models/custom_models', self.custom_model_directories_line_edit.text()) settings.setValue( 'Asistente-LADM_COL/quality/too_long_tolerance', int(self.txt_too_long_tolerance.text()) or DEFAULT_TOO_LONG_BOUNDARY_SEGMENTS_TOLERANCE) settings.setValue('Asistente-LADM_COL/quality/use_roads', self.chk_use_roads.isChecked()) settings.setValue( 'Asistente-LADM_COL/automatic_values/automatic_values_in_batch_mode', self.chk_automatic_values_in_batch_mode.isChecked()) settings.setValue('Asistente-LADM_COL/sources/document_repository', self.connection_box.isChecked()) settings.setValue('Asistente-LADM_COL/advanced_settings/ant_tools', self.chk_ant_tools.isChecked()) settings.setValue( 'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting', self.chk_validate_data_importing_exporting.isChecked()) endpoint = self.txt_service_endpoint.text().strip() settings.setValue( 'Asistente-LADM_COL/sources/service_endpoint', (endpoint[:-1] if endpoint.endswith('/') else endpoint) or DEFAULT_ENDPOINT_SOURCE_SERVICE) # Changes in automatic namespace or local_id configuration? current_namespace_enabled = settings.value( 'Asistente-LADM_COL/automatic_values/namespace_enabled', True, bool) current_namespace_prefix = settings.value( 'Asistente-LADM_COL/automatic_values/namespace_prefix', "") current_local_id_enabled = settings.value( 'Asistente-LADM_COL/automatic_values/local_id_enabled', True, bool) settings.setValue( 'Asistente-LADM_COL/automatic_values/namespace_enabled', self.namespace_collapsible_group_box.isChecked()) if self.namespace_collapsible_group_box.isChecked(): settings.setValue( 'Asistente-LADM_COL/automatic_values/namespace_prefix', self.txt_namespace.text()) settings.setValue( 'Asistente-LADM_COL/automatic_values/local_id_enabled', self.chk_local_id.isChecked()) if current_namespace_enabled != self.namespace_collapsible_group_box.isChecked() or \ current_namespace_prefix != self.txt_namespace.text() or \ current_local_id_enabled != self.chk_local_id.isChecked(): self.qgis_utils.automatic_namespace_local_id_configuration_changed( self._db) def _restore_settings_db(self): settings = QSettings() # reload all panels for index_db, item_db in self._lst_panel.items(): dict_conn = dict() keys = item_db.get_keys_connection_parameters() for key in keys: dict_conn[key] = settings.value('Asistente-LADM_COL/' + index_db + '/' + key) item_db.write_connection_parameters(dict_conn) item_db.save_state() def restore_settings(self): # Restore QSettings settings = QSettings() default_db = self.conf_db.id_default_db index_db = self.cbo_db_source.findData( settings.value('Asistente-LADM_COL/db_connection_source', default_db)) if index_db == -1: index_db = self.cbo_db_source.findData(default_db) self.cbo_db_source.setCurrentIndex(index_db) self.db_source_changed() self._restore_settings_db() custom_model_directories_is_checked = settings.value( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', type=bool) if custom_model_directories_is_checked: self.offline_models_radio_button.setChecked(True) self.custom_model_directories_line_edit.setText( settings.value('Asistente-LADM_COL/models/custom_models')) self.custom_model_directories_line_edit.setVisible(True) self.custom_models_dir_button.setVisible(True) else: self.online_models_radio_button.setChecked(True) self.custom_model_directories_line_edit.setText("") self.custom_model_directories_line_edit.setVisible(False) self.custom_models_dir_button.setVisible(False) self.txt_too_long_tolerance.setText( str( settings.value('Asistente-LADM_COL/quality/too_long_tolerance', DEFAULT_TOO_LONG_BOUNDARY_SEGMENTS_TOLERANCE))) use_roads = settings.value('Asistente-LADM_COL/quality/use_roads', True, bool) self.chk_use_roads.setChecked(use_roads) self.update_images_state(use_roads) self.chk_automatic_values_in_batch_mode.setChecked( settings.value( 'Asistente-LADM_COL/automatic_values/automatic_values_in_batch_mode', True, bool)) self.connection_box.setChecked( settings.value('Asistente-LADM_COL/sources/document_repository', True, bool)) self.namespace_collapsible_group_box.setChecked( settings.value( 'Asistente-LADM_COL/automatic_values/namespace_enabled', True, bool)) self.chk_local_id.setChecked( settings.value( 'Asistente-LADM_COL/automatic_values/local_id_enabled', True, bool)) self.txt_namespace.setText( str( settings.value( 'Asistente-LADM_COL/automatic_values/namespace_prefix', ""))) self.ant_tools_initial_chk_value = settings.value( 'Asistente-LADM_COL/advanced_settings/ant_tools', False, bool) self.chk_ant_tools.setChecked(self.ant_tools_initial_chk_value) self.chk_validate_data_importing_exporting.setChecked( settings.value( 'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting', True, bool)) self.txt_service_endpoint.setText( settings.value('Asistente-LADM_COL/sources/service_endpoint', DEFAULT_ENDPOINT_SOURCE_SERVICE)) def db_source_changed(self): if self._db is not None: self._db.close_connection() self._db = None # Reset db connection for key, value in self._lst_panel.items(): value.setVisible(False) current_db = self.cbo_db_source.currentData() self._lst_panel[current_db].setVisible(True) def test_connection(self): db = self._get_db_connector_from_gui() test_level = EnumTestLevel.DB_SCHEMA if self._action_type == EnumDbActionType.SCHEMA_IMPORT: test_level |= EnumTestLevel.CREATE_SCHEMA res, msg = db.test_connection(test_level=test_level) if db is not None: db.close_connection() self.show_message(msg, Qgis.Info if res else Qgis.Warning) self.log.logMessage("Test connection!", PLUGIN_NAME, Qgis.Info) def test_ladm_col_structure(self): db = self._get_db_connector_from_gui() test_level = EnumTestLevel.LADM res, msg = db.test_connection(test_level=test_level) if db is not None: db.close_connection() self.show_message(msg, Qgis.Info if res else Qgis.Warning) self.log.logMessage("Test connection!", PLUGIN_NAME, Qgis.Info) def test_service(self): self.setEnabled(False) QCoreApplication.processEvents() res, msg = self.is_source_service_valid() self.setEnabled(True) self.show_message(msg['text'], msg['level']) def is_source_service_valid(self): res = False msg = {'text': '', 'level': Qgis.Warning} url = self.txt_service_endpoint.text().strip() if url: with OverrideCursor(Qt.WaitCursor): self.qgis_utils.status_bar_message_emitted.emit( "Checking source service availability (this might take a while)...", 0) QCoreApplication.processEvents() if self.qgis_utils.is_connected(TEST_SERVER): nam = QNetworkAccessManager() request = QNetworkRequest(QUrl(url)) reply = nam.get(request) loop = QEventLoop() reply.finished.connect(loop.quit) loop.exec_() allData = reply.readAll() response = QTextStream(allData, QIODevice.ReadOnly) status = reply.attribute( QNetworkRequest.HttpStatusCodeAttribute) if status == 200: try: data = json.loads(response.readAll()) if 'id' in data and data[ 'id'] == SOURCE_SERVICE_EXPECTED_ID: res = True msg['text'] = QCoreApplication.translate( "SettingsDialog", "The tested service is valid to upload files!" ) msg['level'] = Qgis.Info else: res = False msg['text'] = QCoreApplication.translate( "SettingsDialog", "The tested upload service is not compatible: no valid 'id' found in response." ) except json.decoder.JSONDecodeError as e: res = False msg['text'] = QCoreApplication.translate( "SettingsDialog", "Response from the tested service is not compatible: not valid JSON found." ) else: res = False msg['text'] = QCoreApplication.translate( "SettingsDialog", "There was a problem connecting to the server. The server might be down or the service cannot be reached at the given URL." ) else: res = False msg['text'] = QCoreApplication.translate( "SettingsDialog", "There was a problem connecting to Internet.") self.qgis_utils.clear_status_bar_emitted.emit() else: res = False msg['text'] = QCoreApplication.translate( "SettingsDialog", "Not valid service URL to test!") return (res, msg) def show_message(self, message, level): self.bar.pushMessage(message, level, 10) def update_images_state(self, checked): self.img_with_roads.setEnabled(checked) self.img_with_roads.setToolTip( QCoreApplication.translate( "SettingsDialog", "Missing roads will be marked as errors." ) if checked else '') self.img_without_roads.setEnabled(not checked) self.img_without_roads.setToolTip( '' if checked else QCoreApplication.translate( "SettingsDialog", "Missing roads will not be marked as errors." )) def show_help(self): self.qgis_utils.show_help("settings") def set_action_type(self, action_type): self._action_type = action_type for key, value in self._lst_panel.items(): value.set_action(action_type)
class AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(self.getParametersPanel(alg, self)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.cornerWidget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 5) self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }") self.runAsBatchButton = QPushButton(self.tr("Run as batch process...")) self.runAsBatchButton.clicked.connect(self.runAsBatch) layout.addWidget(self.runAsBatchButton) self.cornerWidget.setLayout(layout) self.tabWidget.setCornerWidget(self.cornerWidget) def getParametersPanel(self, alg, parent): return ParametersPanel(parent, alg) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def getParamValues(self): parameters = {} for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if not param.isDestination(): wrapper = self.mainWidget.wrappers[param.name()] value = None if wrapper.widget: value = wrapper.value() parameters[param.name()] = value if not param.checkValueIsAcceptable(value): raise AlgorithmDialogBase.InvalidParameterValue( param, wrapper.widget) else: dest_project = None if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \ isinstance(param, (QgsProcessingParameterRasterOutput, QgsProcessingParameterFeatureSink, OutputTable)): if self.mainWidget.checkBoxes[param.name()].isChecked(): dest_project = QgsProject.instance() value = self.mainWidget.outputWidgets[param.name()].getValue() if value: value.destinationProject = dest_project parameters[param.name()] = value return parameters def checkExtentCRS(self): unmatchingCRS = False hasExtent = False context = dataobjects.createContext() projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance()) for param in self.alg.parameterDefinitions(): if isinstance( param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = QgsProcessingUtils.mapLayerFromString( inputlayer, context) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): if param.skip_crs_check: continue value = self.mainWidget.wrappers[ param.name()].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): super(AlgorithmDialog, self)._saveGeometry() context = dataobjects.createContext() checkCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_CRS) try: parameters = self.getParamValues() QgsMessageLog.logMessage(str(parameters), 'Processing', QgsMessageLog.CRITICAL) # TODO if False and checkCRS and not self.alg.checkInputCRS(): reply = QMessageBox.question( self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) #TODO if False and checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question( self, self.tr("Extent CRS"), self. tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.alg.checkParameterValues(parameters, context) if msg: QMessageBox.warning(self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.setInfo( self.tr('<b>Algorithm {0} starting...</b>').format( self.alg.displayName())) if self.iterateParam: if executeIterating(self.alg, parameters, self.iterateParam, context, self.feedback): self.finish(parameters, context) else: QApplication.restoreOverrideCursor() self.resetGUI() else: # TODO #command = self.alg.getAsCommand() #if command: # ProcessingLog.addToLog(command) self.buttonCancel.setEnabled( self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) result = executeAlgorithm(self.alg, parameters, context, self.feedback) self.buttonCancel.setEnabled(False) self.finish(result, context) #TODO #else: # QApplication.restoreOverrideCursor() # self.resetGUI() except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect( lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage( "", self.tr("Wrong or missing parameter value: {0}").format( e.parameter.description()), level=QgsMessageBar.WARNING, duration=5) def finish(self, result, context): keepOpen = ProcessingConfig.getSetting( ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: if not handleAlgorithmResults(self.alg, context, self.feedback, not keepOpen): self.resetGUI() return self.executed = True self.setInfo( self.tr('Algorithm {0} finished').format(self.alg.displayName())) QApplication.restoreOverrideCursor() if not keepOpen: self.close() else: self.resetGUI() if self.alg.hasHtmlOutputs(): self.setInfo( self.tr('HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.')) def closeEvent(self, event): QgsProject.instance().layerWasAdded.disconnect( self.mainWidget.layerRegistryChanged) QgsProject.instance().layersWillBeRemoved.disconnect( self.mainWidget.layerRegistryChanged) super(AlgorithmDialog, self).closeEvent(event)
class AssociateExtAddressWizard(QWizard, WIZARD_UI): def __init__(self, iface, db, qgis_utils, parent=None): QWizard.__init__(self, parent) self.setupUi(self) self.iface = iface self.log = QgsApplication.messageLog() self._db = db self.qgis_utils = qgis_utils self.canvas = self.iface.mapCanvas() self.maptool = self.canvas.mapTool() self.select_maptool = None self.help_strings = HelpStrings() self.translatable_config_strings = TranslatableConfigStrings() self._extaddress_layer = None self._plot_layer = None self._building_layer = None self._building_unit_layer = None self._current_layer = None self._feature_tid = None self._extaddress_tid = None self.restore_settings() self.rad_to_plot.toggled.connect(self.adjust_page_1_controls) self.rad_to_building.toggled.connect(self.adjust_page_1_controls) self.rad_to_building_unit.toggled.connect(self.adjust_page_1_controls) self.adjust_page_1_controls() self.button(QWizard.NextButton).clicked.connect(self.prepare_selection) self.button(QWizard.FinishButton).clicked.connect(self.finished_dialog) self.button(QWizard.HelpButton).clicked.connect(self.show_help) self.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.PolygonLayer) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def adjust_page_1_controls(self): self.cbo_mapping.clear() self.cbo_mapping.addItem("") self.cbo_mapping.addItems(self.qgis_utils.get_field_mappings_file_names(EXTADDRESS_TABLE)) if self.rad_refactor.isChecked(): self.lbl_refactor_source.setEnabled(True) self.mMapLayerComboBox.setEnabled(True) self.lbl_field_mapping.setEnabled(True) self.cbo_mapping.setEnabled(True) disable_next_wizard(self) FinishButton_text = QCoreApplication.translate("AssociateExtAddressWizard", "Import") self.txt_help_page_1.setHtml(self.help_strings.get_refactor_help_string(EXTADDRESS_TABLE, True)) self.wizardPage1.setFinalPage(True) self.wizardPage1.setButtonText(QWizard.FinishButton, QCoreApplication.translate("AssociateExtAddressWizard", FinishButton_text)) elif self.rad_to_plot.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) self.lbl_field_mapping.setEnabled(False) self.cbo_mapping.setEnabled(False) self.wizardPage1.setFinalPage(False) enable_next_wizard(self) FinishButton_text = QCoreApplication.translate("AssociateExtAddressWizard", "Associate Plot ExtAddress") self.txt_help_page_1.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_1) elif self.rad_to_building.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) self.lbl_field_mapping.setEnabled(False) self.cbo_mapping.setEnabled(False) self.wizardPage1.setFinalPage(False) enable_next_wizard(self) FinishButton_text = QCoreApplication.translate("AssociateExtAddressWizard", "Associate Building ExtAddress") self.txt_help_page_1.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_2) else: # self.rad_to_building_unit.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) self.lbl_field_mapping.setEnabled(False) self.cbo_mapping.setEnabled(False) self.wizardPage1.setFinalPage(False) enable_next_wizard(self) FinishButton_text = QCoreApplication.translate("AssociateExtAddressWizard", "Associate Building Unit ExtAddress") self.txt_help_page_1.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_3) self.wizardPage2.setButtonText(QWizard.FinishButton, QCoreApplication.translate('AssociateExtAddressWizard', FinishButton_text)) def prepare_selection(self): self.button(self.FinishButton).setDisabled(True) if self.rad_to_plot.isChecked(): self.btn_select.setText(QCoreApplication.translate("AssociateExtAddressWizard", "Select Plot")) self.txt_help_page_2.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_1) # Load layers res_layers = self.qgis_utils.get_layers(self._db, { EXTADDRESS_TABLE: {'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry}, OID_TABLE: {'name': OID_TABLE, 'geometry': None}, PLOT_TABLE: {'name': PLOT_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry} }, load=True) self._extaddress_layer = res_layers[EXTADDRESS_TABLE] self._oid_layer = res_layers[OID_TABLE] self._plot_layer = res_layers[PLOT_TABLE] self._current_layer = self._plot_layer elif self.rad_to_building.isChecked(): self.btn_select.setText(QCoreApplication.translate("AssociateExtAddressWizard", "Select Building")) self.txt_help_page_2.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_2) # Load layers res_layers = self.qgis_utils.get_layers(self._db, { EXTADDRESS_TABLE: {'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry}, OID_TABLE: {'name': OID_TABLE, 'geometry': None}, BUILDING_TABLE: {'name': BUILDING_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry} }, load=True) self._extaddress_layer = res_layers[EXTADDRESS_TABLE] self._building_layer = res_layers[BUILDING_TABLE] self._oid_layer = res_layers[OID_TABLE] self._current_layer = self._building_layer else: # self.rad_to_building_unit.isChecked(): self.btn_select.setText(QCoreApplication.translate("AssociateExtAddressWizard", "Select Building Unit")) self.txt_help_page_2.setHtml(self.help_strings.WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_3) # Load layers res_layers = self.qgis_utils.get_layers(self._db, { EXTADDRESS_TABLE: {'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry}, OID_TABLE: {'name': OID_TABLE, 'geometry': None}, BUILDING_UNIT_TABLE: {'name': BUILDING_UNIT_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry} }, load=True) self._extaddress_layer = res_layers[EXTADDRESS_TABLE] self._building_unit_layer = res_layers[BUILDING_UNIT_TABLE] self._oid_layer = res_layers[OID_TABLE] self._current_layer = self._building_unit_layer self.iface.setActiveLayer(self._current_layer) self.check_selected_features() self.btn_select.clicked.connect(self.select_feature) self.btn_select_by_expression.clicked.connect(self.select_feature_by_expression) def check_selected_features(self): self.bar.clearWidgets() if self._current_layer.selectedFeatureCount() == 1: self.lbl_selected.setText(QCoreApplication.translate("AssociateExtAddressWizard", "1 Feature Selected")) self.button(self.FinishButton).setDisabled(False) self._feature_tid = self._current_layer.selectedFeatures()[0][ID_FIELD] self.canvas.zoomToSelected(self._current_layer) elif self._current_layer.selectedFeatureCount() > 1: self.show_message(QCoreApplication.translate("AssociateExtAddressWizard", "Please select just one feature"), Qgis.Warning) self.lbl_selected.setText(QCoreApplication.translate("AssociateExtAddressWizard", "{} Feature(s) Selected".format( self._current_layer.selectedFeatureCount()))) self.button(self.FinishButton).setDisabled(True) else: self.lbl_selected.setText(QCoreApplication.translate("AssociateExtAddressWizard", "0 Features Selected")) self.button(self.FinishButton).setDisabled(True) def select_feature_by_expression(self): Dlg_expression_selection = QgsExpressionSelectionDialog(self._current_layer) self._current_layer.selectionChanged.connect(self.check_selected_features) Dlg_expression_selection.exec() self._current_layer.selectionChanged.disconnect(self.check_selected_features) def select_feature(self): self.setVisible(False) # Make wizard disappear # Create maptool self.select_maptool = SelectMapTool(self.canvas, self._current_layer, multi=False) self.canvas.setMapTool(self.select_maptool) self.select_maptool.features_selected_signal.connect(self.feature_selected) # TODO: Take into account that a user can select another tool def feature_selected(self): self.setVisible(True) # Make wizard appear feature = self._current_layer.selectedFeatures()[0] # Get selected feature if feature: self.lbl_selected.setText(QCoreApplication.translate("AssociateExtAddressWizard", "1 Feature Selected")) self._current_layer.selectByIds([feature.id()]) self.canvas.setMapTool(self.maptool) self.check_selected_features() self.select_maptool.features_selected_signal.disconnect(self.feature_selected) self.log.logMessage("Spatial Unit's featureIdentified SIGNAL disconnected", PLUGIN_NAME, Qgis.Info) def finished_dialog(self): self.save_settings() if self.rad_refactor.isChecked(): if self.mMapLayerComboBox.currentLayer() is not None: field_mapping = self.cbo_mapping.currentText() res_etl_model = self.qgis_utils.show_etl_model(self._db, self.mMapLayerComboBox.currentLayer(), EXTADDRESS_TABLE, field_mapping=field_mapping) if res_etl_model: if field_mapping: self.qgis_utils.delete_old_field_mapping(field_mapping) self.qgis_utils.save_field_mapping(EXTADDRESS_TABLE) else: self.iface.messageBar().pushMessage('Asistente LADM_COL', QCoreApplication.translate("AssociateExtAddressWizard", "Select a source layer to set the field mapping to '{}'.").format( EXTADDRESS_TABLE), Qgis.Warning) else: self.prepare_extaddress_creation() def prepare_extaddress_creation(self): # Don't suppress (i.e., show) feature form form_config = self._extaddress_layer.editFormConfig() form_config.setSuppress(QgsEditFormConfig.SuppressOff) self._extaddress_layer.setEditFormConfig(form_config) # Suppress (i.e., hide) feature form form_config = self._oid_layer.editFormConfig() form_config.setSuppress(QgsEditFormConfig.SuppressOn) self._oid_layer.setEditFormConfig(form_config) self.edit_extaddress() def edit_extaddress(self): if self._current_layer.selectedFeatureCount() == 1: # Open Form self.iface.layerTreeView().setCurrentLayer(self._extaddress_layer) self._extaddress_layer.startEditing() self.iface.actionAddFeature().trigger() # Create connections to react when a feature is added to buffer and # when it gets stored into the DB self._extaddress_layer.featureAdded.connect(self.call_extaddress_commit) else: self.iface.messageBar().pushMessage("Asistente LADM_COL", QCoreApplication.translate("AssociateExtAddressWizard", "Please select a feature"), Qgis.Warning) def call_extaddress_commit(self, fid): plot_field_idx = self._extaddress_layer.getFeature(fid).fieldNameIndex(EXTADDRESS_PLOT_FIELD) building_field_idx = self._extaddress_layer.getFeature(fid).fieldNameIndex(EXTADDRESS_BUILDING_FIELD) building_unit_field_idx = self._extaddress_layer.getFeature(fid).fieldNameIndex(EXTADDRESS_BUILDING_UNIT_FIELD) self._extaddress_tid = self._extaddress_layer.getFeature(fid)[ID_FIELD] if self._current_layer.name() == PLOT_TABLE: self._extaddress_layer.changeAttributeValue(fid, plot_field_idx, self._feature_tid) elif self._current_layer.name() == BUILDING_TABLE: self._extaddress_layer.changeAttributeValue(fid, building_field_idx, self._feature_tid) else: # self._current_layer.name() == BUILDING_UNIT_TABLE: self._extaddress_layer.changeAttributeValue(fid, building_unit_field_idx, self._feature_tid) self._extaddress_layer.featureAdded.disconnect(self.call_extaddress_commit) self.log.logMessage("Extaddress's featureAdded SIGNAL disconnected", PLUGIN_NAME, Qgis.Info) res = self._extaddress_layer.commitChanges() self._current_layer.removeSelection() self.add_oid_feature() def add_oid_feature(self): # Add OID record self._oid_layer.startEditing() feature = QgsVectorLayerUtils().createFeature(self._oid_layer) feature.setAttribute(OID_EXTADDRESS_ID_FIELD, self._extaddress_tid) self._oid_layer.addFeature(feature) self._oid_layer.commitChanges() def show_message(self, message, level): self.bar.pushMessage(message, level, 0) def save_settings(self): settings = QSettings() load_data_type = 'refactor' if self.rad_to_plot.isChecked(): load_data_type = 'to_plot' elif self.rad_to_building.isChecked(): load_data_type = 'to_building' else: # self.rad_to_building_unit.isChecked(): load_data_type = 'to_building_unit' settings.setValue('Asistente-LADM_COL/wizards/ext_address_load_data_type', load_data_type) def restore_settings(self): settings = QSettings() load_data_type = settings.value('Asistente-LADM_COL/wizards/ext_address_load_data_type', 'to_plot') if load_data_type == 'refactor': self.rad_refactor.setChecked(True) elif load_data_type == 'to_plot': self.rad_to_plot.setChecked(True) elif load_data_type == 'to_building': self.rad_to_building.setChecked(True) else: # load_data_type == 'to_building_unit': self.rad_to_building_unit.setChecked(True) def show_help(self): self.qgis_utils.show_help("associate_ext_address")
class BackupedImgUploaderWizard(QtGui.QWizard, FORM_CLASS): def __init__(self, iface, settings, parent=None): """Constructor.""" super(BackupedImgUploaderWizard, 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.iface = iface self.setupUi(self) # Message bars need to be attached to pages, since the wizard object # does not have a layout. It doesn't work to attach the same bar # object to all pages (it is only shown in the last one). The only way # I could make it work was to create different QgsMessageBar objects, # one per page, but it is very to keep track of those references # along the code. It is messy, there should be a better solution. self.bar0 = QgsMessageBar() self.bar0.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(0).layout().addWidget(self.bar0) self.bar1 = QgsMessageBar() self.bar1.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(1).layout().addWidget(self.bar1) self.bar2 = QgsMessageBar() self.bar2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(2).layout().addWidget(self.bar2) self.setButtonText(QtGui.QWizard.CustomButton1, self.tr("&Start upload")); self.setOption(QtGui.QWizard.HaveCustomButton1, True); self.settings = settings # Dictionaries to save imagery info (todo: defined as a classes in the future) self.metadata = {} self.reprojected = [] self.licensed = [] # Initialize layers and default settings self.loadLayers() self.loadMetadataSettings() self.loadStorageSettings() self.loadOptionsSettings() # register event handlers """ List of page navigation buttons in QWizard, for reference. Please comment out and implement following functions if necessary.""" #self.button(QWizard.BackButton).clicked.connect(self.previousPage) #self.button(QWizard.NextButton).clicked.connect(self.nextPage) #self.button(QWizard.FinishButton).clicked.connect(self.finishWizard) #self.button(QWizard.CancelButton).clicked.connect(self.cancelWizard) # Imagery connections (wizard page 1) self.layers_tool_button.clicked.connect(self.loadLayers) self.file_tool_button.clicked.connect(self.selectFile) self.add_source_button.clicked.connect(self.addSources) self.remove_source_button.clicked.connect(self.removeSources) self.up_source_button.clicked.connect(self.upSource) self.down_source_button.clicked.connect(self.downSource) # Metadata connections (wizard page 2) self.sense_start_edit.setCalendarPopup(1) self.sense_start_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.sense_end_edit.setCalendarPopup(1) self.sense_end_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.default_button.clicked.connect(self.loadMetadataSettings) self.clean_button.clicked.connect(self.cleanMetadataSettings) self.save_button.clicked.connect(self.saveMetadata) # Upload tab connections (wizard page 3) self.storage_combo_box.currentIndexChanged.connect(self.enableSpecify) self.customButtonClicked.connect(self.startUploader) # event handling for wizard page 1 def loadLayers(self): all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if not self.layers_list_widget.findItems(layer.name(),Qt.MatchExactly): item = QListWidgetItem() item.setText(layer.name()) item.setData(Qt.UserRole, layer.dataProvider().dataSourceUri()) self.layers_list_widget.addItem(item) def selectFile(self): selected_file = QFileDialog.getOpenFileName( self, 'Select imagery file', os.path.expanduser("~")) self.source_file_edit.setText(selected_file) def addSources(self): filename = self.source_file_edit.text() selected_layers = self.layers_list_widget.selectedItems() if not filename and not selected_layers: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'Either a layer or file should be selected to be added', level=QgsMessageBar.WARNING) else: added = False if filename: if self.validateFile(filename): if not self.sources_list_widget.findItems(filename,Qt.MatchExactly): item = QListWidgetItem() item.setText(os.path.basename(filename)) item.setData(Qt.UserRole, filename) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) self.source_file_edit.setText('') added = True if selected_layers: for item in selected_layers: if self.validateLayer(item.text()): if not self.sources_list_widget.findItems(item.text(),Qt.MatchExactly): self.layers_list_widget.takeItem(self.layers_list_widget.row(item)) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) added = True if added: self.bar0.clearWidgets() self.bar0.pushMessage( 'INFO', 'Source(s) added to the upload queue', level=QgsMessageBar.INFO) self.loadMetadataReviewBox() def removeSources(self): selected_sources = self.sources_list_widget.selectedItems() added_sources = self.added_sources_list_widget.selectedItems() if selected_sources: for item in selected_sources: self.sources_list_widget.takeItem(self.sources_list_widget.row(item)) added_item = self.added_sources_list_widget.findItems(item.text(),Qt.MatchExactly) if added_item: self.added_sources_list_widget.takeItem(self.added_sources_list_widget.row(added_item[0])) all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if item.text() == layer.name(): if not self.layers_list_widget.findItems(item.text(),Qt.MatchExactly): self.layers_list_widget.addItem(item) else: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'An imagery source must be selected to be removed', level=QgsMessageBar.WARNING) def upSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position > 0: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position-1,item) item.setSelected(1) def downSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position < self.sources_list_widget.count()-1: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position+1,item) item.setSelected(1) # event handling for wizard page 2 def loadMetadataSettings(self): self.settings.beginGroup("Metadata") self.title_edit.setText(self.settings.value('TITLE')) if self.settings.value('PLATFORM') == None: self.platform_combo_box.setCurrentIndex(0) else: self.platform_combo_box.setCurrentIndex(int(self.settings.value('PLATFORM'))) self.sensor_edit.setText(self.settings.value('SENSOR')) self.sensor_edit.setCursorPosition(0) """ self.sense_start_edit.setDateTime(QDateTime(self.settings.value('SENSE_START'))) self.sense_end_edit.setDateTime(QDateTime(self.settings.value('SENSE_END'))) """ self.sense_start_edit.setDate(QDateTime.fromString( self.settings.value('SENSE_START'), Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_START'), Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).time()) self.tags_edit.setText(self.settings.value('TAGS')) self.tags_edit.setCursorPosition(0) self.provider_edit.setText(self.settings.value('PROVIDER')) self.provider_edit.setCursorPosition(0) self.contact_edit.setText(self.settings.value('CONTACT')) self.contact_edit.setCursorPosition(0) self.website_edit.setText(self.settings.value('WEBSITE')) self.website_edit.setCursorPosition(0) """ Boolean values are converted into string and lower case for 'if' statement, since PyQt sometimes returns 'true', just like C++, instead of 'True', Python style. Maybe we can use integer values (0 or 1), instead of using string. """ if str(self.settings.value('LICENSE')).lower() == 'true': self.license_check_box.setCheckState(2) if str(self.settings.value('REPROJECT')).lower() == 'true': self.reproject_check_box.setCheckState(2) self.settings.endGroup() def cleanMetadataSettings(self): self.title_edit.setText('') self.platform_combo_box.setCurrentIndex(0) self.sensor_edit.setText('') self.sense_start_edit.setDate( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime().fromString('1970-01-01T00:00:00',Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime().fromString('1970-01-01T00:00:00',Qt.ISODate).time()) self.tags_edit.setText('') self.provider_edit.setText('') self.contact_edit.setText('') self.website_edit.setText('') self.license_check_box.setCheckState(0) self.reproject_check_box.setCheckState(0) def saveMetadata(self): selected_layers = self.added_sources_list_widget.selectedItems() if selected_layers: for item in selected_layers: filename = item.data(Qt.UserRole) self.loadImageryInfo(filename) json_string = json.dumps(self.metadata[filename],indent=4,separators=(',', ': ')) if filename not in self.reprojected: json_filename = os.path.splitext(filename)[0]+'.json' else: # to avoid repetition of "EPSG3857" in filename: if not "EPSG3857" in filename: json_filename = os.path.splitext(filename)[0]+'_EPSG3857.json' json_file = open(json_filename, 'w') print >> json_file, json_string json_file.close() self.loadMetadataReviewBox() self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Metadata for the selected sources were saved', level=QgsMessageBar.INFO) else: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'One or more source imagery should be selected to have the metadata saved', level=QgsMessageBar.WARNING) # event handling for wizard page 3 # also see multi-thread for startUploader function def enableSpecify(self): if self.storage_combo_box.currentIndex() == 1: self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) else: self.specify_label.setEnabled(0) self.specify_edit.setText('') self.specify_edit.setEnabled(0) def loadStorageSettings(self): self.settings.beginGroup("Storage") bucket = self.settings.value('S3_BUCKET_NAME') storage_index = self.storage_combo_box.findText(bucket,Qt.MatchExactly) if not storage_index == -1: self.storage_combo_box.setCurrentIndex(storage_index) else: self.storage_combo_box.setCurrentIndex(self.storage_combo_box.findText(self.tr('other...'))) self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) self.specify_edit.setText(self.settings.value('S3_BUCKET_NAME')) self.key_id_edit.setText(self.settings.value('AWS_ACCESS_KEY_ID')) self.key_id_edit.setCursorPosition(0) self.secret_key_edit.setText(self.settings.value('AWS_SECRET_ACCESS_KEY')) self.secret_key_edit.setCursorPosition(0) self.settings.endGroup() def loadOptionsSettings(self): self.settings.beginGroup("Options") """ Boolean values are converted into string and lower case for 'if' statement, since PyQt sometimes returns 'true', just like C++, instead of 'True', Python style. Maybe we can use integer values (0 or 1), instead of using string. """ if str(self.settings.value('NOTIFY_OAM')).lower() == 'true': self.notify_oam_check.setCheckState(2) if str(self.settings.value('TRIGGER_OAM_TS')).lower() == 'true': self.trigger_tiling_check.setCheckState(2) self.settings.endGroup() # other functions def validateFile(self,filename): # check that file exists if not os.path.exists(filename): self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "The file %s does not exist" % filename, level=QgsMessageBar.CRITICAL) return False # check that file is an image if imghdr.what(filename) is None: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "The file %s is not a supported data source" % filename, level=QgsMessageBar.CRITICAL) return False # check if gdal can read file try: raster = gdal.Open(filename,gdal.GA_ReadOnly) except: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "GDAL could not read file %s" % filename, level=QgsMessageBar.CRITICAL) return False # check if there is an object raster if not raster: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "GDAL could not read file %s" % filename, level=QgsMessageBar.CRITICAL) return False # check that image has at least 3 bands if raster.RasterCount < 3: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "The file %s has less than 3 raster bands" % filename, level=QgsMessageBar.CRITICAL) return False # check if projection is set if raster.GetProjection() is '': self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "Could not extract projection from file %s" % filename, level=QgsMessageBar.CRITICAL) return False # finally, check if bbox has valid data xy_points = [(0.0,0.0),(0.0,raster.RasterYSize),(raster.RasterXSize,0.0),(raster.RasterXSize,raster.RasterYSize)] for point in xy_points: if point != self.GDALInfoReportCorner(raster,point[0],point[1]): QgsMessageLog.logMessage( 'File %s is a valid data source' % filename, 'OAM', level=QgsMessageLog.INFO) return True def validateLayer(self,layer_name): all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if layer_name == layer.name(): if layer.type() == QgsMapLayer.VectorLayer: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "Vector layers cannot be selected for upload", level=QgsMessageBar.CRITICAL) return 0 else: return 1 def extractMetadata(self,filename): """Extract filesize, projection, bbox and gsd from image file""" self.metadata[filename]['File size'] = os.stat(filename).st_size datafile = gdal.Open(filename,gdal.GA_ReadOnly) if datafile is None: self.bar1.clearWidgets() self.bar1.pushMessage( 'CRITICAL', 'Extraction of raster metadata failed', level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage( 'Failed to extract metadata', 'OAM', level=QgsMessageLog.CRITICAL) else: # extract projection projInfo = datafile.GetProjection() # WKT format spatialRef = osr.SpatialReference() spatialRef.ImportFromWkt(projInfo) # Proj4 format spatialRefProj = spatialRef.ExportToProj4() self.metadata[filename]['Projection'] = str(spatialRefProj) # original bbox upper_left = self.GDALInfoReportCorner(datafile,0.0,0.0 ); lower_left = self.GDALInfoReportCorner(datafile,0.0,datafile.RasterYSize); upper_right = self.GDALInfoReportCorner(datafile,datafile.RasterXSize,0.0 ); lower_right = self.GDALInfoReportCorner(datafile,datafile.RasterXSize,datafile.RasterYSize ); center = self.GDALInfoReportCorner(datafile,datafile.RasterXSize/2.0,datafile.RasterYSize/2.0 ); # get new bbox values if reprojection will happen try: if filename in self.reprojected: self.metadata[filename]['Projection'] = "EPSG:3857" target = osr.SpatialReference() target.ImportFromEPSG(3857) transform = osr.CoordinateTransformation(spatialRef,target) point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (upper_left[0],upper_left[1])) point.Transform(transform) upper_left = json.loads(point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (lower_left[0],lower_left[1])) point.Transform(transform) lower_left = json.loads(point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (upper_right[0],upper_right[1])) point.Transform(transform) upper_right = json.loads(point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (lower_right[0],lower_right[1])) point.Transform(transform) lower_right = json.loads(point.ExportToJson())['coordinates'] except (RuntimeError, TypeError, NameError) as error: print error except: print "Unexpected error:", sys.exc_info()[0] self.metadata[filename]['BBOX'] = (upper_left,lower_left,upper_right,lower_right) def GDALInfoReportCorner(self,hDataset,x,y): """GDALInfoReportCorner: extracted and adapted from the python port of gdalinfo""" # Transform the point into georeferenced coordinates adfGeoTransform = hDataset.GetGeoTransform(can_return_null = True) if adfGeoTransform is not None: dfGeoX = adfGeoTransform[0] + adfGeoTransform[1] * x + adfGeoTransform[2] * y dfGeoY = adfGeoTransform[3] + adfGeoTransform[4] * x + adfGeoTransform[5] * y else: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'BBOX might be wrong. Transformation coefficient could not be fetched from raster', level=QgsMessageBar.WARNING) return (x,y) # Report the georeferenced coordinates if abs(dfGeoX) < 181 and abs(dfGeoY) < 91: return literal_eval(("(%12.7f,%12.7f) " % (dfGeoX, dfGeoY ))) else: return literal_eval(("(%12.3f,%12.3f) " % (dfGeoX, dfGeoY ))) def loadImageryInfoForm(self, filename): pass def loadImageryInfo(self, filename): self.metadata[filename] = {} self.metadata[filename]['Title'] = self.title_edit.text() self.metadata[filename]['Platform'] = self.platform_combo_box.currentIndex() self.metadata[filename]['Sensor'] = self.sensor_edit.text() start = QDateTime() start.setDate(self.sense_start_edit.date()) start.setTime(self.sense_start_edit.time()) self.metadata[filename]['Sensor start'] = start.toString(Qt.ISODate) end = QDateTime() end.setDate(self.sense_end_edit.date()) end.setTime(self.sense_end_edit.time()) self.metadata[filename]['Sensor end'] = end.toString(Qt.ISODate) self.metadata[filename]['Tags'] = self.tags_edit.text() self.metadata[filename]['Provider'] = self.provider_edit.text() self.metadata[filename]['Contact'] = self.contact_edit.text() self.metadata[filename]['Website'] = self.website_edit.text() if self.reproject_check_box.isChecked(): if filename not in self.reprojected: self.reprojected.append(filename) else: while filename in self.reprojected: self.reprojected.remove(filename) if self.license_check_box.isChecked(): self.metadata[filename]['License'] = "Licensed under CC-BY 4.0 and allow tracing in OSM" if filename not in self.licensed: self.licensed.append(filename) else: while filename in self.licensed: self.licensed.remove(filename) self.extractMetadata(filename) def loadMetadataReviewBox(self): json_filenames = [] for index in xrange(self.sources_list_widget.count()): filename = str(self.sources_list_widget.item(index).data(Qt.UserRole)) if filename not in self.reprojected: f = os.path.splitext(filename)[0]+'.json' else: f = os.path.splitext(filename)[0]+'_EPSG3857.json' json_filenames.append(f) temp = QTemporaryFile() temp.open() for f in json_filenames: if os.path.exists(f): with open(f) as infile: temp.write(infile.read()) temp.flush() temp.seek(0) stream = QTextStream(temp) self.review_metadata_box.setText(stream.readAll()) #functions for threading purpose def startConnection(self): if self.storage_combo_box.currentIndex() == 0: bucket_name = 'oam-qgis-plugin-test' else: bucket_name = str(self.specify_edit.text()) if not bucket_name: self.bar2.clearWidgets() self.bar2.pushMessage( 'WARNING', 'The bucket for upload must be provided', level=QgsMessageBar.WARNING) bucket_key = str(self.key_id_edit.text()) bucket_secret = str(self.secret_key_edit.text()) self.bucket = None for trial in xrange(3): if self.bucket: break try: connection = S3Connection(bucket_key,bucket_secret) self.bucket = connection.get_bucket(bucket_name) QgsMessageLog.logMessage( 'Connection established' % trial, 'OAM', level=QgsMessageLog.INFO) except: if trial == 2: QgsMessageLog.logMessage( 'Failed to connect after 3 attempts', 'OAM', level=QgsMessageLog.CRITICAL) return self.bucket def reproject(self,filename): # to avoid repetition of "EPSG3857" in filename: if not "EPSG3857" in filename: reproject_filename = os.path.splitext(filename)[0]+'_EPSG3857.tif' os.system("gdalwarp -of GTiff -t_srs epsg:3857 %s %s" % (filename,reproject_filename)) QgsMessageLog.logMessage( 'Reprojected to EPSG:3857', 'OAM', level=QgsMessageLog.INFO) return reproject_filename def convert(self,filename): tif_filename = os.path.splitext(filename)[0]+".tif" #Open existing dataset src_ds = gdal.Open(filename) driver = gdal.GetDriverByName("GTiff") dst_ds = driver.CreateCopy(tif_filename, src_ds, 0 ) #Properly close the datasets to flush to disk dst_ds = None src_ds = None def startUploader(self): # initialize options self.upload_options = [] if self.notify_oam_check.isChecked(): self.upload_options.append("notify_oam") if self.trigger_tiling_check.isChecked(): self.upload_options.append("trigger_tiling") if self.startConnection(): for index in xrange(self.sources_list_widget.count()): filename = str(self.sources_list_widget.item(index).data(Qt.UserRole)) self.bar2.clearWidgets() self.bar2.pushMessage( 'INFO', 'Pre-upload image processing...', level=QgsMessageBar.INFO) # Perfom reprojection if filename in self.reprojected: filename = self.reproject(filename) QgsMessageLog.logMessage( 'Created reprojected file: %s' % filename, 'OAM', level=QgsMessageLog.INFO) # Convert file format if not (imghdr.what(filename) == 'tiff'): filename = self.convert(filename) QgsMessageLog.logMessage( 'Converted file to tiff: %s' % filename, 'OAM', level=QgsMessageLog.INFO) # create a new uploader instance uploader = Uploader(filename,self.bucket,self.upload_options) QgsMessageLog.logMessage( 'Uploader started\n', 'OAM', level=QgsMessageLog.INFO) # configure the QgsMessageBar messageBar = self.bar2.createMessage('INFO: Performing upload...', ) progressBar = QProgressBar() progressBar.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) messageBar.layout().addWidget(progressBar) cancelButton = QPushButton() cancelButton.setText('Cancel') cancelButton.clicked.connect(self.cancelUpload) messageBar.layout().addWidget(cancelButton) self.bar2.clearWidgets() self.bar2.pushWidget(messageBar, level=QgsMessageBar.INFO) self.messageBar = messageBar # start the worker in a new thread thread = QThread(self) uploader.moveToThread(thread) uploader.finished.connect(self.uploaderFinished) uploader.error.connect(self.uploaderError) uploader.progress.connect(progressBar.setValue) thread.started.connect(uploader.run) thread.start() self.thread = thread self.uploader = uploader else: QgsMessageLog.logMessage( 'No connection to the server\n', 'OAM', level=QgsMessageLog.CRITICAL) def cancelUpload(self): self.uploader.kill() self.bar2.clearWidgets() self.bar2.pushMessage( 'WARNING', 'Canceling upload...', level=QgsMessageBar.WARNING) def uploaderFinished(self, success): # clean up the uploader and thread try: self.uploader.deleteLater() except: QgsMessageLog.logMessage( 'Exception on deleting uploader\n', 'OAM', level=QgsMessageLog.CRITICAL) self.thread.quit() self.thread.wait() try: self.thread.deleteLater() except: QgsMessageLog.logMessage( 'Exception on deleting thread\n', 'OAM', level=QgsMessageLog.CRITICAL) # remove widget from message bar self.bar2.popWidget(self.messageBar) if success: # report the result self.bar2.clearWidgets() self.bar2.pushMessage( 'INFO', 'Upload completed with success', level=QgsMessageBar.INFO) QgsMessageLog.logMessage( 'Upload succeeded', 'OAM', level=QgsMessageLog.INFO) else: # notify the user that something went wrong self.bar2.pushMessage( 'CRITICAL', 'Upload was interrupted', level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage( 'Upload was interrupted', 'OAM', level=QgsMessageLog.CRITICAL) def uploaderError(self, e, exception_string): QgsMessageLog.logMessage( 'Uploader thread raised an exception:\n'.format(exception_string), 'OAM', level=QgsMessageLog.CRITICAL)
class ImgSearchDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, iface, settings, parent=None): """Constructor.""" super(ImgSearchDialog, 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.iface = iface self.setupUi(self) self.settings = settings self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint) # initialize GUI self.initGui() # self.buttonBox.button(QDialogButtonBox.Ok).setText('Save') self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayoutListWidget.layout().addWidget(self.bar) # event handling self.pushButtonSearch.clicked.connect(self.startSearch) # self.returnPressed.connect(self.startSearch) # self.pushButtonSearchLatest.clicked.connect(self.searchLatest) # self.pushButtonBrowseLocation.clicked.connect(self.browseLocation) self.connect(self.listWidget, QtCore.SIGNAL( "itemClicked(QListWidgetItem *)"), self.browseThumbnailAndMeta) # self.buttonBox.clicked.connect(lambda: self.test(self.buttonBox)) self.connect(self.buttonBox, QtCore.SIGNAL('accepted()'), self.execOk) self.connect(self.buttonBox, QtCore.SIGNAL('rejected()'), self.execCancel) # disable some GUIs # self.pushButtonBrowseLocation.hide() # self.pushButtonSearchLatest.hide() # add objects for catalog access self.settings.beginGroup("Storage") if self.settings.value('CATALOG_URL') is None or \ str(self.settings.value('CATALOG_URL')) == '': # self.catalogUrl = "https://oam-catalog.herokuapp.com" self.catalogUrl = "http://api.openaerialmap.org" else: self.catalogUrl = str(self.settings.value('CATALOG_URL')) self.settings.endGroup() self.oamCatalogAccess = OAMCatalogAccess(self.catalogUrl) catalogUrlLabel = self.catalog_url_label.text() + self.catalogUrl self.catalog_url_label.setText(catalogUrlLabel) self.imgBrowser = None def closeEvent(self, evnt): pass def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Return: self.startSearch() def test(self, *argv): print(str(argv)) def createQueriesSettings(self): self.settings.setValue('CHECKBOX_LOCATION', True) self.settings.setValue('CHECKBOX_ACQUISITION_FROM', True) self.settings.setValue('CHECKBOX_ACQUISITION_TO', True) self.settings.setValue('CHECKBOX_GSD_FROM', True) self.settings.setValue('CHECKBOX_GSD_TO', True) self.settings.setValue('LOCATION', '') self.settings.setValue('ACQUISITION_FROM', QDate.currentDate().addMonths(-12).toString(Qt.ISODate)) self.settings.setValue('ACQUISITION_TO', QDate.currentDate().toString(Qt.ISODate)) self.settings.setValue('GSD_FROM', '') self.settings.setValue('GSD_TO', '') self.settings.setValue('LIMIT', 20) self.settings.setValue('ORDER_BY', 0) self.settings.setValue('SORT', 'desc') def loadQueriesSettings(self): self.settings.beginGroup("ImageSearch") if str(self.settings.value('CHECKBOX_LOCATION')).lower() == 'true': self.checkBoxLocation.setCheckState(2) else: self.checkBoxLocation.setCheckState(0) if str(self.settings.value('CHECKBOX_ACQUISITION_FROM')).lower() == 'true': self.checkBoxAcquisitionFrom.setCheckState(2) else: self.checkBoxAcquisitionFrom.setCheckState(0) if str(self.settings.value('CHECKBOX_ACQUISITION_TO')).lower() == 'true': self.checkBoxAcquisitionTo.setCheckState(2) else: self.checkBoxAcquisitionTo.setCheckState(0) if str(self.settings.value('CHECKBOX_GSD_FROM')).lower() == 'true': self.checkBoxResolutionFrom.setCheckState(2) else: self.checkBoxResolutionFrom.setCheckState(0) if str(self.settings.value('CHECKBOX_GSD_TO')).lower() == 'true': self.checkBoxResolutionTo.setCheckState(2) else: self.checkBoxResolutionTo.setCheckState(0) self.lineEditLocation.setText( self.settings.value('LOCATION')) self.dateEditAcquisitionFrom.setDate(QDate.fromString( self.settings.value('ACQUISITION_FROM'), Qt.ISODate)) self.dateEditAcquisitionTo.setDate(QDate.fromString( self.settings.value('ACQUISITION_TO'), Qt.ISODate)) self.lineEditResolutionFrom.setText( str(self.settings.value('GSD_FROM'))) self.lineEditResolutionTo.setText( str(self.settings.value('GSD_TO'))) self.lineEditNumImages.setText( str(self.settings.value('LIMIT'))) self.comboBoxOrderBy.setCurrentIndex( int(self.settings.value('ORDER_BY'))) if self.settings.value('SORT') == 'desc': self.radioButtonDesc.setChecked(True) else: self.radioButtonAsc.setChecked(True) self.settings.endGroup() def saveQueriesSettings(self): self.settings.beginGroup("ImageSearch") self.settings.setValue('CHECKBOX_LOCATION', self.checkBoxLocation.isChecked()) self.settings.setValue('CHECKBOX_ACQUISITION_FROM', self.checkBoxAcquisitionFrom.isChecked()) self.settings.setValue('CHECKBOX_ACQUISITION_TO', self.checkBoxAcquisitionTo.isChecked()) self.settings.setValue('CHECKBOX_GSD_FROM', self.checkBoxResolutionFrom.isChecked()) self.settings.setValue('CHECKBOX_GSD_TO', self.checkBoxResolutionTo.isChecked()) self.settings.setValue('LOCATION', self.lineEditLocation.text()) self.settings.setValue('ACQUISITION_FROM', self.dateEditAcquisitionFrom.date().toString(Qt.ISODate)) self.settings.setValue('ACQUISITION_TO', self.dateEditAcquisitionTo.date().toString(Qt.ISODate)) if (self.lineEditResolutionFrom.text() != '' and self.lineEditResolutionFrom.text() is not None): self.settings.setValue('GSD_FROM', float(self.lineEditResolutionFrom.text())) if (self.lineEditResolutionTo.text() != '' and self.lineEditResolutionTo.text() is not None): self.settings.setValue('GSD_TO', float(self.lineEditResolutionTo.text())) if (self.lineEditNumImages.text() != '' and self.lineEditNumImages.text() is not None): self.settings.setValue('LIMIT', int(self.lineEditNumImages.text())) self.settings.setValue('ORDER_BY', self.comboBoxOrderBy.currentIndex()) if self.radioButtonDesc.isChecked(): self.settings.setValue('SORT', 'desc') else: self.settings.setValue('SORT', 'asc') self.settings.endGroup() def initGui(self): item = QListWidgetItem() item.setText("Please set the conditions and press 'Search' button.") item.setData(Qt.UserRole, "Sample Data") self.listWidget.addItem(item) # load default queries self.settings.beginGroup("ImageSearch") if self.settings.value('CHECKBOX_LOCATION') is None: print('create new queries settings') self.createQueriesSettings() self.settings.endGroup() self.loadQueriesSettings() def refreshListWidget(self, metadataInList): self.listWidget.clear() for singleMetaInDict in metadataInList: item = QListWidgetItem() item.setText(singleMetaInDict[u'title']) item.setData(Qt.UserRole, singleMetaInDict) self.listWidget.addItem(item) def startSearch(self): action = "meta" dictQueries = {} try: if self.checkBoxLocation.isChecked(): location = self.lineEditLocation.text() strBboxForOAM = nominatim_search(location) print(strBboxForOAM) if strBboxForOAM != 'failed': dictQueries['bbox'] = strBboxForOAM else: qMsgBox = QMessageBox() qMsgBox.setWindowTitle('Message') qMsgBox.setText("The 'location' won't be used as a " + "query, because Geocoder could " + "not find the location.") qMsgBox.exec_() if self.checkBoxAcquisitionFrom.isChecked(): dictQueries['acquisition_from'] = \ self.dateEditAcquisitionFrom.date().toString(Qt.ISODate) if self.checkBoxAcquisitionTo.isChecked(): dictQueries['acquisition_to'] = \ self.dateEditAcquisitionTo.date().toString(Qt.ISODate) if self.checkBoxResolutionFrom.isChecked(): if (self.lineEditResolutionFrom.text() != '' and self.lineEditResolutionFrom.text() is not None): dictQueries['gsd_from'] = \ float(self.lineEditResolutionFrom.text()) if self.checkBoxResolutionTo.isChecked(): if (self.lineEditResolutionTo.text() != '' and self.lineEditResolutionTo.text() is not None): dictQueries['gsd_to'] = \ float(self.lineEditResolutionTo.text()) if (self.lineEditNumImages.text() != '' and self.lineEditNumImages.text() is not None): dictQueries['limit'] = int(self.lineEditNumImages.text()) if self.comboBoxOrderBy.currentText() == 'Acquisition Date': dictQueries['order_by'] = "acquisition_end" elif self.comboBoxOrderBy.currentText() == 'GSD': dictQueries['order_by'] = "gsd" print(self.comboBoxOrderBy.currentText()) if self.radioButtonAsc.isChecked(): dictQueries['sort'] = "asc" elif self.radioButtonDesc.isChecked(): dictQueries['sort'] = "desc" self.oamCatalogAccess.setAction(action) self.oamCatalogAccess.setDictQueries(dictQueries) metadataInList = self.oamCatalogAccess.getMetadataInList() self.refreshListWidget(metadataInList) except Exception as e: print(repr(e)) qMsgBox = QMessageBox() qMsgBox.setWindowTitle('Message') qMsgBox.setText("Please make sure if you entered valid data" + "/internet connection, and try again.") qMsgBox.exec_() """ This function is not in use. """ """ def searchLatest(self): action = "meta" dictQueries = {} try: dictQueries['sort'] = "desc" dictQueries['order_by'] = "acquisition_end" if (self.lineEditResolutionFrom.text() != '' and self.lineEditResolutionFrom.text() is not None): dictQueries['gsd_from'] = float( self.lineEditResolutionFrom.text()) if (self.lineEditResolutionTo.text() != '' and self.lineEditResolutionTo.text() is not None): dictQueries['gsd_to'] = float( self.lineEditResolutionTo.text()) if (self.lineEditNumImages.text() != '' and self.lineEditNumImages.text() is not None): dictQueries['limit'] = int(self.lineEditNumImages.text()) self.oamCatalogAccess.setAction(action) self.oamCatalogAccess.setDictQueries(dictQueries) metadataInList = self.oamCatalogAccess.getMetadataInList() self.refreshListWidget(metadataInList) except Exception as e: qMsgBox = QMessageBox() qMsgBox.setWindowTitle('Message') qMsgBox.setText("Please make sure if you entered " + "valid data/internet connection, and try again.") qMsgBox.exec_() """ """ This function is not in use. """ """ def browseLocation(self): print("Browse location of loaded layer...") """ def browseThumbnailAndMeta(self, item): singleMetaInDict = item.data(Qt.UserRole) # print(str(singleMetaInDict)) if type(singleMetaInDict) is dict: if self.imgBrowser is None: self.imgBrowser = ImgBrowser(self.iface) self.imgBrowser.thumbnailManager.statusChanged.connect( self.changeThumbnailStatus) self.imgBrowser.thumbnailManager.error.connect( self.displayThumnailDownloadError) pos = self.pos() print(pos.x()) print(pos.y()) pos.setX(pos.x() + 400) pos.setY(pos.y() + 20) self.imgBrowser.move(pos) self.imgBrowser.setSingleMetaInDic(singleMetaInDict) self.imgBrowser.displayMetadata() self.imgBrowser.displayThumbnail() if not self.imgBrowser.isVisible(): self.imgBrowser.show() self.imgBrowser.activateWindow() def changeThumbnailStatus(self, status): # print(str(status)) if status == 0: pass """ self.bar.clearWidgets() self.bar.pushMessage( 'INFO', 'Downloading thumbnail...', level=QgsMessageBar.INFO, duration=8) """ if status == 1: self.bar.clearWidgets() def displayThumnailDownloadError(self, e): self.bar.clearWidgets() self.bar.pushMessage( 'Failed to download thumbnail.\n{}'.format(str(e)), level=QgsMessageBar.WARNING, duration=8) def execOk(self): # save self.defaultQueriesInDict into self.settings self.saveQueriesSettings() self.close() def execCancel(self): self.close()
class AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(alg.getParametersPanel(self)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.cornerWidget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 5) self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }") self.runAsBatchButton = QPushButton(self.tr("Run as batch process...")) self.runAsBatchButton.clicked.connect(self.runAsBatch) layout.addWidget(self.runAsBatchButton) self.cornerWidget.setLayout(layout) self.tabWidget.setCornerWidget(self.cornerWidget) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def setParamValues(self): params = self.alg.parameters outputs = self.alg.outputs for param in params: if param.hidden: continue wrapper = self.mainWidget.wrappers[param.name] if not self.setParamValue(param, wrapper): raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget) for output in outputs: if output.hidden: continue output.value = self.mainWidget.outputWidgets[output.name].getValue() if isinstance(output, (OutputRaster, OutputVector, OutputTable)): output.open = self.mainWidget.checkBoxes[output.name].isChecked() return True def setParamValue(self, param, wrapper): if wrapper.widget: return param.setValue(wrapper.value()) else: return True def checkExtentCRS(self): unmatchingCRS = False hasExtent = False projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = dataobjects.getAllLayers() for param in self.alg.parameters: if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = dataobjects.getObjectFromUri(inputlayer) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): value = self.mainWidget.wrappers[param.name].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): self.settings.setValue("/Processing/dialogBase", self.saveGeometry()) checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS) try: self.setParamValues() if checkCRS and not self.alg.checkInputCRS(): reply = QMessageBox.question(self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) if checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question(self, self.tr("Extent CRS"), self.tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return msg = self.alg._checkParameterValuesBeforeExecuting() if msg: QMessageBox.warning( self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.setInfo( self.tr('<b>Algorithm %s starting...</b>') % self.alg.name) if self.iterateParam: if runalgIterating(self.alg, self.iterateParam, self.feedback): self.finish() else: QApplication.restoreOverrideCursor() self.resetGUI() else: command = self.alg.getAsCommand() if command: ProcessingLog.addToLog( ProcessingLog.LOG_ALGORITHM, command) if runalg(self.alg, self.feedback): self.finish() else: QApplication.restoreOverrideCursor() self.resetGUI() except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect(lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage("", "Wrong or missing parameter value: %s" % e.parameter.description, level=QgsMessageBar.WARNING, duration=5) def finish(self): keepOpen = ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: if not handleAlgorithmResults(self.alg, self.feedback, not keepOpen): self.resetGUI() return self.executed = True self.setInfo('Algorithm %s finished' % self.alg.name) QApplication.restoreOverrideCursor() if not keepOpen: self.close() else: self.resetGUI() if self.alg.getHTMLOutputsCount() > 0: self.setInfo( self.tr('HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.')) def closeEvent(self, evt): QgsProject.instance().layerWasAdded.disconnect(self.mainWidget.layerRegistryChanged) QgsProject.instance().layersWillBeRemoved.disconnect(self.mainWidget.layerRegistryChanged) super(AlgorithmDialog, self).closeEvent(evt)
class OfficialDataSettingsDialog(QDialog, DIALOG_UI): official_db_connection_changed = pyqtSignal(DBConnector, bool) # dbconn, ladm_col_db def __init__(self, qgis_utils=None, conn_manager=None, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.log = QgsApplication.messageLog() self.conn_manager = conn_manager self._db = None self.qgis_utils = qgis_utils self.db_source = OFFICIAL_DB_SOURCE self.conf_db = ConfigDbSupported() # Set connections self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.helpRequested.connect(self.show_help) self.finished.connect(self.finished_slot) self.btn_test_connection.clicked.connect(self.test_connection) self.btn_test_ladm_col_structure.clicked.connect( self.test_ladm_col_structure) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.cbo_db_source.clear() self._lst_db = self.conf_db.get_db_items() self._lst_panel = dict() for key, value in self._lst_db.items(): self.cbo_db_source.addItem(value.get_name(), key) self._lst_panel[key] = value.get_config_panel(self) self._lst_panel[key].notify_message_requested.connect( self.show_message) self.db_layout.addWidget(self._lst_panel[key]) self.db_source_changed() # Trigger some default behaviours self.restore_settings() self.cbo_db_source.currentIndexChanged.connect(self.db_source_changed) self.rejected.connect(self.close_dialog) def close_dialog(self): self.close() def showEvent(self, event): # It is necessary to reload the variables # to load the database and schema name self.restore_settings() def _get_db_connector_from_gui(self): current_db = self.cbo_db_source.currentData() params = self._lst_panel[current_db].read_connection_parameters() db = self._lst_db[current_db].get_db_connector(params) db.open_connection() # Open connection using gui parameters return db def get_db_connection(self): if self._db is not None: self.log.logMessage("Returning existing db connection...", PLUGIN_NAME, Qgis.Info) else: self.log.logMessage("Getting new db connection...", PLUGIN_NAME, Qgis.Info) self._db = self._get_db_connector_from_gui() self._db.open_connection() return self._db def accepted(self): current_db = self.cbo_db_source.currentData() if self._lst_panel[current_db].state_changed(): valid_connection = True ladm_col_schema = False db = self._get_db_connector_from_gui() res, msg = db.test_connection(EnumTestLevel.DB_SCHEMA) if res: ladm_col_schema, msg = db.test_connection(EnumTestLevel.LADM) else: self.show_message(msg, Qgis.Warning) valid_connection = False if not ladm_col_schema: self.show_message(msg, Qgis.Warning) return # Do not close the dialog if valid_connection: if self._db is not None: self._db.close_connection() self._db = db # Update db connect with new db conn self.conn_manager.set_db_connector_for_source( self._db, self.db_source) # Emmit signal when change db source self.official_db_connection_changed.emit( self._db, ladm_col_schema) self.save_settings() QDialog.accept(self) # TODO remove? else: return # Do not close the dialog else: QDialog.accept(self) # TODO remove? def finished_slot(self, result): self.bar.clearWidgets() def set_db_connection(self, mode, dict_conn): """ To be used by external scripts and unit tests """ self.cbo_db_source.setCurrentIndex(self.cbo_db_source.findData(mode)) self.db_source_changed() current_db = self.cbo_db_source.currentData() self._lst_panel[current_db].write_connection_parameters(dict_conn) self.accepted() # Create/update the db object def save_settings(self): settings = QSettings() current_db = self.cbo_db_source.currentData() settings.setValue( 'Asistente-LADM_COL/db/{db_source}/db_connection_source'.format( db_source=self.db_source), current_db) dict_conn = self._lst_panel[current_db].read_connection_parameters() self._lst_db[current_db].save_parameters_conn(dict_conn=dict_conn, db_source=self.db_source) def restore_settings(self): # Restore QSettings settings = QSettings() default_db = self.conf_db.id_default_db index_db = self.cbo_db_source.findData( settings.value( 'Asistente-LADM_COL/db/{db_source}/db_connection_source'. format(db_source=self.db_source), default_db)) if index_db == -1: index_db = self.cbo_db_source.findData(default_db) self.cbo_db_source.setCurrentIndex(index_db) self.db_source_changed() # restore db settings for all panels for id_db, db_factory in self._lst_db.items(): dict_conn = db_factory.get_parameters_conn(self.db_source) self._lst_panel[id_db].write_connection_parameters(dict_conn) self._lst_panel[id_db].save_state() def db_source_changed(self): if self._db is not None: self._db.close_connection() self._db = None # Reset db connection for key, value in self._lst_panel.items(): value.setVisible(False) current_db = self.cbo_db_source.currentData() self._lst_panel[current_db].setVisible(True) def test_connection(self): db = self._get_db_connector_from_gui() res, msg = db.test_connection(test_level=EnumTestLevel.DB_SCHEMA) if db is not None: db.close_connection() self.show_message(msg, Qgis.Info if res else Qgis.Warning) self.log.logMessage("Test connection!", PLUGIN_NAME, Qgis.Info) def test_ladm_col_structure(self): db = self._get_db_connector_from_gui() res, msg = db.test_connection(test_level=EnumTestLevel.LADM) if db is not None: db.close_connection() self.show_message(msg, Qgis.Info if res else Qgis.Warning) self.log.logMessage("Test connection!", PLUGIN_NAME, Qgis.Info) def show_message(self, message, level): self.bar.pushMessage(message, level, 10) def show_help(self): self.qgis_utils.show_help("settings")
class CreateGroupPartyOperation(QDialog, DIALOG_UI): WIZARD_NAME = "CreateGroupPartyOperationWizard" WIZARD_TOOL_NAME = QCoreApplication.translate(WIZARD_NAME, "Create group party") def __init__(self, iface, db, qgis_utils, parent=None): QDialog.__init__(self) self.setupUi(self) self.iface = iface self._db = db self.qgis_utils = qgis_utils self.logger = Logger() self.names = self._db.names self.help_strings = HelpStrings() self.data = {} # {t_id: [display_text, denominator, numerator]} self.current_selected_parties = [] # [t_ids] self.parties_to_group = {} # {t_id: [denominator, numerator]} self._layers = { self.names.OP_GROUP_PARTY_T: { 'name': self.names.OP_GROUP_PARTY_T, 'geometry': None, LAYER: None }, self.names.OP_PARTY_T: { 'name': self.names.OP_PARTY_T, 'geometry': None, LAYER: None }, self.names.MEMBERS_T: { 'name': self.names.MEMBERS_T, 'geometry': None, LAYER: None }, self.names.FRACTION_S: { 'name': self.names.FRACTION_S, 'geometry': None, LAYER: None }, self.names.COL_GROUP_PARTY_TYPE_D: { 'name': self.names.COL_GROUP_PARTY_TYPE_D, 'geometry': None, LAYER: None } } # Fill combo of types col_group_party_type_table = self.qgis_utils.get_layer( self._db, self.names.COL_GROUP_PARTY_TYPE_D, None, True) if not col_group_party_type_table: return for feature in col_group_party_type_table.getFeatures(): self.cbo_group_type.addItem(feature[self.names.DISPLAY_NAME_F], feature[self.names.T_ID_F]) self.txt_search_party.setText("") self.btn_select.setEnabled(False) self.btn_deselect.setEnabled(False) self.tbl_selected_parties.setColumnCount(3) self.tbl_selected_parties.setColumnWidth(0, 140) self.tbl_selected_parties.setColumnWidth(1, 90) self.tbl_selected_parties.setColumnWidth(2, 90) self.tbl_selected_parties.sortItems(0, Qt.AscendingOrder) self.txt_search_party.textEdited.connect(self.search) self.lst_all_parties.itemSelectionChanged.connect( self.selection_changed_all) self.tbl_selected_parties.itemSelectionChanged.connect( self.selection_changed_selected) self.tbl_selected_parties.cellChanged.connect(self.valueEdited) self.btn_select_all.clicked.connect(self.select_all) self.btn_deselect_all.clicked.connect(self.deselect_all) self.btn_select.clicked.connect(self.select) self.btn_deselect.clicked.connect(self.deselect) self.buttonBox.helpRequested.connect(self.show_help) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.rejected.connect(self.close_wizard) def closeEvent(self, e): # It's necessary to prevent message bar alert pass def required_layers_are_available(self): layers_are_available = self.qgis_utils.required_layers_are_available( self._db, self._layers, self.WIZARD_TOOL_NAME) return layers_are_available def load_parties_data(self): expression = QgsExpression( LayerConfig.get_dict_display_expressions( self.names)[self.names.OP_PARTY_T]) context = QgsExpressionContext() data = dict() for feature in self._layers[ self.names.OP_PARTY_T][LAYER].getFeatures(): context.setFeature(feature) expression.prepare(context) value = expression.evaluate(context) data[feature[self.names.T_ID_F]] = [ value if value != NULL else None, 0, 0 ] self.set_parties_data(data) def set_parties_data(self, parties_data): """ Initialize parties data. :param parties_data: Dictionary {t_id: [display_text, denominator, numerator]} :type parties_data: dict """ self.data = parties_data self.update_lists() def search(self, text): self.update_lists(True) def selection_changed_all(self): self.btn_select.setEnabled(len(self.lst_all_parties.selectedItems())) def selection_changed_selected(self): self.btn_deselect.setEnabled( len(self.tbl_selected_parties.selectedItems())) def select_all(self): """ SLOT. Select all parties listed from left list widget. """ items_ids = [] for index in range(self.lst_all_parties.count()): items_ids.append( self.lst_all_parties.item(index).data(Qt.UserRole)) self.add_parties_to_selected(items_ids) def deselect_all(self): """ SLOT. Remove all parties from left list widget. """ items_ids = [] for index in range(self.tbl_selected_parties.rowCount()): items_ids.append( self.tbl_selected_parties.item(index, 0).data(Qt.UserRole)) self.remove_parties_from_selected(items_ids) def select(self): """ SLOT. Select all parties highlighted in left list widget. """ self.add_parties_to_selected([ item.data(Qt.UserRole) for item in self.lst_all_parties.selectedItems() ]) def deselect(self): """ SLOT. Remove all parties highlighted in right list widget. """ self.remove_parties_from_selected([ item.data(Qt.UserRole) for item in self.tbl_selected_parties.selectedItems() if item.column() == 0 ]) def add_parties_to_selected(self, parties_ids): self.current_selected_parties.extend(parties_ids) self.update_lists() def remove_parties_from_selected(self, parties_ids): for party_id in parties_ids: self.current_selected_parties.remove(party_id) if party_id in self.parties_to_group: del self.parties_to_group[party_id] self.update_lists() def update_lists(self, only_update_all_list=False): """ Update left list widget and optionally the right one. :param only_update_all_list: Only update left list widget. :type only_update_all_list: bool """ # All parties self.lst_all_parties.clear() if self.txt_search_party.text(): tmp_parties = { i: d for i, d in self.data.items() if self.txt_search_party.text().lower() in d[0].lower() } else: tmp_parties = copy.deepcopy(self.data) # Copy all! for party_id in self.current_selected_parties: if party_id in tmp_parties: del tmp_parties[party_id] for i, d in tmp_parties.items(): item = QListWidgetItem(d[0]) item.setData(Qt.UserRole, i) self.lst_all_parties.addItem(item) if not only_update_all_list: # Selected parties self.tbl_selected_parties.clearContents() self.tbl_selected_parties.setRowCount( len(self.current_selected_parties)) self.tbl_selected_parties.setColumnCount(3) self.tbl_selected_parties.setSortingEnabled(False) for row, party_id in enumerate(self.current_selected_parties): item = QTableWidgetItem(self.data[party_id][0]) item.setFlags(item.flags() & ~Qt.ItemIsEditable) item.setData(Qt.UserRole, party_id) self.tbl_selected_parties.setItem(row, 0, item) value_denominator = self.parties_to_group[party_id][ 0] if party_id in self.parties_to_group else self.data[ party_id][1] self.tbl_selected_parties.setItem( row, 1, QTableWidgetItem(str(value_denominator))) value_numerator = self.parties_to_group[party_id][ 1] if party_id in self.parties_to_group else self.data[ party_id][2] self.tbl_selected_parties.setItem( row, 2, QTableWidgetItem(str(value_numerator))) self.tbl_selected_parties.setSortingEnabled(True) def valueEdited(self, row, column): """ SLOT. Update either the denominator or the numerator for given row. :param row: Edited row :type row: int :param column: Edited column :type column: int """ if column != 0: party_id = self.tbl_selected_parties.item(row, 0).data(Qt.UserRole) value_denominator = self.tbl_selected_parties.item(row, 1).text() # While creating a row and the second column is created, the third # one doesn't exist, so use the value already stored for that case value_numerator = self.parties_to_group[party_id][ 1] if party_id in self.parties_to_group else 0 if self.tbl_selected_parties.item(row, 2) is not None: value_numerator = self.tbl_selected_parties.item(row, 2).text() self.parties_to_group[party_id] = [ value_denominator, value_numerator ] def accept(self): """ Overwrite the dialog's `accept <https://doc.qt.io/qt-5/qdialog.html#accept>`_ SLOT to store selected parties and numerator-denominator before closing the dialog. """ self.parties_to_group = {} for index in range(self.tbl_selected_parties.rowCount()): k = self.tbl_selected_parties.item(index, 0).data(Qt.UserRole) try: v_n = int(self.tbl_selected_parties.item(index, 1).text()) except ValueError as e: self.show_message( QCoreApplication.translate( "WizardTranslations", "There are some invalid values in the numerator column. Fix them before continuing..." ), Qgis.Warning) return try: v_d = int(self.tbl_selected_parties.item(index, 2).text()) except ValueError as e: self.show_message( QCoreApplication.translate( "WizardTranslations", "There are some invalid values in the denominator column. Fix them before continuing..." ), Qgis.Warning) return self.parties_to_group[k] = [v_n, v_d] name = self.txt_group_name.text() group_party_type = self.cbo_group_type.itemData( self.cbo_group_type.currentIndex()) dict_params = { self.names.COL_PARTY_T_NAME_F: name, self.names.COL_GROUP_PARTY_T_TYPE_F: group_party_type, 'porcentajes': self.parties_to_group } res, msg = self.validate_group_party(dict_params) if not res: self.show_message(msg, Qgis.Warning) return self.save_group_party(self._db, [dict_params]) def validate_group_party(self, params): name = params[self.names.COL_PARTY_T_NAME_F] group_party_type = params[self.names.COL_GROUP_PARTY_T_TYPE_F] porcentajes = params['porcentajes'] if not porcentajes: return (False, QCoreApplication.translate( "CreateGroupParty", "You need to select some parties to create a group.")) elif len(porcentajes) == 1: return ( False, QCoreApplication.translate( "CreateGroupParty", "There is just one party, you need to add at least two parties to a group." )) there_percents = False fraction = Fraction() for t, nd in porcentajes.items(): if porcentajes[t] != [0, 0]: there_percents = True break if there_percents: for t, nd in porcentajes.items(): if porcentajes[t][1] == 0: return ( False, QCoreApplication.translate( "CreateGroupParty", "There are denominators equal to zero. You need to change those values." )) elif porcentajes[t][1] < porcentajes[t][0]: return ( False, QCoreApplication.translate( "CreateGroupParty", "The denominator cannot be less than the numerator." )) else: fraction = Fraction(porcentajes[t][0], porcentajes[t][1]) + fraction if fraction != 1.0: return (False, QCoreApplication.translate( "CreateGroupParty", "The sum of the fractions must be equal to one.")) return (True, QCoreApplication.translate("CreateGroupParty", "Validation passed!")) def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 10) def save_group_party(self, db, params): """ Save group party data into associated tables: self.names.OP_GROUP_PARTY_T, self.names.MEMBERS_T and self.names.FRACTION_S. params: List of dicts, where each dict is an independent group party: { self.names.COL_PARTY_T_NAME_F: '', self.names.COL_GROUP_PARTY_T_TYPE_F: '', 'porcentajes': { 't_id_miembro': [20, 100], # numerador/denominador 't_id_miembro2': [40, 100] } } """ # Disconnect from previous runs self.disconnect_signals() for group in params: # Create connections to react when a group party is stored to the DB self._layers[self.names.OP_GROUP_PARTY_T][ LAYER].committedFeaturesAdded.connect( partial(self.finish_group_party_saving, group['porcentajes'])) # First save the group party new_feature = QgsVectorLayerUtils().createFeature( self._layers[self.names.OP_GROUP_PARTY_T][LAYER]) new_feature.setAttribute( self.names.COL_GROUP_PARTY_T_TYPE_F, group[self.names.COL_GROUP_PARTY_T_TYPE_F]) new_feature.setAttribute(self.names.COL_PARTY_T_NAME_F, group[self.names.COL_PARTY_T_NAME_F]) # TODO: Remove when local id and working space are defined new_feature.setAttribute(self.names.OID_T_LOCAL_ID_F, 1) new_feature.setAttribute(self.names.OID_T_NAMESPACE_F, self.names.OP_GROUP_PARTY_T) # TODO: Gui should allow users to ented namespace, local_id and date values #new_feature.setAttribute("p_espacio_de_nombres", self.names.OP_GROUP_PARTY_T) #new_feature.setAttribute("p_local_id", '0') #new_feature.setAttribute("comienzo_vida_util_version", 'now()') self.logger.info(__name__, "Saving Group Party: {}".format(group)) with edit(self._layers[self.names.OP_GROUP_PARTY_T][LAYER]): self._layers[self.names.OP_GROUP_PARTY_T][LAYER].addFeature( new_feature) def finish_group_party_saving(self, members, layer_id, features): try: self._layers[self.names.OP_GROUP_PARTY_T][ LAYER].committedFeaturesAdded.disconnect() except TypeError as e: pass message = QCoreApplication.translate( "WizardTranslations", "'{}' tool has been closed because an error occurred while trying to save the data." ).format(self.WIZARD_TOOL_NAME) if len(features) != 1: message = QCoreApplication.translate( "WizardTranslations", "'{}' tool has been closed. We should have got only one group party... We cannot do anything with {} group parties" ).format(self.WIZARD_TOOL_NAME, len(features)) self.logger.warning( __name__, "We should have got only one group party... We cannot do anything with {} group parties" .format(len(features))) else: fid = features[0].id() if not self._layers[self.names.OP_GROUP_PARTY_T][LAYER].getFeature( fid).isValid(): self.logger.warning( __name__, "Feature not found in table Group Party...") else: group_party_id = self._layers[self.names.OP_GROUP_PARTY_T][ LAYER].getFeature(fid)[self.names.T_ID_F] # Now save members party_ids = list() for party_id, fraction in members.items(): # Create connections to react when a group party is stored to the DB self._layers[self.names.MEMBERS_T][ LAYER].committedFeaturesAdded.connect( partial(self.finish_member_saving, fraction)) new_feature = QgsVectorLayerUtils().createFeature( self._layers[self.names.MEMBERS_T][LAYER]) new_feature.setAttribute( self.names.MEMBERS_T_GROUP_PARTY_F, group_party_id) new_feature.setAttribute(self.names.MEMBERS_T_PARTY_F, party_id) self.logger.info( __name__, "Saving group party's member ({}: {}).".format( group_party_id, party_id)) with edit(self._layers[self.names.MEMBERS_T][LAYER]): self._layers[self.names.MEMBERS_T][LAYER].addFeature( new_feature) party_ids.append(party_id) if len(party_ids): message = QCoreApplication.translate( "WizardTranslations", "The new group party (t_id={}) was successfully created and associated with its corresponding party(ies) (t_id={})!" ).format(group_party_id, ", ".join([str(b) for b in party_ids])) else: message = QCoreApplication.translate( "WizardTranslations", "The new group party (t_id={}) was successfully created but this one wasn't associated with a party(ies)" ).format(group_party_id) self.close_wizard(message) def finish_member_saving(self, fraction, layer_id, features): try: self._layers[self.names. MEMBERS_T][LAYER].committedFeaturesAdded.disconnect() except TypeError as e: pass if len(features) != 1: self.logger.warning( __name__, "We should have got only one member... We cannot do anything with {} members" .format(len(features))) else: fid = features[0].id() if not self._layers[self.names.MEMBERS_T][LAYER].getFeature( fid).isValid(): self.logger.warning(__name__, "Feature not found in table Members...") else: member_id = self._layers[self.names.MEMBERS_T][ LAYER].getFeature(fid)[self.names.T_ID_F] if fraction == [0, 0]: return # And finally save fractions new_feature = QgsVectorLayerUtils().createFeature( self._layers[self.names.FRACTION_S][LAYER]) new_feature.setAttribute(self.names.FRACTION_S_MEMBER_F, member_id) new_feature.setAttribute(self.names.FRACTION_S_NUMERATOR_F, fraction[0]) new_feature.setAttribute(self.names.FRACTION_S_DENOMINATOR_F, fraction[1]) with edit(self._layers[self.names.FRACTION_S][LAYER]): self.logger.info( __name__, "Saving member's fraction ({}: {}).".format( member_id, fraction)) self._layers[self.names.FRACTION_S][LAYER].addFeature( new_feature) def close_wizard(self, message=None, show_message=True): if message is None: message = QCoreApplication.translate( "WizardTranslations", "'{}' tool has been closed.").format(self.WIZARD_TOOL_NAME) if show_message: self.logger.info_msg(__name__, message) self.disconnect_signals() self.close() def disconnect_signals(self): try: self._layers[self.names.OP_GROUP_PARTY_T][ LAYER].committedFeaturesAdded.disconnect() except TypeError as e: pass try: self._layers[self.names. MEMBERS_T][LAYER].committedFeaturesAdded.disconnect() except TypeError as e: pass def show_help(self): self.qgis_utils.show_help("group_party")
class PublishWidget(BASE, WIDGET): def __init__(self, parent): super(PublishWidget, self).__init__() self.isMetadataPublished = {} self.isDataPublished = {} self.currentRow = None self.currentLayer = None self.parent = parent self.fieldsToPublish = {} self.metadata = {} execute(self._setupUi) def _setupUi(self): self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.txtNoLayers.setVisible(False) self.populateComboBoxes() self.populateLayers() self.listLayers.setContextMenuPolicy(Qt.CustomContextMenu) self.listLayers.customContextMenuRequested.connect( self.showContextMenu) self.listLayers.currentRowChanged.connect(self.currentRowChanged) self.comboGeodataServer.currentIndexChanged.connect( self.geodataServerChanged) self.comboMetadataServer.currentIndexChanged.connect( self.metadataServerChanged) self.btnPublish.clicked.connect(self.publish) self.btnPublishOnBackground.clicked.connect(self.publishOnBackground) self.btnOpenQgisMetadataEditor.clicked.connect(self.openMetadataEditor) self.labelSelect.linkActivated.connect(self.selectLabelClicked) self.btnRemoveAll.clicked.connect(self.unpublishAll) self.btnRemoveAll.setIcon(REMOVE_ICON) self.btnValidate.setIcon(VALIDATE_ICON) self.btnPreview.clicked.connect(self.previewMetadata) self.btnPreview.setIcon(PREVIEW_ICON) self.btnImport.setIcon(IMPORT_ICON) #self.btnImport.setVisible(False) self.btnImport.clicked.connect(self.importMetadata) self.btnValidate.clicked.connect(self.validateMetadata) self.btnUseConstraints.clicked.connect( lambda: self.openMetadataEditor(ACCESS)) self.btnAccessConstraints.clicked.connect( lambda: self.openMetadataEditor(ACCESS)) self.btnIsoTopic.clicked.connect( lambda: self.openMetadataEditor(CATEGORIES)) self.btnKeywords.clicked.connect( lambda: self.openMetadataEditor(KEYWORDS)) self.btnDataContact.clicked.connect( lambda: self.openMetadataEditor(CONTACT)) self.btnMetadataContact.clicked.connect( lambda: self.openMetadataEditor(CONTACT)) self.btnExportFolder.clicked.connect(self.selectExportFolder) if self.listLayers.count(): item = self.listLayers.item(0) self.listLayers.setCurrentItem(item) self.currentRowChanged(0) else: self.txtNoLayers.setVisible(True) self.listLayers.setVisible(False) self.labelSelect.setVisible(False) #self.spacerLayerSelect.setVisible(False) self.btnRemoveAll.setVisible(False) self.metadataServerChanged() self.selectLabelClicked("all") def selectExportFolder(self): folder = QFileDialog.getExistingDirectory(self, self.tr("Export to folder")) if folder: self.txtExportFolder.setText(folder) def geodataServerChanged(self): self.updateLayersPublicationStatus(True, False) def metadataServerChanged(self): self.updateLayersPublicationStatus(False, True) try: profile = metadataServers()[ self.comboMetadataServer.currentText()].profile except KeyError: profile = GeonetworkServer.PROFILE_DEFAULT if profile == GeonetworkServer.PROFILE_DEFAULT: if self.tabWidgetMetadata.count() == 3: self.tabWidgetMetadata.removeTab(1) self.tabWidgetMetadata.removeTab(1) else: if self.tabWidgetMetadata.count() == 1: title = "Dutch geography" if profile == GeonetworkServer.PROFILE_DUTCH else "INSPIRE" self.tabWidgetMetadata.addTab(self.tabInspire, title) self.tabWidgetMetadata.addTab(self.tabTemporal, self.tr("Temporal")) self.comboStatus.setVisible( profile == GeonetworkServer.PROFILE_DUTCH) def selectLabelClicked(self, url): state = Qt.Unchecked if url == "none" else Qt.Checked for i in range(self.listLayers.count()): item = self.listLayers.item(i) widget = self.listLayers.itemWidget(item).setCheckState(state) def currentRowChanged(self, currentRow): if self.currentRow == currentRow: return self.currentRow = currentRow self.storeFieldsToPublish() self.storeMetadata() layers = self.publishableLayers() layer = layers[currentRow] self.currentLayer = layer self.populateLayerMetadata() self.populateLayerFields() if layer.type() != layer.VectorLayer: self.tabLayerInfo.setCurrentWidget(self.tabMetadata) def populateLayerMetadata(self): metadata = self.metadata[self.currentLayer] self.txtMetadataTitle.setText(metadata.title()) self.txtAbstract.setPlainText(metadata.abstract()) isoTopics = ",".join(metadata.keywords().get("gmd:topicCategory", [])) self.txtIsoTopic.setText(isoTopics) keywords = [] for group in metadata.keywords().values(): keywords.extend(group) self.txtKeywords.setText(",".join(keywords)) if metadata.contacts(): self.txtDataContact.setText(metadata.contacts()[0].name) self.txtMetadataContact.setText(metadata.contacts()[0].name) self.txtUseConstraints.setText(metadata.fees()) licenses = metadata.licenses() if licenses: self.txtAccessConstraints.setText(licenses[0]) else: self.txtAccessConstraints.setText("") self.comboLanguage.setCurrentText(metadata.language()) #TODO: Use default values if no values in QGIS metadata object def populateLayerFields(self): if self.currentLayer.type() == self.currentLayer.VectorLayer: fields = [f.name() for f in self.currentLayer.fields()] self.tabLayerInfo.setTabEnabled(1, True) self.tableFields.setRowCount(len(fields)) for i, field in enumerate(fields): item = QTableWidgetItem() item.setFlags(item.flags() ^ Qt.ItemIsEditable) check = Qt.Checked if self.fieldsToPublish[ self.currentLayer][field] else Qt.Unchecked item.setCheckState(check) self.tableFields.setItem(i, 0, item) item = QTableWidgetItem(field) item.setFlags(item.flags() ^ Qt.ItemIsEditable) self.tableFields.setItem(i, 1, item) else: self.tabLayerInfo.setTabEnabled(1, False) def storeMetadata(self): if self.currentLayer is not None: metadata = self.metadata[self.currentLayer] metadata.setTitle(self.txtMetadataTitle.text()) metadata.setAbstract(self.txtAbstract.toPlainText()) metadata.setLanguage(self.comboLanguage.currentText()) self.currentLayer.setMetadata(metadata) def storeFieldsToPublish(self): if self.currentLayer is not None: if self.currentLayer.type() == self.currentLayer.VectorLayer: fieldsToPublish = {} fields = self.currentLayer.fields() for i in range(fields.count()): chkItem = self.tableFields.item(i, 0) nameItem = self.tableFields.item(i, 1) fieldsToPublish[ nameItem.text()] = chkItem.checkState() == Qt.Checked self.fieldsToPublish[self.currentLayer] = fieldsToPublish def showContextMenu(self, pos): item = self.listLayers.itemAt(pos) if item is None: return row = self.listLayers.row(item) name = self.listLayers.itemWidget(item).name() menu = QMenu() if self.isDataPublished.get(name): menu.addAction(self.tr("View WMS layer"), lambda: self.viewWms(name)) menu.addAction(self.tr("Unpublish data"), lambda: self.unpublishData(name)) if self.isMetadataPublished.get(name): menu.addAction(self.tr("View metadata"), lambda: self.viewMetadata(name)) menu.addAction(self.tr("Unpublish metadata"), lambda: self.unpublishMetadata(name)) if any(self.isDataPublished.values()): menu.addAction(self.tr("View all WMS layers"), self.viewAllWms) menu.exec_(self.listLayers.mapToGlobal(pos)) def publishableLayers(self): def _layersFromTree(layerTreeGroup): _layers = [] for child in layerTreeGroup.children(): if isinstance(child, QgsLayerTreeLayer): _layers.append(child.layer()) elif isinstance(child, QgsLayerTreeGroup): _layers.extend(_layersFromTree(child)) return _layers root = QgsProject.instance().layerTreeRoot() layers = [ layer for layer in _layersFromTree(root) if layer.type() in [QgsMapLayer.VectorLayer, QgsMapLayer.RasterLayer] and layer.dataProvider().name() != "wms" ] return layers def populateLayers(self): layers = self.publishableLayers() for i, layer in enumerate(layers): fields = [f.name() for f in layer.fields() ] if layer.type() == layer.VectorLayer else [] self.fieldsToPublish[layer] = {f: True for f in fields} self.metadata[layer] = layer.metadata().clone() self.addLayerListItem(layer) self.updateLayersPublicationStatus() def addLayerListItem(self, layer): widget = LayerItemWidget(layer) item = QListWidgetItem(self.listLayers) item.setSizeHint(widget.sizeHint()) self.listLayers.addItem(item) self.listLayers.setItemWidget(item, widget) return item def populateComboBoxes(self): self.populatecomboMetadataServer() self.populatecomboGeodataServer() self.comboLanguage.clear() for lang in QgsMetadataWidget.parseLanguages(): self.comboLanguage.addItem(lang) def populatecomboGeodataServer(self): self.comboGeodataServer.clear() self.comboGeodataServer.addItem(self.tr("Do not publish data")) self.comboGeodataServer.addItems(geodataServers().keys()) def populatecomboMetadataServer(self): self.comboMetadataServer.clear() self.comboMetadataServer.addItem(self.tr("Do not publish metadata")) self.comboMetadataServer.addItems(metadataServers().keys()) def updateServers(self): #TODO: do not call updateLayersPublicationStatus if not really needed self.comboGeodataServer.currentIndexChanged.disconnect( self.geodataServerChanged) self.comboMetadataServer.currentIndexChanged.disconnect( self.metadataServerChanged) self.populatecomboMetadataServer() current = self.comboGeodataServer.currentText() self.populatecomboGeodataServer() if current in geodataServers().keys(): self.comboGeodataServer.setCurrentText(current) current = self.comboMetadataServer.currentText() self.populatecomboMetadataServer() if current in metadataServers().keys(): self.comboMetadataServer.setCurrentText(current) self.updateLayersPublicationStatus() self.comboGeodataServer.currentIndexChanged.connect( self.geodataServerChanged) self.comboMetadataServer.currentIndexChanged.connect( self.metadataServerChanged) def isMetadataOnServer(self, layer): try: server = metadataServers()[self.comboMetadataServer.currentText()] uuid = uuidForLayer(self.layerFromName(layer)) return server.metadataExists(uuid) except KeyError: return False except: return False def isDataOnServer(self, layer): try: server = geodataServers()[self.comboGeodataServer.currentText()] return server.layerExists(layer) except KeyError: return False except Exception as e: return False def importMetadata(self): if self.currentLayer is None: return metadataFile = os.path.splitext(self.currentLayer.source())[0] + ".xml" if not os.path.exists(metadataFile): metadataFile = self.currentLayer.source() + ".xml" if not os.path.exists(metadataFile): metadataFile = None if metadataFile is not None: try: loadMetadataFromIsoXml(self.currentLayer, metadataFile) except: self.bar.pushMessage( self.tr("Error importing metadata"), self. tr("Cannot convert the metadata file. Maybe not ISO format?" ), level=Qgis.Warning, duration=5) return self.metadata[ self.currentLayer] = self.currentLayer.metadata().clone() self.populateLayerMetadata() self.bar.pushMessage("", self.tr("Metadata correctly imported"), level=Qgis.Success, duration=5) else: self.bar.pushMessage( self.tr("Error importing metadata"), self.tr("Cannot find ISO metadata file for the current layer"), level=Qgis.Warning, duration=5) def validateMetadata(self): if self.currentLayer is None: return self.storeMetadata() validator = QgsNativeMetadataValidator() result, errors = validator.validate(self.metadata[self.currentLayer]) if result: txt = self.tr("No validation errors") else: txt = (self.tr("The following issues were found:") + "<br>" + "<br>".join([ "<b>%s</b>:%s" % (err.section, err.note) for err in errors ])) dlg = QgsMessageOutput.createMessageOutput() dlg.setTitle(self.tr("Metadata validation")) dlg.setMessage(txt, QgsMessageOutput.MessageHtml) dlg.showMessage() def openMetadataEditor(self, tab): if self.currentLayer is None: return self.storeMetadata() metadata = self.metadata[self.currentLayer].clone() w = MetadataDialog(metadata, tab, self) w.exec_() if w.metadata is not None: self.metadata[self.currentLayer] = w.metadata self.populateLayerMetadata() def unpublishData(self, name): server = geodataServers()[self.comboGeodataServer.currentText()] server.deleteLayer(name) server.deleteStyle(name) self.updateLayerIsDataPublished(name, False) def unpublishMetadata(self, name): server = metadataServers()[self.comboMetadataServer.currentText()] uuid = uuidForLayer(self.layerFromName(name)) server.deleteMetadata(uuid) self.updateLayerIsMetadataPublished(name, False) def updateLayerIsMetadataPublished(self, name, value): self.isMetadataPublished[name] = value for i in range(self.listLayers.count()): item = self.listLayers.item(i) widget = self.listLayers.itemWidget(item) if widget.name() == name: server = metadataServers()[ self.comboMetadataServer.currentText()] if value else None widget.setMetadataPublished(server) def updateLayerIsDataPublished(self, name, value): self.isDataPublished[name] = value for i in range(self.listLayers.count()): item = self.listLayers.item(i) widget = self.listLayers.itemWidget(item) if widget.name() == name: server = geodataServers()[ self.comboGeodataServer.currentText()] if value else None widget.setDataPublished(server) def updateLayersPublicationStatus(self, data=True, metadata=True): canPublish = True if data: try: dataServer = geodataServers()[ self.comboGeodataServer.currentText()] if dataServer.testConnection(): self.comboGeodataServer.setStyleSheet("QComboBox { }") else: self.comboGeodataServer.setStyleSheet( "QComboBox { border: 2px solid red; }") dataServer = None canPublish = False except KeyError: self.comboGeodataServer.setStyleSheet("QComboBox { }") dataServer = None if metadata: try: metadataServer = metadataServers()[ self.comboMetadataServer.currentText()] if metadataServer.testConnection(): self.comboMetadataServer.setStyleSheet("QComboBox { }") else: self.comboMetadataServer.setStyleSheet( "QComboBox { border: 2px solid red; }") metadataServer = None canPublish = False except KeyError: self.comboMetadataServer.setStyleSheet("QComboBox { }") metadataServer = None for i in range(self.listLayers.count()): item = self.listLayers.item(i) widget = self.listLayers.itemWidget(item) name = widget.name() if data: server = None if dataServer: self.isDataPublished[name] = self.isDataOnServer(name) server = dataServer if self.isDataPublished[name] else None widget.setDataPublished(server) if metadata: server = None if metadataServer: self.isMetadataPublished[name] = self.isMetadataOnServer( name) server = metadataServer if self.isMetadataPublished[ name] else None widget.setMetadataPublished(server) canPublish = canPublish and self.listLayers.count() self.btnPublish.setEnabled(canPublish) self.btnPublishOnBackground.setEnabled(canPublish) def unpublishAll(self): for name in self.isDataPublished: if self.isDataPublished.get(name, False): self.unpublishData(name) if self.isMetadataPublished.get(name, False): self.unpublishMetadata(name) def viewWms(self, name): layer = self.layerFromName(name) names = [layer.name()] bbox = layer.extent() if bbox.isEmpty(): bbox.grow(1) sbbox = ",".join([ str(v) for v in [ bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum() ] ]) server = geodataServers()[self.comboGeodataServer.currentText()] server.openPreview(names, sbbox, layer.crs().authid()) def viewAllWms(self): server = geodataServers()[self.comboGeodataServer.currentText()] layers = self.publishableLayers() bbox = QgsRectangle() canvasCrs = iface.mapCanvas().mapSettings().destinationCrs() names = [] for layer in layers: if self.isDataPublished[layer.name()]: names.append(layer.name()) xform = QgsCoordinateTransform(layer.crs(), canvasCrs, QgsProject.instance()) extent = xform.transform(layer.extent()) bbox.combineExtentWith(extent) sbbox = ",".join([ str(v) for v in [ bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum() ] ]) server.openPreview(names, sbbox, canvasCrs.authid()) def previewMetadata(self): if self.currentLayer is None: return html = self.currentLayer.htmlMetadata() dlg = QgsMessageOutput.createMessageOutput() dlg.setTitle("Layer metadata") dlg.setMessage(html, QgsMessageOutput.MessageHtml) dlg.showMessage() def viewMetadata(self, name): server = metadataServers()[self.comboMetadataServer.currentText()] layer = self.layerFromName(name) uuid = uuidForLayer(layer) server.openMetadata(uuid) def publish(self): if self.validateBeforePublication(): toPublish = self._toPublish() progressDialog = ProgressDialog(toPublish, self.parent) task = self.getPublishTask(self.parent) task.stepStarted.connect(progressDialog.setInProgress) task.stepSkipped.connect(progressDialog.setSkipped) task.stepFinished.connect(progressDialog.setFinished) progressDialog.show() #task.progressChanged.connect(progress.setValue) ret = execute(task.run) progressDialog.close() #self.bar.clearWidgets() task.finished(ret) if task.exception is not None: if task.exceptiontype == requests.exceptions.ConnectionError: QMessageBox.warning( self, self.tr("Error while publishing"), self. tr("Connection error. Server unavailable.\nSee QGIS log for details" )) else: self.bar.clearWidgets() self.bar.pushMessage(self.tr("Error while publishing"), self.tr("See QGIS log for details"), level=Qgis.Warning, duration=5) QgsMessageLog.logMessage(task.exception, 'GeoCat Bridge', level=Qgis.Critical) if isinstance(task, PublishTask): self.updateLayersPublicationStatus( task.geodataServer is not None, task.metadataServer is not None) def publishOnBackground(self): if self.validateBeforePublication(): self.parent.close() task = self.getPublishTask(iface.mainWindow()) def _finished(): if task.exception is not None: iface.messageBar().pushMessage( self.tr("Error while publishing"), self.tr("See QGIS log for details"), level=Qgis.Warning, duration=5) QgsMessageLog.logMessage(task.exception, 'GeoCat Bridge', level=Qgis.Critical) task.taskTerminated.connect(_finished) QgsApplication.taskManager().addTask(task) QCoreApplication.processEvents() def validateBeforePublication(self): names = [] errors = set() for i in range(self.listLayers.count()): item = self.listLayers.item(i) widget = self.listLayers.itemWidget(item) if widget.checked(): name = widget.name() for c in "?&=#": if c in name: errors.add("Unsupported character in layer name: " + c) if name in names: errors.add("Several layers with the same name") names.append(name) if self.comboGeodataServer.currentIndex() != 0: geodataServer = geodataServers()[ self.comboGeodataServer.currentText()] geodataServer.validateBeforePublication(errors) if self.comboMetadataServer.currentIndex() != 0: metadataServer = metadataServers()[ self.comboMetadataServer.currentText()] metadataServer.validateBeforePublication(errors) if errors: txt = '''<p><b>Cannot publish data.</b></p> <p>The following issues were found:<p><ul><li>%s</li></ul> ''' % "</li><li>".join(errors) dlg = QgsMessageOutput.createMessageOutput() dlg.setTitle("Publish") dlg.setMessage(txt, QgsMessageOutput.MessageHtml) dlg.showMessage() return False else: return True def _toPublish(self): toPublish = [] for i in range(self.listLayers.count()): item = self.listLayers.item(i) widget = self.listLayers.itemWidget(item) if widget.checked(): name = widget.name() toPublish.append(name) return toPublish def getPublishTask(self, parent): self.storeMetadata() self.storeFieldsToPublish() toPublish = self._toPublish() if self.tabOnOffline.currentIndex() == 0: if self.comboGeodataServer.currentIndex() != 0: geodataServer = geodataServers()[ self.comboGeodataServer.currentText()] else: geodataServer = None if self.comboMetadataServer.currentIndex() != 0: metadataServer = metadataServers()[ self.comboMetadataServer.currentText()] else: metadataServer = None onlySymbology = self.chkOnlySymbology.checkState() == Qt.Checked return PublishTask(toPublish, self.fieldsToPublish, onlySymbology, geodataServer, metadataServer, parent) else: return ExportTask(self.txtExportFolder.text(), toPublish, self.fieldsToPublish, self.chkExportData.isChecked(), self.chkExportMetadata.isChecked(), self.chkExportSymbology.isChecked()) def layerFromName(self, name): layers = self.publishableLayers() for layer in layers: if layer.name() == name: return layer
class CreatePointsSurveyWizard(QWizard, WIZARD_UI): WIZARD_NAME = "CreatePointsSurveyWizard" WIZARD_TOOL_NAME = QCoreApplication.translate(WIZARD_NAME, "Create Point") def __init__(self, iface, db): QWizard.__init__(self) self.setupUi(self) self.iface = iface self._db = db self.logger = Logger() self.app = AppInterface() self.names = self._db.names self.help_strings = HelpStrings() self._layers = { self.names.LC_BOUNDARY_POINT_T: None, self.names.LC_SURVEY_POINT_T: None, self.names.LC_CONTROL_POINT_T: None } self.target_layer = None # Auxiliary data to set nonlinear next pages self.pages = [self.wizardPage1, self.wizardPage2, self.wizardPage3] self.dict_pages_ids = {self.pages[idx] : pid for idx, pid in enumerate(self.pageIds())} self.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.PointLayer) # Set connections self.btn_browse_file.clicked.connect( make_file_selector(self.txt_file_path, file_filter=QCoreApplication.translate("WizardTranslations",'CSV File (*.csv *.txt)'))) self.txt_file_path.textChanged.connect(self.file_path_changed) self.crsSelector.crsChanged.connect(self.crs_changed) self.crs = "" # SRS auth id self.txt_delimiter.textChanged.connect(self.fill_long_lat_combos) self.mMapLayerComboBox.layerChanged.connect(self.import_layer_changed) self.known_delimiters = [ {'name': ';', 'value': ';'}, {'name': ',', 'value': ','}, {'name': 'tab', 'value': '\t'}, {'name': 'space', 'value': ' '}, {'name': '|', 'value': '|'}, {'name': '~', 'value': '~'}, {'name': 'Other', 'value': ''} ] self.cbo_delimiter.addItems([ item['name'] for item in self.known_delimiters ]) self.cbo_delimiter.currentTextChanged.connect(self.separator_changed) self.restore_settings() self.txt_file_path.textChanged.emit(self.txt_file_path.text()) self.rad_boundary_point.toggled.connect(self.point_option_changed) self.rad_control_point.toggled.connect(self.point_option_changed) self.rad_csv.toggled.connect(self.adjust_page_2_controls) self.point_option_changed() # Initialize it self.button(QWizard.FinishButton).clicked.connect(self.finished_dialog) self.currentIdChanged.connect(self.current_page_changed) self.txt_help_page_2.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_2_OPTION_CSV) self.wizardPage2.setButtonText(QWizard.FinishButton, QCoreApplication.translate("WizardTranslations", "Import")) self.txt_help_page_3.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_3_OPTION_CSV) self.txt_help_page_3.anchorClicked.connect(self.save_template) self.button(QWizard.HelpButton).clicked.connect(self.show_help) self.rejected.connect(self.close_wizard) # Set MessageBar for QWizard self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def nextId(self): """ Set navigation order. Should return an integer. -1 is Finish. """ if self.currentId() == self.dict_pages_ids[self.wizardPage1]: return self.dict_pages_ids[self.wizardPage2] elif self.currentId() == self.dict_pages_ids[self.wizardPage2]: if self.rad_csv.isChecked(): return self.dict_pages_ids[self.wizardPage3] elif self.rad_refactor.isChecked(): return -1 elif self.currentId() == self.dict_pages_ids[self.wizardPage3]: return -1 else: return -1 def current_page_changed(self, id): """ Reset the Next button. Needed because Next might have been disabled by a condition in a another SLOT. """ enable_next_wizard(self) if id == self.dict_pages_ids[self.wizardPage2]: self.adjust_page_2_controls() elif id == self.dict_pages_ids[self.wizardPage3]: self.set_buttons_visible(False) self.set_buttons_enabled(False) QCoreApplication.processEvents() self.check_z_in_geometry() QCoreApplication.processEvents() self.fill_long_lat_combos("") QCoreApplication.processEvents() self.set_buttons_visible(True) self.set_buttons_enabled(True) def set_buttons_visible(self, visible): self.button(self.BackButton).setVisible(visible) self.button(self.FinishButton).setVisible(visible) self.button(self.CancelButton).setVisible(visible) def set_buttons_enabled(self, enabled): self.wizardPage3.setEnabled(enabled) self.button(self.BackButton).setEnabled(enabled) self.button(self.FinishButton).setEnabled(enabled) self.button(self.CancelButton).setEnabled(enabled) def check_z_in_geometry(self): self.target_layer = self.app.core.get_layer(self._db, self.current_point_name(), load=True) if not self.target_layer: return if not QgsWkbTypes().hasZ(self.target_layer.wkbType()): self.labelZ.setEnabled(False) self.cbo_elevation.setEnabled(False) msg = QCoreApplication.translate("WizardTranslations", "The current model does not support 3D geometries") self.cbo_elevation.setToolTip(msg) self.labelZ.setToolTip(msg) else: self.labelZ.setEnabled(True) self.cbo_elevation.setEnabled(True) self.labelZ.setToolTip("") self.cbo_elevation.setToolTip("") def adjust_page_2_controls(self): self.cbo_mapping.clear() self.cbo_mapping.addItem("") self.cbo_mapping.addItems(self.app.core.get_field_mappings_file_names(self.current_point_name())) if self.rad_refactor.isChecked(): self.lbl_refactor_source.setEnabled(True) self.mMapLayerComboBox.setEnabled(True) self.lbl_field_mapping.setEnabled(True) self.cbo_mapping.setEnabled(True) self.import_layer_changed(self.mMapLayerComboBox.currentLayer()) disable_next_wizard(self) self.wizardPage2.setFinalPage(True) self.txt_help_page_2.setHtml(self.help_strings.get_refactor_help_string(self._db, self._layers[self.current_point_name()])) elif self.rad_csv.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) self.lbl_field_mapping.setEnabled(False) self.cbo_mapping.setEnabled(False) self.lbl_refactor_source.setStyleSheet('') enable_next_wizard(self) self.wizardPage2.setFinalPage(False) self.txt_help_page_2.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_2_OPTION_CSV) def point_option_changed(self): if self.rad_boundary_point.isChecked(): self.gbx_page_2.setTitle(QCoreApplication.translate("WizardTranslations", "Load data to Boundary Points...")) self.gbx_page_3.setTitle(QCoreApplication.translate("WizardTranslations", "Configure CSV data source for Boundary Points...")) self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_1_OPTION_BP) elif self.rad_survey_point.isChecked(): # self.rad_survey_point is checked self.gbx_page_2.setTitle(QCoreApplication.translate("WizardTranslations", "Load data to Survey Points...")) self.gbx_page_3.setTitle(QCoreApplication.translate("WizardTranslations", "Configure CSV data source for Survey Points...")) self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_1_OPTION_SP) else: # self.rad_control_point is checked self.gbx_page_2.setTitle(QCoreApplication.translate("WizardTranslations", "Load data to Control Points...")) self.gbx_page_3.setTitle(QCoreApplication.translate("WizardTranslations", "Configure CSV data source for Control Points...")) self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_SURVEY_PAGE_1_OPTION_CP) def finished_dialog(self): self.save_settings() if self.rad_refactor.isChecked(): output_layer_name = self.current_point_name() if self.mMapLayerComboBox.currentLayer() is not None: field_mapping = self.cbo_mapping.currentText() res_etl_model = self.app.core.show_etl_model(self._db, self.mMapLayerComboBox.currentLayer(), output_layer_name, field_mapping=field_mapping) if res_etl_model: self.app.gui.redraw_all_layers() # Redraw all layers to show imported data # If the result of the etl_model is successful and we used a stored recent mapping, we delete the # previous mapping used (we give preference to the latest used mapping) if field_mapping: self.app.core.delete_old_field_mapping(field_mapping) self.app.core.save_field_mapping(output_layer_name) else: self.logger.warning_msg(__name__, QCoreApplication.translate("WizardTranslations", "Select a source layer to set the field mapping to '{}'.").format(output_layer_name)) self.close_wizard() elif self.rad_csv.isChecked(): self.prepare_copy_csv_points_to_db() def close_wizard(self, message=None, show_message=True): if message is None: message = QCoreApplication.translate("WizardTranslations", "'{}' tool has been closed.").format(self.WIZARD_TOOL_NAME) if show_message: self.logger.info_msg(__name__, message) self.close() def current_point_name(self): if self.rad_boundary_point.isChecked(): return self.names.LC_BOUNDARY_POINT_T elif self.rad_survey_point.isChecked(): return self.names.LC_SURVEY_POINT_T else: return self.names.LC_CONTROL_POINT_T def prepare_copy_csv_points_to_db(self): csv_path = self.txt_file_path.text().strip() if not csv_path or not os.path.exists(csv_path): self.logger.warning_msg(__name__, QCoreApplication.translate("WizardTranslations", "No CSV file given or file doesn't exist.")) return target_layer_name = self.current_point_name() with OverrideCursor(Qt.WaitCursor): csv_layer = self.app.core.csv_to_layer(csv_path, self.txt_delimiter.text(), self.cbo_longitude.currentText(), self.cbo_latitude.currentText(), self.crs, self.cbo_elevation.currentText() or None, self.detect_decimal_point(csv_path)) self.app.core.copy_csv_to_db(csv_layer, self._db, target_layer_name) def required_layers_are_available(self): layers_are_available = self.app.core.required_layers_are_available(self._db, self._layers, self.WIZARD_TOOL_NAME) return layers_are_available def file_path_changed(self): self.autodetect_separator() self.fill_long_lat_combos("") self.cbo_delimiter.currentTextChanged.connect(self.separator_changed) def detect_decimal_point(self, csv_path): if os.path.exists(csv_path): with open(csv_path) as file: file.readline() # headers data = file.readline().strip() # 1st line with data if data: fields = self.get_fields_from_csv_file(csv_path) if self.cbo_latitude.currentText() in fields: num_col = data.split(self.cbo_delimiter.currentText())[fields.index(self.cbo_latitude.currentText())] for decimal_point in ['.', ',']: if decimal_point in num_col: return decimal_point return '.' # just use the default one def autodetect_separator(self): csv_path = self.txt_file_path.text().strip() if os.path.exists(csv_path): with open(csv_path) as file: first_line = file.readline() for delimiter in self.known_delimiters: if delimiter['value'] == '': continue # if separator works like a column separator in header # number of cols is greater than 1 if len(first_line.split(delimiter['value'])) > 1: self.cbo_delimiter.setCurrentText(delimiter['name']) return def update_crs_info(self): self.crsSelector.setCrs(QgsCoordinateReferenceSystem(self.crs)) def crs_changed(self): self.crs = get_crs_authid(self.crsSelector.crs()) if self.crs != DEFAULT_SRS_AUTHID: self.lbl_crs.setStyleSheet('color: orange') self.lbl_crs.setToolTip(QCoreApplication.translate("WizardTranslations", "Your CSV data will be reprojected for you to '{}' (Colombian National Origin),<br>before attempting to import it into LADM-COL.").format(DEFAULT_SRS_AUTHID)) else: self.lbl_crs.setStyleSheet('') self.lbl_crs.setToolTip(QCoreApplication.translate("WizardTranslations", "Coordinate Reference System")) def fill_long_lat_combos(self, text): csv_path = self.txt_file_path.text().strip() self.cbo_longitude.clear() self.cbo_latitude.clear() self.cbo_elevation.clear() if os.path.exists(csv_path): self.button(QWizard.FinishButton).setEnabled(True) fields = self.get_fields_from_csv_file(csv_path) fields_dict = {field: field.lower() for field in fields} if not fields: self.button(QWizard.FinishButton).setEnabled(False) return self.cbo_longitude.addItems(fields) self.cbo_latitude.addItems(fields) self.cbo_elevation.addItems([""] + fields) # Heuristics to suggest values for x, y and z x_potential_names = ['x', 'lon', 'long', 'longitud', 'longitude', 'este', 'east', 'oeste', 'west'] y_potential_names = ['y', 'lat', 'latitud', 'latitude', 'norte', 'north'] z_potential_names = ['z', 'altura', 'elevacion', 'elevation', 'elevación', 'height'] for x_potential_name in x_potential_names: for k,v in fields_dict.items(): if x_potential_name == v: self.cbo_longitude.setCurrentText(k) break for y_potential_name in y_potential_names: for k, v in fields_dict.items(): if y_potential_name == v: self.cbo_latitude.setCurrentText(k) break if self.cbo_elevation.isEnabled(): for z_potential_name in z_potential_names: for k, v in fields_dict.items(): if z_potential_name == v: self.cbo_elevation.setCurrentText(k) break else: self.button(QWizard.FinishButton).setEnabled(False) def get_fields_from_csv_file(self, csv_path): if not self.txt_delimiter.text(): return [] error_reading = False try: reader = open(csv_path, "r") except IOError: error_reading = True line = reader.readline().replace("\n", "") reader.close() if not line: error_reading = True else: return line.split(self.txt_delimiter.text()) if error_reading: self.logger.warning_msg(__name__, QCoreApplication.translate("WizardTranslations", "It was not possible to read field names from the CSV. Check the file and try again.")) return [] def separator_changed(self, text): # first ocurrence value = next((x['value'] for x in self.known_delimiters if x['name'] == text), '') self.txt_delimiter.setText(value) if value == '': self.txt_delimiter.setEnabled(True) else: self.txt_delimiter.setEnabled(False) def save_template(self, url): link = url.url() if self.rad_boundary_point.isChecked(): if link == '#template': self.download_csv_file('template_boundary_points.csv') elif link == '#data': self.download_csv_file('sample_boundary_points.csv') elif self.rad_survey_point.isChecked(): if link == '#template': self.download_csv_file('template_survey_points.csv') elif link == '#data': self.download_csv_file('sample_survey_points.csv') elif self.rad_control_point.isChecked(): if link == '#template': self.download_csv_file('template_control_points.csv') elif link == '#data': self.download_csv_file('sample_control_points.csv') def download_csv_file(self, filename): settings = QSettings() settings.setValue('Asistente-LADM-COL/wizards/points_csv_file_delimiter', self.txt_delimiter.text().strip()) new_filename, filter = QFileDialog.getSaveFileName(self, QCoreApplication.translate("WizardTranslations", "Save File"), os.path.join(settings.value('Asistente-LADM-COL/wizards/points_download_csv_path', '.'), filename), QCoreApplication.translate("WizardTranslations", "CSV File (*.csv *.txt)")) if new_filename: settings.setValue('Asistente-LADM-COL/wizards/points_download_csv_path', os.path.dirname(new_filename)) template_file = QFile(":/Asistente-LADM-COL/resources/csv/" + filename) if not template_file.exists(): self.logger.critical(__name__, "CSV doesn't exist! Probably due to a missing 'make' execution to generate resources...") msg = QCoreApplication.translate("WizardTranslations", "CSV file not found. Update your plugin. For details see log.") self.show_message(msg, Qgis.Warning) return if os.path.isfile(new_filename): self.logger.info(__name__, 'Removing existing file {}...'.format(new_filename)) os.chmod(new_filename, 0o777) os.remove(new_filename) if template_file.copy(new_filename): os.chmod(new_filename, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) msg = QCoreApplication.translate("WizardTranslations", """The file <a href="file:///{}">{}</a> was successfully saved!""").format(normalize_local_url(new_filename), os.path.basename(new_filename)) self.show_message(msg, Qgis.Info) else: self.logger.warning(__name__, 'There was an error copying the CSV file {}!'.format(new_filename)) msg = QCoreApplication.translate("WizardTranslations", "The file couldn\'t be saved.") self.show_message(msg, Qgis.Warning) def import_layer_changed(self, layer): if layer: crs = get_crs_authid(layer.crs()) if crs != DEFAULT_SRS_AUTHID: self.lbl_refactor_source.setStyleSheet('color: orange') self.lbl_refactor_source.setToolTip(QCoreApplication.translate("WizardTranslations", "This layer will be reprojected for you to '{}' (Colombian National Origin),<br>before attempting to import it into LADM-COL.").format( DEFAULT_SRS_AUTHID)) else: self.lbl_refactor_source.setStyleSheet('') self.lbl_refactor_source.setToolTip('') def show_message(self, message, level): self.bar.clearWidgets() # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 10) def save_settings(self): settings = QSettings() point_type = None if self.rad_boundary_point.isChecked(): point_type = 'boundary_point' elif self.rad_survey_point.isChecked(): point_type = 'survey_point' else: point_type = 'control_point' settings.setValue('Asistente-LADM-COL/wizards/points_add_points_type', point_type) settings.setValue('Asistente-LADM-COL/wizards/points_load_data_type', 'csv' if self.rad_csv.isChecked() else 'refactor') settings.setValue('Asistente-LADM-COL/wizards/points_add_points_csv_file', self.txt_file_path.text().strip()) settings.setValue('Asistente-LADM-COL/wizards/points_csv_file_delimiter', self.txt_delimiter.text().strip()) settings.setValue('Asistente-LADM-COL/wizards/points_csv_crs', self.crs) def restore_settings(self): settings = QSettings() point_type = settings.value('Asistente-LADM-COL/wizards/points_add_points_type') or 'boundary_point' if point_type == 'boundary_point': self.rad_boundary_point.setChecked(True) elif point_type == 'survey_point': self.rad_survey_point.setChecked(True) else: # 'control_point' self.rad_control_point.setChecked(True) load_data_type = settings.value('Asistente-LADM-COL/wizards/points_load_data_type') or 'csv' if load_data_type == 'refactor': self.rad_refactor.setChecked(True) else: self.rad_csv.setChecked(True) self.txt_file_path.setText(settings.value('Asistente-LADM-COL/wizards/points_add_points_csv_file')) self.txt_delimiter.setText(settings.value('Asistente-LADM-COL/wizards/points_csv_file_delimiter')) self.crs = settings.value('Asistente-LADM-COL/wizards/points_csv_crs', DEFAULT_SRS_AUTHID, str) self.update_crs_info() def show_help(self): show_plugin_help("create_points")
class ImportFromExcelDialog(QDialog, DIALOG_UI): log_excel_show_message_emitted = pyqtSignal(str) def __init__(self, iface, db, qgis_utils, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.iface = iface self._db = db self.qgis_utils = qgis_utils self.logger = Logger() self.help_strings = HelpStrings() self.log_dialog_excel_text_content = "" self.group_parties_exists = False self.names = self._db.names self._running_tool = False self.tool_name = QCoreApplication.translate( "ImportFromExcelDialog", "Import intermediate structure") self.fields = { EXCEL_SHEET_NAME_PLOT: [ EXCEL_SHEET_TITLE_DEPARTMENT, EXCEL_SHEET_TITLE_MUNICIPALITY, EXCEL_SHEET_TITLE_ZONE, EXCEL_SHEET_TITLE_REGISTRATION_PLOT, EXCEL_SHEET_TITLE_NPN, EXCEL_SHEET_TITLE_NPV, EXCEL_SHEET_TITLE_PLOT_NAME, EXCEL_SHEET_TITLE_VALUATION, EXCEL_SHEET_TITLE_PLOT_CONDITION, EXCEL_SHEET_TITLE_PLOT_TYPE, EXCEL_SHEET_TITLE_ADDRESS ], EXCEL_SHEET_NAME_PARTY: [ EXCEL_SHEET_TITLE_FIRST_NAME, EXCEL_SHEET_TITLE_MIDDLE, EXCEL_SHEET_TITLE_FIRST_SURNAME, EXCEL_SHEET_TITLE_SECOND_SURNAME, EXCEL_SHEET_TITLE_BUSINESS_NAME, EXCEL_SHEET_TITLE_SEX, EXCEL_SHEET_TITLE_DOCUMENT_TYPE, EXCEL_SHEET_TITLE_DOCUMENT_NUMBER, EXCEL_SHEET_TITLE_KIND_PERSON, EXCEL_SHEET_TITLE_ISSUING_ENTITY, EXCEL_SHEET_TITLE_DATE_ISSUE, EXCEL_SHEET_TITLE_NPN ], EXCEL_SHEET_NAME_GROUP: [ EXCEL_SHEET_TITLE_NPN, EXCEL_SHEET_TITLE_DOCUMENT_TYPE, EXCEL_SHEET_TITLE_DOCUMENT_NUMBER, EXCEL_SHEET_TITLE_ID_GROUP ], EXCEL_SHEET_NAME_RIGHT: [ EXCEL_SHEET_TITLE_TYPE, EXCEL_SHEET_TITLE_PARTY_DOCUMENT_NUMBER, EXCEL_SHEET_TITLE_GROUP, EXCEL_SHEET_TITLE_NPN, EXCEL_SHEET_TITLE_SOURCE_TYPE, EXCEL_SHEET_TITLE_DESCRIPTION_SOURCE, EXCEL_SHEET_TITLE_STATE_SOURCE, EXCEL_SHEET_TITLE_OFFICIALITY_SOURCE, EXCEL_SHEET_TITLE_STORAGE_PATH ] } self.txt_help_page.setHtml(self.help_strings.DLG_IMPORT_FROM_EXCEL) self.txt_help_page.anchorClicked.connect(self.save_template) self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) #self.buttonBox.rejected.connect(self.rejected) self.buttonBox.helpRequested.connect(self.show_help) self.btn_browse_file.clicked.connect( make_file_selector( self.txt_excel_path, QCoreApplication.translate( "ImportFromExcelDialog", "Select the Excel file with data in the intermediate structure" ), QCoreApplication.translate("ImportFromExcelDialog", 'Excel File (*.xlsx *.xls)'))) self.buttonBox.button(QDialogButtonBox.Ok).setText( QCoreApplication.translate("ImportFromExcelDialog", "Import")) self.initialize_feedback() self.restore_settings() self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) # self.tabWidget.currentWidget().layout().setContentsMargins(0, 0, 0, 0) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def accepted(self): self.save_settings() self.import_from_excel() def import_from_excel(self): self._running_tool = True steps = 18 step = 0 self.progress.setVisible(True) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) # Where to store the reports? excel_path = self.txt_excel_path.text() if not excel_path: self.show_message( QCoreApplication.translate( "ImportFromExcelDialog", "You need to select an Excel file before continuing with the import." ), Qgis.Warning) self.progress.setVisible(False) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) return if not os.path.exists(excel_path): self.show_message( QCoreApplication.translate( "ImportFromExcelDialog", "The specified Excel file does not exist!"), Qgis.Warning) self.progress.setVisible(False) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) return self.progress.setVisible(True) self.txt_log.setText( QCoreApplication.translate( "ImportFromExcelDialog", "Loading tables from the Excel file...")) # Now that we have the Excel file, build vrts to load its sheets appropriately # Also validate each layer against a number of rules layer_parcel = self.check_layer_from_excel_sheet( excel_path, EXCEL_SHEET_NAME_PLOT) layer_party = self.check_layer_from_excel_sheet( excel_path, EXCEL_SHEET_NAME_PARTY) layer_group_party = self.check_layer_from_excel_sheet( excel_path, EXCEL_SHEET_NAME_GROUP) layer_right = self.check_layer_from_excel_sheet( excel_path, EXCEL_SHEET_NAME_RIGHT) if layer_parcel is None or layer_party is None or layer_group_party is None or layer_right is None: # A layer is None if at least an error was found self.group_parties_exists = False self.log_excel_show_message_emitted.emit( self.log_dialog_excel_text_content) self.done(0) return if not layer_group_party.isValid() or not layer_party.isValid( ) or not layer_parcel.isValid() or not layer_right.isValid(): self.show_message( QCoreApplication.translate( "ImportFromExcelDialog", "One of the sheets of the Excel file couldn't be loaded! Check the format again." ), Qgis.Warning) self.progress.setVisible(False) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) return QgsProject.instance().addMapLayers( [layer_group_party, layer_party, layer_parcel, layer_right]) # GET LADM LAYERS layers = { self.names.OP_PARTY_T: { 'name': self.names.OP_PARTY_T, 'geometry': None, LAYER: None }, self.names.OP_PARCEL_T: { 'name': self.names.OP_PARCEL_T, 'geometry': None, LAYER: None }, self.names.OP_RIGHT_T: { 'name': self.names.OP_RIGHT_T, 'geometry': None, LAYER: None }, self.names.EXT_ARCHIVE_S: { 'name': self.names.EXT_ARCHIVE_S, 'geometry': None, LAYER: None }, self.names.COL_RRR_SOURCE_T: { 'name': self.names.COL_RRR_SOURCE_T, 'geometry': None, LAYER: None }, self.names.OP_GROUP_PARTY_T: { 'name': self.names.OP_GROUP_PARTY_T, 'geometry': None, LAYER: None }, self.names.MEMBERS_T: { 'name': self.names.MEMBERS_T, 'geometry': None, LAYER: None }, self.names.OP_ADMINISTRATIVE_SOURCE_T: { 'name': self.names.OP_ADMINISTRATIVE_SOURCE_T, 'geometry': None, LAYER: None } } self.qgis_utils.get_layers(self._db, layers, load=True) if not layers: return None # Get feature counts to compare after the ETL and know how many records were imported to each ladm_col table ladm_tables = [ layers[self.names.OP_PARCEL_T][LAYER], layers[self.names.OP_PARTY_T][LAYER], layers[self.names.OP_RIGHT_T][LAYER], layers[self.names.OP_ADMINISTRATIVE_SOURCE_T][LAYER], layers[self.names.COL_RRR_SOURCE_T][LAYER], layers[self.names.OP_GROUP_PARTY_T][LAYER], layers[self.names.MEMBERS_T][LAYER] ] ladm_tables_feature_count_before = { t.name(): t.featureCount() for t in ladm_tables } # Run the ETL params = { 'agrupacion': layers[self.names.OP_GROUP_PARTY_T][LAYER], 'colmiembros': layers[self.names.MEMBERS_T][LAYER], 'colrrrsourcet': layers[self.names.COL_RRR_SOURCE_T][LAYER], 'extarchivo': layers[self.names.EXT_ARCHIVE_S][LAYER], 'interesado': layers[self.names.OP_PARTY_T][LAYER], 'layergroupparty': layer_group_party, 'layerparcel': layer_parcel, 'layerparty': layer_party, 'layerright': layer_right, 'opderecho': layers[self.names.OP_RIGHT_T][LAYER], 'opfuenteadministrativatipo': layers[self.names.OP_ADMINISTRATIVE_SOURCE_T][LAYER], 'parcel': layers[self.names.OP_PARCEL_T][LAYER] } self.qgis_utils.disable_automatic_fields(self._db, self.names.OP_GROUP_PARTY_T) self.qgis_utils.disable_automatic_fields(self._db, self.names.OP_RIGHT_T) self.qgis_utils.disable_automatic_fields( self._db, self.names.OP_ADMINISTRATIVE_SOURCE_T) processing.run("model:ETL_intermediate_structure", params, feedback=self.feedback) if not self.feedback.isCanceled(): self.progress.setValue(100) self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: self.initialize_feedback() # Print summary getting feature count in involved LADM_COL tables... summary = """<html><head/><body><p>""" summary += QCoreApplication.translate("ImportFromExcelDialog", "Import done!!!<br/>") for table in ladm_tables: summary += QCoreApplication.translate( "ImportFromExcelDialog", "<br/><b>{count}</b> records loaded into table <b>{table}</b>" ).format(count=table.featureCount() - ladm_tables_feature_count_before[table.name()], table=table.name()) summary += """</body></html>""" self.txt_log.setText(summary) self.logger.success_msg( __name__, QCoreApplication.translate( "QGISUtils", "Data successfully imported to LADM_COL from intermediate structure (Excel file: '{}')!!!" ).format(excel_path)) self._running_tool = False def check_layer_from_excel_sheet(self, excel_path, sheetname): layer = self.get_layer_from_excel_sheet(excel_path, sheetname) error_counter = 0 if layer is None and sheetname != EXCEL_SHEET_NAME_GROUP: # optional sheet self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The {} sheet has not information or has another name."). format(sheetname)) error_counter += 1 else: title_validator = layer.fields().toList() if sheetname == EXCEL_SHEET_NAME_PLOT and layer is not None: if not title_validator: self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The title does not match the format in the sheet {}." ).format(sheetname)) error_counter += 1 if list(layer.getFeatures('"numero predial nuevo" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column numero predial nuevo has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if not self.check_field_numeric_layer(layer, 'departamento'): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column departamento has non-numeric values in sheet {}." ).format(sheetname)) error_counter += 1 if not self.check_field_numeric_layer(layer, 'municipio'): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column municipio has non-numeric values in sheet {}." ).format(sheetname)) error_counter += 1 if not self.check_field_numeric_layer(layer, 'numero predial nuevo'): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column numero predial nuevo has non-numeric values in sheet {}." ).format(sheetname)) error_counter += 1 if sheetname == EXCEL_SHEET_NAME_PARTY and layer is not None: if not title_validator: self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The title does not match the format in sheet {}."). format(sheetname)) error_counter += 1 if list(layer.getFeatures('"tipo documento" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column tipo documento has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if list(layer.getFeatures('"numero de documento" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column numero de documento has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if not self.check_length_attribute_value( layer, 'numero de documento', 12): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column numero de documento has more characters than expected in sheet {}." ).format(sheetname)) error_counter += 1 if list(layer.getFeatures('"tipo persona" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column tipo persona has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if sheetname == EXCEL_SHEET_NAME_GROUP and layer is not None: if not title_validator: self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The title does not match the format in the sheet {}." ).format(sheetname)) error_counter += 1 self.group_parties_exists = True if list(layer.getFeatures('"numero predial nuevo" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column numero predial nuevo has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if list(layer.getFeatures('"tipo documento" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column tipo documento has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if list(layer.getFeatures('"numero de documento" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column numero de documento has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if list(layer.getFeatures('"id agrupación" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column id agrupación has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if not self.check_length_attribute_value( layer, 'numero de documento', 12): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column numero de documento has more characters of the permitted in sheet {}." ).format(sheetname)) error_counter += 1 if sheetname == EXCEL_SHEET_NAME_RIGHT and layer is not None: if not title_validator: self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The title does not match the format in sheet {}."). format(sheetname)) error_counter += 1 if list(layer.getFeatures('"tipo" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column tipo has empty values in sheet {}."). format(sheetname)) error_counter += 1 if list(layer.getFeatures('"tipo de fuente" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column tipo de fuente has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if list( layer.getFeatures( '"estado_disponibilidad de la fuente" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column estado_disponibilidad de la fuente has empty values in sheet {}." ).format(sheetname)) error_counter += 1 #if list(layer.getFeatures('"Ruta de Almacenamiento de la fuente" is Null')): # self.generate_message_excel_error(QCoreApplication.translate("ImportFromExcelDialog", # "The column Ruta de Almacenamiento de la fuente has empty values in sheet {}.").format(sheetname)) # error_counter += 1 if len( list( layer.getFeatures( '"número documento Interesado" is Null'))) + len( list(layer.getFeatures('"agrupación" is Null')) ) != layer.featureCount(): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "Number of non-null parties plus number of non-null group parties is not equal to number of records in sheet {}. There might be rights without party or group party associated." ).format(sheetname)) error_counter += 1 if not self.group_parties_exists: if list( layer.getFeatures( '"número documento Interesado" is Null')): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column número documento Interesado has empty values in sheet {}." ).format(sheetname)) error_counter += 1 if len(list(layer.getFeatures( '"agrupacion" is Null'))) != layer.featureCount(): self.generate_message_excel_error( QCoreApplication.translate( "ImportFromExcelDialog", "The column agrupacion has data but the sheet does not exist in sheet {}." ).format(sheetname)) error_counter += 1 return layer if error_counter == 0 else None def check_field_numeric_layer(self, layer, name): id_field_idx = layer.fields().indexFromName(name) request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx]) features = layer.getFeatures(request) is_numeric = True for feature in features: try: int(feature[name]) except: is_numeric = False break return is_numeric def check_length_attribute_value(self, layer, name, size): id_field_idx = layer.fields().indexFromName(name) request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx]) features = layer.getFeatures(request) right_length = True for feature in features: if len(str(feature[name])) > size: right_length = False break return right_length def generate_message_excel_error(self, msg): self.log_dialog_excel_text_content += "{}{}{}{}{}{}".format( LOG_QUALITY_LIST_CONTAINER_OPEN, LOG_QUALITY_LIST_ITEM_ERROR_OPEN, msg, LOG_QUALITY_LIST_ITEM_ERROR_CLOSE, LOG_QUALITY_LIST_CONTAINER_CLOSE, LOG_QUALITY_CONTENT_SEPARATOR) def get_layer_from_excel_sheet(self, excel_path, sheetname): basename = os.path.basename(excel_path) filename = os.path.splitext(basename)[0] dirname = os.path.dirname(excel_path) header_in_first_row, count = self.get_excel_info(excel_path, sheetname) if header_in_first_row is None and count is None: return None layer_definition = "<SrcLayer>{sheetname}</SrcLayer>".format( sheetname=sheetname) if header_in_first_row: layer_definition = """<SrcSql dialect="sqlite">SELECT * FROM '{sheetname}' LIMIT {count} OFFSET 1</SrcSql>""".format( sheetname=sheetname, count=count) xml_text_group_party = """<?xml version="1.0" encoding="UTF-8"?> <OGRVRTDataSource> <OGRVRTLayer name="{filename}-{sheetname}"> <SrcDataSource relativeToVRT="1">{basename}</SrcDataSource> <!--Header={header}--> {layer_definition} {fields} </OGRVRTLayer> </OGRVRTDataSource> """.format(filename=filename, basename=basename, header=header_in_first_row, layer_definition=layer_definition, sheetname=sheetname, fields=self.get_vrt_fields(sheetname, header_in_first_row)) group_party_file_path = os.path.join( dirname, '{}.{}.vrt'.format(basename, sheetname)) with open(group_party_file_path, 'w') as sheet: sheet.write(xml_text_group_party) uri = '{vrtfilepath}|layername={filename}-{sheetname}'.format( vrtfilepath=group_party_file_path, sheetname=sheetname, filename=filename) self.logger.info(__name__, "Loading layer from excel with uri='{}'".format(uri)) layer = QgsVectorLayer(uri, '{}-{}'.format('excel', sheetname), 'ogr') layer.setProviderEncoding('UTF-8') return layer def get_excel_info(self, path, sheetname): data_source = ogr.Open(path, 0) layer = data_source.GetLayerByName(sheetname) if layer is None: # A sheetname couldn't be found return None, None feature = layer.GetNextFeature() # If ogr recognizes the header, the first row will contain data, otherwise it'll contain field names header_in_first_row = True for field in self.fields[sheetname]: if feature.GetField(self.fields[sheetname].index(field)) != field: header_in_first_row = False num_rows = layer.GetFeatureCount() return header_in_first_row, num_rows - 1 if header_in_first_row else num_rows def get_vrt_fields(self, sheetname, header_in_first_row): vrt_fields = "" for index, field in enumerate(self.fields[sheetname]): vrt_fields += """<Field name="{field}" src="{src}" type="String"/>\n""".format( field=field, src='Field{}'.format(index + 1) if header_in_first_row else field) return vrt_fields.strip() def save_template(self, url): link = url.url() if link == '#template': self.download_excel_file('plantilla_estructura_excel.xlsx') elif link == '#data': self.download_excel_file('datos_estructura_excel.xlsx') def download_excel_file(self, filename): settings = QSettings() new_filename, filter = QFileDialog.getSaveFileName( self, QCoreApplication.translate("ImportFromExcelDialog", "Save File"), os.path.join( settings.value( 'Asistente-LADM_COL/import_from_excel_dialog/template_save_path', '.'), filename), QCoreApplication.translate("ImportFromExcelDialog", "Excel File (*.xlsx *.xls)")) if new_filename: settings.setValue( 'Asistente-LADM_COL/import_from_excel_dialog/template_save_path', os.path.dirname(new_filename)) template_file = QFile(":/Asistente-LADM_COL/resources/excel/" + filename) if not template_file.exists(): self.logger.critical( __name__, "Excel doesn't exist! Probably due to a missing 'make' execution to generate resources..." ) msg = QCoreApplication.translate( "ImportFromExcelDialog", "Excel file not found. Update your plugin. For details see log." ) self.show_message(msg, Qgis.Warning) return if os.path.isfile(new_filename): self.logger.info( __name__, 'Removing existing file {}...'.format(new_filename)) os.chmod(new_filename, 0o777) os.remove(new_filename) if template_file.copy(new_filename): os.chmod( new_filename, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) msg = QCoreApplication.translate( "ImportFromExcelDialog", """The file <a href="file:///{}">{}</a> was successfully saved!""" ).format(normalize_local_url(new_filename), os.path.basename(new_filename)) self.show_message(msg, Qgis.Info) else: self.logger.info( __name__, 'There was an error copying the CSV file {}!'.format( new_filename)) msg = QCoreApplication.translate( "ImportFromExcelDialog", "The file couldn\'t be saved.") self.show_message(msg, Qgis.Warning) def reject(self): self.selected_items_dict = dict() if self._running_tool: reply = QMessageBox.question( self, QCoreApplication.translate("import_from_excel", "Warning"), QCoreApplication.translate( "import_from_excel", "The '{}' tool is still running. Do you want to cancel it? If you cancel, the data might be incomplete in the target database." ).format(self.tool_name), QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.feedback.cancel() self._running_tool = False msg = QCoreApplication.translate( "import_from_excel", "The '{}' tool was cancelled.").format(self.tool_name) self.logger.info(__name__, msg) self.show_message(msg, Qgis.Info) else: self.logger.info(__name__, "Dialog closed.") self.done(1) def save_settings(self): settings = QSettings() settings.setValue( 'Asistente-LADM_COL/import_from_excel_dialog/excel_path', self.txt_excel_path.text()) def restore_settings(self): settings = QSettings() self.txt_excel_path.setText( settings.value( 'Asistente-LADM_COL/import_from_excel_dialog/excel_path', '')) def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 10) def show_help(self): self.qgis_utils.show_help("import_from_excel") def progress_changed(self): QCoreApplication.processEvents() # Listen to cancel from the user self.progress.setValue(self.feedback.progress()) def initialize_feedback(self): self.progress.setValue(0) self.progress.setVisible(False) self.feedback = QgsProcessingFeedback() self.feedback.progressChanged.connect(self.progress_changed) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True)
class AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(alg.getParametersPanel(self)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.cornerWidget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 5) self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }") self.runAsBatchButton = QPushButton(self.tr("Run as batch process...")) self.runAsBatchButton.clicked.connect(self.runAsBatch) layout.addWidget(self.runAsBatchButton) self.cornerWidget.setLayout(layout) self.tabWidget.setCornerWidget(self.cornerWidget) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def setParamValues(self): params = self.alg.parameters outputs = self.alg.outputs for param in params: if param.hidden: continue wrapper = self.mainWidget.wrappers[param.name] if not self.setParamValue(param, wrapper): raise AlgorithmDialogBase.InvalidParameterValue( param, wrapper.widget) for output in outputs: if output.hidden: continue output.value = self.mainWidget.outputWidgets[ output.name].getValue() if isinstance(output, (OutputRaster, OutputVector, OutputTable)): output.open = self.mainWidget.checkBoxes[ output.name].isChecked() return True def setParamValue(self, param, wrapper): if wrapper.widget: return param.setValue(wrapper.value()) else: return True def checkExtentCRS(self): unmatchingCRS = False hasExtent = False projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = dataobjects.getAllLayers() for param in self.alg.parameters: if isinstance( param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = dataobjects.getObjectFromUri(inputlayer) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): if param.skip_crs_check: continue value = self.mainWidget.wrappers[ param.name].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): self.settings.setValue("/Processing/dialogBase", self.saveGeometry()) checkCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_CRS) try: self.setParamValues() if checkCRS and not self.alg.checkInputCRS(): reply = QMessageBox.question( self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) if checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question( self, self.tr("Extent CRS"), self. tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return msg = self.alg._checkParameterValuesBeforeExecuting() if msg: QMessageBox.warning(self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.setInfo( self.tr('<b>Algorithm %s starting...</b>') % self.alg.name) if self.iterateParam: if runalgIterating(self.alg, self.iterateParam, self.feedback): self.finish() else: QApplication.restoreOverrideCursor() self.resetGUI() else: command = self.alg.getAsCommand() if command: ProcessingLog.addToLog(ProcessingLog.LOG_ALGORITHM, command) if runalg(self.alg, self.feedback): self.finish() else: QApplication.restoreOverrideCursor() self.resetGUI() except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect( lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage("", "Wrong or missing parameter value: %s" % e.parameter.description, level=QgsMessageBar.WARNING, duration=5) def finish(self): keepOpen = ProcessingConfig.getSetting( ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: if not handleAlgorithmResults(self.alg, self.feedback, not keepOpen): self.resetGUI() return self.executed = True self.setInfo('Algorithm %s finished' % self.alg.name) QApplication.restoreOverrideCursor() if not keepOpen: self.close() else: self.resetGUI() if self.alg.getHTMLOutputsCount() > 0: self.setInfo( self.tr('HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.')) def closeEvent(self, evt): QgsProject.instance().layerWasAdded.disconnect( self.mainWidget.layerRegistryChanged) QgsProject.instance().layersWillBeRemoved.disconnect( self.mainWidget.layerRegistryChanged) super(AlgorithmDialog, self).closeEvent(evt)
class geopunt4QgisAdresDialog(QtGui.QDialog): def __init__(self, iface): QtGui.QDialog.__init__(self, None) self.setWindowFlags( self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint ) #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QtCore.QSettings().value("locale/userLocale", "nl") if not locale: locale == 'nl' else: locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QtCore.QTranslator() self.translator.load(localePath) if QtCore.qVersion() > '4.3.3': QtCore.QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_geopunt4Qgis() self.ui.setupUi(self) #get settings self.s = QtCore.QSettings() self.loadSettings() #setup geometryHelper object self.gh = gh.geometryHelper(self.iface) #create graphicsLayer self.graphicsLayer = [] #populate gemeenteBox gemeentes = json.load( open(os.path.join(os.path.dirname(__file__), "data/gemeentenVL.json")) ) gemeenteNamen = [str( n["Naam"]) for n in gemeentes] self.ui.gemeenteBox.addItems( gemeenteNamen ) self.ui.gemeenteBox.setEditText(QtCore.QCoreApplication.translate( "geopunt4QgisAdresDialog", "gemeente")) self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}') self.ui.gemeenteBox.setFocus() self.completer = QtGui.QCompleter( self ) self.completerModel = QtGui.QStringListModel( self ) self.ui.gemeenteBox.setCompleter( self.completer ) self.completer.setModel( self.completerModel ) self.completer.setCaseSensitivity(False) self.completerModel.setStringList( gemeenteNamen ) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed ) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton( QtGui.QPushButton("Sluiten"), QtGui.QDialogButtonBox.RejectRole ) for btn in self.ui.buttonBox.buttons(): btn.setAutoDefault(0) #event handlers if self.adresSearchOnEnter: self.ui.zoekText.returnPressed.connect(self.onZoekActivated) else: self.ui.zoekText.textEdited.connect(self.onZoekActivated) self.ui.gemeenteBox.currentIndexChanged.connect(self.onZoekActivated) self.ui.resultLijst.itemDoubleClicked.connect(self.onItemActivated) self.ui.resultLijst.itemClicked.connect(self.onItemClick) self.ui.ZoomKnop.clicked.connect(self.onZoomKnopClick) self.ui.Add2mapKnop.clicked.connect(self.onAdd2mapKnopClick) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean ) def loadSettings(self): self.saveToFile = int( self.s.value("geopunt4qgis/adresSavetoFile" , 1)) layerName = self.s.value("geopunt4qgis/adreslayerText", "") if layerName : self.layerName= layerName self.adresSearchOnEnter = int( self.s.value("geopunt4qgis/adresSearchOnEnter" , 0)) self.timeout = int( self.s.value("geopunt4qgis/timeout" ,15)) if int( self.s.value("geopunt4qgis/useProxy" , 0)): self.proxy = self.s.value("geopunt4qgis/proxyHost" ,"") self.port = self.s.value("geopunt4qgis/proxyPort" ,"") else: self.proxy = "" self.port = "" self.startDir = self.s.value("geopunt4qgis/startDir", os.path.dirname(__file__)) self.gp = geopunt.Adres(self.timeout, self.proxy, self.port) def openHelp(self): webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/zoek-een-adres") def onZoekActivated(self): self._clearGraphicsLayer() self.bar.clearWidgets() gemeente = self.ui.gemeenteBox.currentText() if gemeente <> QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","gemeente"): self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #000000}') txt = self.ui.zoekText.text() +", "+ gemeente suggesties = self.gp.fetchSuggestion( txt , 25 ) self.ui.resultLijst.clear() if type( suggesties ) is list and len(suggesties) <> 0: self.ui.resultLijst.addItems(suggesties) if len(suggesties) == 1: self.ui.resultLijst.setCurrentRow(0) elif type( suggesties ) is str: self.bar.pushMessage( QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","Waarschuwing"), suggesties, level=QgsMessageBar.WARNING) def onItemActivated( self, item): txt = item.text() self._zoomLoc(txt) def onItemClick(self, item): txt = item.text() streetNr = txt.split(",")[:-1] self.ui.zoekText.setText( ",".join(streetNr) ) def onZoomKnopClick(self): item = self.ui.resultLijst.currentItem() if item: self._zoomLoc(item.text()) def onAdd2mapKnopClick(self): item = self.ui.resultLijst.currentItem() if item: self._addToMap(item.text()) def _clearGraphicsLayer(self): for graphic in self.graphicsLayer: self.iface.mapCanvas().scene().removeItem(graphic) self.graphicsLayer = [] def _zoomLoc(self, txt): self._clearGraphicsLayer() locations = self.gp.fetchLocation(txt) if type( locations ) is list and len(locations): loc = locations[0] LowerLeftX = loc['BoundingBox']['LowerLeft']['X_Lambert72'] LowerLeftY = loc['BoundingBox']['LowerLeft']['Y_Lambert72'] UpperRightX = loc['BoundingBox']['UpperRight']['X_Lambert72'] UpperRightY = loc['BoundingBox']['UpperRight']['Y_Lambert72'] self.gh.zoomtoRec(QgsPoint(LowerLeftX,LowerLeftY),QgsPoint(UpperRightX, UpperRightY), 31370) xlb, ylb = loc["Location"]["X_Lambert72"], loc["Location"]["Y_Lambert72"] x, y = self.gh.prjPtToMapCrs(QgsPoint( xlb , ylb), 31370) m = QgsVertexMarker(self.iface.mapCanvas()) self.graphicsLayer.append(m) m.setCenter(QgsPoint(x,y)) m.setColor(QtGui.QColor(255,255,0)) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(9) elif type( locations ) is str: self.bar.pushMessage( QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","Waarschuwing"), locations, level=QgsMessageBar.WARNING, duration=3) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","onbekende fout"), level=QgsMessageBar.CRITICAL, duration=3) def _addToMap(self, txt): if not self.layernameValid(): return locations = self.gp.fetchLocation(txt) if type( locations ) is list and len(locations): loc = locations[0] x, y = loc["Location"]["X_Lambert72"], loc["Location"]["Y_Lambert72"] adres = loc["FormattedAddress"] LocationType = loc["LocationType"] pt = self.gh.prjPtToMapCrs(QgsPoint( x, y), 31370) self.gh.save_adres_point( pt, adres, typeAddress=LocationType, layername=self.layerName, saveToFile=self.saveToFile, sender=self, startFolder= os.path.join(self.startDir, self.layerName)) elif type( locations ) is str: self.bar.pushMessage( QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","Waarschuwing"), locations, level=QgsMessageBar.WARNING) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("geopunt4QgisAdresDialog","onbekende fout"), level=QgsMessageBar.CRITICAL) def layernameValid(self): if not hasattr(self, 'layerName'): layerName, accept = QtGui.QInputDialog.getText(None, QtCore.QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QtCore.QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return False else: self.layerName = layerName return True def clean(self): self.bar.clearWidgets() self.ui.resultLijst.clear() self.ui.zoekText.setText("") self.ui.gemeenteBox.setEditText(QtCore.QCoreApplication.translate( "geopunt4QgisAdresDialog","gemeente")) self.ui.gemeenteBox.setStyleSheet('QComboBox {color: #808080}') self._clearGraphicsLayer()