class SaveQueryDialog(QDialog, Ui_ui_save_query): # Signal new query signal_new_query_successful = pyqtSignal( name='signal_new_query_successful') def __init__( self, parent=None, query=None, white_list_values=None, output_geometry_types=None): """ SaveQueryDialog constructor @param query:query to save @type query: str @param white_list_values: doc of layers with columns @type white_list_values: dic @param output_geometry_types: list of layers @type output_geometry_types: list """ super(SaveQueryDialog, self).__init__(parent) QDialog.__init__(self) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar) self.whiteListValues = white_list_values self.outputGeomTypes = output_geometry_types self.query = query def accept(self): """ On accept, we call the FileQueryWriter """ category = self.lineEdit_category.text() name = self.lineEdit_name.text() # Get folder .qgis2/QuickOSM/queries on linux for instance folder = get_user_query_folder() ini_file = FileQueryWriter( path=folder, name=name, category=category, query=self.query, whiteListValues=self.whiteListValues, outputGeomTypes=self.outputGeomTypes) try: ini_file.save() self.signal_new_query_successful.emit() self.hide() except QuickOsmException, e: self.bar.pushMessage(e.msg, level=e.level, duration=e.duration) except Exception, e: self.display_exception(e)
class MyDialog(QDialog): def __init__(self): QDialog.__init__(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok) self.buttonbox.accepted.connect(self.run) self.layout().addWidget(self.buttonbox, 0, 0, 2, 1) self.layout().addWidget(self.bar, 0, 0, 1, 1) def run(self): self.bar.pushMessage("Hello", "World", level=QgsMessageBar.INFO)
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 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 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 BatchAlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setWindowTitle(self.tr('Batch Processing - {0}').format(self.alg.displayName())) self.setMainWidget(BatchPanel(self, self.alg)) self.textShortHelp.setVisible(False) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) def accept(self): alg_parameters = [] load = [] feedback = self.createFeedback() context = dataobjects.createContext(feedback) for row in range(self.mainWidget.tblParameters.rowCount()): col = 0 parameters = {} for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden or param.isDestination(): continue wrapper = self.mainWidget.wrappers[row][col] parameters[param.name()] = wrapper.value() if not param.checkValueIsAcceptable(wrapper.value(), context): self.bar.pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format( param.description(), row + 1), level=QgsMessageBar.WARNING, duration=5) return col += 1 count_visible_outputs = 0 for out in self.alg.destinationParameterDefinitions(): if out.flags() & QgsProcessingParameterDefinition.FlagHidden: continue count_visible_outputs += 1 widget = self.mainWidget.tblParameters.cellWidget(row, col) text = widget.getValue() if param.checkValueIsAcceptable(text, context): if isinstance(out, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink)): # load rasters and sinks on completion parameters[out.name()] = QgsProcessingOutputLayerDefinition(text, context.project()) else: parameters[out.name()] = text col += 1 else: self.bar.pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format( out.description(), row + 1), level=QgsMessageBar.WARNING, duration=5) return alg_parameters.append(parameters) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.mainWidget.setEnabled(False) self.buttonCancel.setEnabled(True) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass start_time = time.time() algorithm_results = [] for count, parameters in enumerate(alg_parameters): if feedback.isCanceled(): break self.setText(self.tr('\nProcessing algorithm {0}/{1}...').format(count + 1, len(alg_parameters))) 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('') alg_start_time = time.time() ret, results = execute(self.alg, parameters, context, feedback) if ret: self.setInfo(self.tr('Algorithm {0} correctly executed...').format(self.alg.displayName()), escape_html=False) feedback.setProgress(100) feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - alg_start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(results)) feedback.pushInfo('') algorithm_results.append(results) else: break feedback.pushInfo(self.tr('Batch execution completed in {0:0.2f} seconds'.format(time.time() - start_time))) handleAlgorithmResults(self.alg, context, feedback, False) self.finish(algorithm_results) self.buttonCancel.setEnabled(False) def finish(self, algorithm_results): for count, results in enumerate(algorithm_results): self.loadHTMLResults(results, count) self.createSummaryTable(algorithm_results) QApplication.restoreOverrideCursor() self.mainWidget.setEnabled(True) QMessageBox.information(self, self.tr('Batch processing'), self.tr('Batch processing completed')) def loadHTMLResults(self, results, num): for out in self.alg.outputDefinitions(): if isinstance(out, QgsProcessingOutputHtml) and out.name() in results and results[out.name()]: resultsList.addResult(icon=self.alg.icon(), name='{} [{}]'.format(out.description(), num), result=results[out.name()]) def createSummaryTable(self, algorithm_results): createTable = False for out in self.alg.outputDefinitions(): if isinstance(out, (QgsProcessingOutputNumber, QgsProcessingOutputString)): createTable = True break if not createTable: return outputFile = getTempFilename('html') with codecs.open(outputFile, 'w', encoding='utf-8') as f: for res in algorithm_results: f.write('<hr>\n') for out in self.alg.outputDefinitions(): if isinstance(out, (QgsProcessingOutputNumber, QgsProcessingOutputString)) and out.name() in res: f.write('<p>{}: {}</p>\n'.format(out.description(), res[out.name()])) f.write('<hr>\n') resultsList.addResult(self.alg.icon(), '{} [summary]'.format(self.alg.name()), outputFile)
class geopunt4QgisDataCatalog(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_geopunt4QgisDataCatalogDlg() self.ui.setupUi(self) #get settings self.s = QtCore.QSettings() self.loadSettings() self.gh = gh.geometryHelper( self.iface ) #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 ) #vars self.firstShow = True self.wms = None self.wfs = None self.dl = None self.zoek = '' self.bronnen = None self.model = QtGui.QStandardItemModel( self ) self.proxyModel = QtGui.QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.ui.resultView.setModel( self.proxyModel ) self.completer = QtGui.QCompleter( self ) self.completerModel = QtGui.QStringListModel( self) self.ui.zoekTxt.setCompleter( self.completer ) self.completer.setModel( self.completerModel ) #eventhandlers self.ui.zoekBtn.clicked.connect(self.onZoekClicked) self.ui.addWMSbtn.clicked.connect(self.addWMS) self.ui.addWFSbtn.clicked.connect(self.addWFS) self.ui.DLbtn.clicked.connect(lambda: self.openUrl(self.dl)) self.ui.resultView.clicked.connect(self.resultViewClicked) self.ui.modelFilterCbx.currentIndexChanged.connect(self.modelFilterCbxIndexChanged) self.ui.filterWgt.setHidden(1) self.finished.connect(self.clean) def loadSettings(self): 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.md = metadata.MDReader( self.timeout, self.proxy, self.port ) def _setModel(self, records): self.model.clear() for rec in records: title = QtGui.QStandardItem( rec['title'] ) #0 wms = QtGui.QStandardItem( rec['wms'] ) #1 downloadLink = QtGui.QStandardItem(rec['download']) #2 id = QtGui.QStandardItem( rec['uuid'] ) #3 abstract = QtGui.QStandardItem( rec['abstract'] ) #4 wfs = QtGui.QStandardItem( rec['wfs'] ) #5 self.model.appendRow([title,wms,downloadLink,id,abstract,wfs]) #overwrite def show(self): QtGui.QDialog.show(self) self.setWindowModality(0) if self.firstShow: inet = internet_on( proxyUrl=self.proxy, port=self.port, timeout=self.timeout ) if inet: self.ui.GDIThemaCbx.addItems( ['']+ self.md.list_GDI_theme() ) self.ui.organisatiesCbx.addItems( ['']+ self.md.list_organisations() ) keywords = sorted( self.md.list_suggestionKeyword() ) self.completerModel.setStringList( keywords ) self.bronnen = self.md.list_bronnen() self.ui.bronCbx.addItems( ['']+ [ n[1] for n in self.bronnen] ) self.ui.typeCbx.addItems(['']+ [ n[0] for n in self.md.dataTypes]) self.ui.INSPIREannexCbx.addItems( ['']+ self.md.inspireannex ) self.ui.INSPIREserviceCbx.addItems( ['']+ self.md.inspireServiceTypes ) self.ui.INSPIREthemaCbx.addItems( ['']+ self.md.list_inspire_theme() ) 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) #eventhandlers def resultViewClicked(self): if self.ui.resultView.selectedIndexes(): row = self.ui.resultView.selectedIndexes()[0].row() title = self.proxyModel.data( self.proxyModel.index( row, 0) ) self.wms = self.proxyModel.data( self.proxyModel.index( row, 1) ) self.dl = self.proxyModel.data( self.proxyModel.index( row, 2) ) self.wfs = self.proxyModel.data( self.proxyModel.index( row, 5) ) uuid = self.proxyModel.data( self.proxyModel.index( row, 3) ) abstract = self.proxyModel.data( self.proxyModel.index( row, 4) ) self.ui.descriptionText.setText( """<h3>%s</h3><div>%s</div><br/><br/> <a href='https://metadata.geopunt.be/zoekdienst/apps/tabsearch/index.html?uuid=%s'> Ga naar fiche</a>""" % (title , abstract, uuid )) if self.wms: self.ui.addWMSbtn.setEnabled(1) else: self.ui.addWMSbtn.setEnabled(0) if self.wfs: self.ui.addWFSbtn.setEnabled(1) else: self.ui.addWFSbtn.setEnabled(0) if self.dl: self.ui.DLbtn.setEnabled(1) else: self.ui.DLbtn.setEnabled(0) def onZoekClicked(self): self.zoek = self.ui.zoekTxt.currentText() self.search() def modelFilterCbxIndexChanged(self): value = self.ui.modelFilterCbx.currentIndex() if value == 1: self.filterModel(1) elif value == 2: self.filterModel(5) elif value == 3: self.filterModel(2) else: self.filterModel() def filterModel(self, col=None): if col != None: self.proxyModel.setFilterKeyColumn(col) expr = QtCore.QRegExp("?*", QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard ) self.proxyModel.setFilterRegExp(expr) else: self.proxyModel.setFilterRegExp(None) def search(self): try: if self.ui.filterBox.isChecked(): themekey= self.ui.GDIThemaCbx.currentText() orgName= self.ui.organisatiesCbx.currentText() dataTypes= [ n[1] for n in self.md.dataTypes if n[0] == self.ui.typeCbx.currentText()] if dataTypes != []: dataType= dataTypes[0] else: dataType='' siteIds = [ n[0] for n in self.bronnen if n[1] == self.ui.bronCbx.currentText() ] if siteIds != []: siteId= siteIds[0] else: siteId ='' inspiretheme= self.ui.INSPIREthemaCbx.currentText() inspireannex= self.ui.INSPIREannexCbx.currentText() inspireServiceType= self.ui.INSPIREserviceCbx.currentText() searchResult = metadata.MDdata( self.md.searchAll( self.zoek, themekey, orgName, dataType, siteId, inspiretheme, inspireannex, inspireServiceType)) else: searchResult = metadata.MDdata( self.md.searchAll( self.zoek )) except: self.bar.pushMessage("Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=3) return self.ui.countLbl.setText( "Aantal gevonden: %s" % searchResult.count ) self.ui.descriptionText.setText('') self._setModel(searchResult.records) if searchResult.count == 0: self.bar.pushMessage( QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Er werden geen resultaten gevonde voor deze zoekopdracht"), duration=5) def openUrl(self, url): if url: webbrowser.open_new_tab( url.encode("utf-8") ) def addWMS(self): if self.wms == None: return crs = self.iface.mapCanvas().mapRenderer().destinationCrs().authid() if crs != 'EPSG:31370' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:31370' try: lyrs = metadata.getWmsLayerNames( self.wms , self.proxy, self.port) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WMS", QtCore.QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan geen lagen vinden in: %s" % self.wms ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, "WMS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle ][0] style = [n[2] for n in lyrs if n[1] == layerTitle ][0] url= self.wms.split('?')[0] if crs != 'EPSG:31370' or crs != 'EPSG:3857' : crs = 'EPSG:31370' wmsUrl = "url=%s&layers=%s&format=image/png&styles=%s&crs=%s" % (url, layerName, style , crs) try: rlayer = QgsRasterLayer(wmsUrl, layerTitle, 'wms') if rlayer.isValid(): rlayer.renderer().setOpacity(0.8) QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def addWFS(self): if self.wfs == None: return try: lyrs = metadata.getWFSLayerNames( self.wfs, self.proxy, self.port) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WFS", QtCore.QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan geen lagen vinden in: %s" % self.wfs ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, "WFS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle ][0] crs = [n[2] for n in lyrs if n[1] == layerTitle ][0] url = self.wfs.split('?')[0] wfsUri = metadata.makeWFSuri( url, layerName, crs ) try: vlayer = QgsVectorLayer( wfsUri, layerTitle , "WFS") QgsMapLayerRegistry.instance().addMapLayer(vlayer) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def clean(self): self.model.clear() self.wms = None self.wfs = None self.dl = None self.ui.zoekTxt.setCurrentIndex(0) self.ui.descriptionText.setText('') self.ui.countLbl.setText( "") self.ui.msgLbl.setText("" ) self.ui.DLbtn.setEnabled(0) self.ui.addWFSbtn.setEnabled(0) self.ui.addWMSbtn.setEnabled(0)
class LoadByClass(QtGui.QDialog, FORM_CLASS): def __init__(self, codeList, parent=None): """Constructor.""" super(LoadByClass, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.selectedClasses = [] self.bar = QgsMessageBar() self.setLayout(QtGui.QGridLayout(self)) self.layout().setContentsMargins(0,0,0,0) self.layout().setAlignment(QtCore.Qt.AlignTop) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.bar.setSizePolicy(sizePolicy) self.layout().addWidget(self.bar, 0,0,1,1) #Objects Connections QtCore.QObject.connect(self.widget, QtCore.SIGNAL(("connectionChanged()")), self.listClassesFromDatabase) QtCore.QObject.connect(self.widget, QtCore.SIGNAL(("problemOccurred()")), self.pushMessage) QtCore.QObject.connect(self.pushButtonCancel, QtCore.SIGNAL(("clicked()")), self.cancel) QtCore.QObject.connect(self.selectAllCheck, QtCore.SIGNAL(("stateChanged(int)")), self.selectAll) QtCore.QObject.connect(self.pushButtonOk, QtCore.SIGNAL(("clicked()")), self.okSelected) self.widget.tabWidget.currentChanged.connect(self.restoreInitialState) self.codeList = codeList self.layerFactory = LayerFactory() def restoreInitialState(self): self.selectedClasses = [] tam = self.classesListWidget.__len__() for i in range(tam+1,1,-1): item = self.classesListWidget.takeItem(i-2) self.selectAllCheck.setCheckState(0) def listClassesFromDatabase(self): self.classes = [] self.classesListWidget.clear() self.dbVersion = self.widget.getDBVersion() self.qmlPath = self.widget.getQmlPath() self.classes = self.widget.abstractDb.listGeomClassesFromDatabase() self.classesListWidget.addItems(self.classes) self.classesListWidget.sortItems() def on_filterEdit_textChanged(self, text): classes = [edgvClass for edgvClass in self.classes if text in edgvClass] self.classesListWidget.clear() self.classesListWidget.addItems(classes) self.classesListWidget.sortItems() def cancel(self): self.restoreInitialState() self.close() def pushMessage(self, msg): self.bar.pushMessage("", msg, level=QgsMessageBar.WARNING) def selectAll(self): if self.selectAllCheck.isChecked(): tam = self.classesListWidget.__len__() for i in range(tam+1): item = self.classesListWidget.item(i-1) self.classesListWidget.setItemSelected(item,2) else: tam = self.classesListWidget.__len__() for i in range(tam+1): item = self.classesListWidget.item(i-1) self.classesListWidget.setItemSelected(item,0) def getSelectedItems(self): lista = self.classesListWidget.selectedItems() self.selectedClasses = [] tam = len(lista) for i in range(tam): self.selectedClasses.append(lista[i].text()) self.selectedClasses.sort() def okSelected(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.loadLayers() QApplication.restoreOverrideCursor() def loadLayers(self): self.getSelectedItems() if len(self.selectedClasses)>0: try: for layer in self.selectedClasses: dbName = self.widget.abstractDb.getDatabaseName() groupList = qgis.utils.iface.legendInterface().groups() edgvLayer = self.layerFactory.makeLayer(self.widget.abstractDb, self.codeList, layer) if dbName in groupList: edgvLayer.load(self.widget.crs,groupList.index(dbName)) else: self.parentTreeNode = qgis.utils.iface.legendInterface().addGroup(self.widget.abstractDb.getDatabaseName(), -1) edgvLayer.load(self.widget.crs,self.parentTreeNode) self.restoreInitialState() self.close() except: self.bar.pushMessage(self.tr("Error!"), self.tr("Could not load the selected classes!"), level=QgsMessageBar.CRITICAL) else: self.bar.pushMessage(self.tr("Warning!"), self.tr("Please, select at least one class!"), level=QgsMessageBar.WARNING)
class GenerateProjectDialog(QDialog, DIALOG_UI): def __init__(self, iface, base_config, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.iface = iface self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) create_button = self.buttonBox.addButton(self.tr('Create'), QDialogButtonBox.AcceptRole) create_button.setDefault(True) self.ili_file_browse_button.clicked.connect( make_file_selector( self.ili_file_line_edit, title=self.tr('Open Interlis Model'), file_filter=self.tr('Interlis Model File (*.ili)'))) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.help_requested) self.crs = QgsCoordinateReferenceSystem() self.ili2db_options = Ili2dbOptionsDialog() self.ili2db_options_button.clicked.connect(self.ili2db_options.open) self.multiple_models_dialog = MultipleModelsDialog(self) self.multiple_models_button.clicked.connect( self.multiple_models_dialog.open) self.multiple_models_dialog.accepted.connect( self.fill_models_line_edit) self.type_combo_box.clear() self.type_combo_box.addItem(self.tr('Interlis (use PostGIS)'), 'ili2pg') self.type_combo_box.addItem(self.tr('Interlis (use GeoPackage)'), 'ili2gpkg') self.type_combo_box.addItem(self.tr('PostGIS'), 'pg') self.type_combo_box.addItem(self.tr('GeoPackage'), 'gpkg') self.type_combo_box.currentIndexChanged.connect(self.type_changed) self.txtStdout.anchorClicked.connect(self.link_activated) self.crsSelector.crsChanged.connect(self.crs_changed) self.base_configuration = base_config self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.txtStdout.setLayout(QGridLayout()) self.txtStdout.layout().setContentsMargins(0, 0, 0, 0) self.txtStdout.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.validators = Validators() nonEmptyValidator = NonEmptyStringValidator() fileValidator = FileValidator(pattern='*.ili', allow_empty=True) self.gpkgSaveFileValidator = FileValidator(pattern='*.gpkg', allow_non_existing=True) self.gpkgOpenFileValidator = FileValidator(pattern='*.gpkg') self.gpkg_file_line_edit.textChanged.connect( self.validators.validate_line_edits) self.restore_configuration() self.ili_models_line_edit.setValidator(nonEmptyValidator) self.pg_host_line_edit.setValidator(nonEmptyValidator) self.pg_database_line_edit.setValidator(nonEmptyValidator) self.pg_user_line_edit.setValidator(nonEmptyValidator) self.ili_file_line_edit.setValidator(fileValidator) self.pg_host_line_edit.textChanged.connect( self.validators.validate_line_edits) self.pg_host_line_edit.textChanged.emit(self.pg_host_line_edit.text()) self.pg_database_line_edit.textChanged.connect( self.validators.validate_line_edits) self.pg_database_line_edit.textChanged.emit( self.pg_database_line_edit.text()) self.pg_user_line_edit.textChanged.connect( self.validators.validate_line_edits) self.pg_user_line_edit.textChanged.emit(self.pg_user_line_edit.text()) self.ili_models_line_edit.textChanged.connect( self.validators.validate_line_edits) self.ili_models_line_edit.textChanged.emit( self.ili_models_line_edit.text()) self.ili_models_line_edit.textChanged.connect(self.on_model_changed) self.ilicache = IliCache(base_config) self.ilicache.models_changed.connect(self.update_models_completer) self.ilicache.new_message.connect(self.show_message) self.ilicache.refresh() self.ili_file_line_edit.textChanged.connect( self.validators.validate_line_edits) self.ili_file_line_edit.textChanged.connect(self.ili_file_changed) self.ili_file_line_edit.textChanged.emit( self.ili_file_line_edit.text()) def accepted(self): configuration = self.updated_configuration() if self.type_combo_box.currentData() in ['ili2pg', 'ili2gpkg']: if not self.ili_file_line_edit.text().strip(): if not self.ili_models_line_edit.text().strip(): self.txtStdout.setText( self. tr('Please set a valid INTERLIS model before creating the project.' )) self.ili_models_line_edit.setFocus() return if self.ili_file_line_edit.text().strip() and \ self.ili_file_line_edit.validator().validate(configuration.ilifile, 0)[0] != QValidator.Acceptable: self.txtStdout.setText( self. tr('Please set a valid INTERLIS file before creating the project.' )) self.ili_file_line_edit.setFocus() return if self.type_combo_box.currentData() in ['ili2pg', 'pg']: if not configuration.host: self.txtStdout.setText( self.tr('Please set a host before creating the project.')) self.pg_host_line_edit.setFocus() return if not configuration.database: self.txtStdout.setText( self.tr( 'Please set a database before creating the project.')) self.pg_database_line_edit.setFocus() return if not configuration.user: self.txtStdout.setText( self. tr('Please set a database user before creating the project.' )) self.pg_user_line_edit.setFocus() return elif self.type_combo_box.currentData() in ['ili2gpkg', 'gpkg']: if not configuration.dbfile or self.gpkg_file_line_edit.validator( ).validate(configuration.dbfile, 0)[0] != QValidator.Acceptable: self.txtStdout.setText( self. tr('Please set a valid database file before creating the project.' )) self.gpkg_file_line_edit.setFocus() return configuration.schema = configuration.schema or configuration.database self.save_configuration(configuration) with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.progress_bar.setValue(0) self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() if self.type_combo_box.currentData() in ['ili2pg', 'ili2gpkg']: importer = iliimporter.Importer() importer.tool_name = self.type_combo_box.currentData() importer.configuration = configuration importer.stdout.connect(self.print_info) importer.stderr.connect(self.on_stderr) importer.process_started.connect(self.on_process_started) importer.process_finished.connect(self.on_process_finished) try: if importer.run() != iliimporter.Importer.SUCCESS: self.enable() self.progress_bar.hide() return except JavaNotFoundError: self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText( self. tr('Java could not be found. Please <a href="https://java.com/en/download/">install Java</a> and or <a href="#configure">configure a custom java path</a>. We also support the JAVA_HOME environment variable in case you prefer this.' )) self.enable() self.progress_bar.hide() return try: generator = Generator(configuration.tool_name, configuration.uri, configuration.inheritance, configuration.schema) self.progress_bar.setValue(50) except OperationalError: self.txtStdout.setText( self. tr('There was an error connecting to the database. Check connection parameters.' )) self.enable() self.progress_bar.hide() return if self.type_combo_box.currentData() in ['pg', 'gpkg']: if not generator.db_or_schema_exists(): self.txtStdout.setText( self. tr('Source {} does not exist. Check connection parameters.' ).format('database' if self.type_combo_box. currentData() == 'gpkg' else 'schema')) self.enable() self.progress_bar.hide() return self.print_info( self.tr('\nObtaining available layers from the database...')) available_layers = generator.layers() if not available_layers: self.txtStdout.setText( self.tr('The {} has no layers to load into QGIS.').format( 'database' if self.type_combo_box.currentData() == 'gpkg' else 'schema')) self.enable() self.progress_bar.hide() return self.progress_bar.setValue(70) self.print_info( self.tr('Obtaining relations from the database...')) relations = generator.relations(available_layers) self.progress_bar.setValue(75) self.print_info(self.tr('Arranging layers into groups...')) legend = generator.legend(available_layers) self.progress_bar.setValue(85) project = Project() project.layers = available_layers project.relations = relations project.legend = legend self.print_info(self.tr('Configuring forms and widgets...')) project.post_generate() self.progress_bar.setValue(90) qgis_project = QgsProject.instance() self.print_info(self.tr('Generating QGIS project...')) project.create(None, qgis_project) self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) self.print_info(self.tr('\nDone!'), '#004905') def print_info(self, text, text_color='#000000'): self.txtStdout.setTextColor(QColor(text_color)) self.txtStdout.append(text) QCoreApplication.processEvents() def on_stderr(self, text): self.txtStdout.setTextColor(QColor('#2a2a2a')) self.txtStdout.append(text) self.advance_progress_bar_by_text(text) QCoreApplication.processEvents() def on_process_started(self, command): self.txtStdout.setText(command) self.progress_bar.setValue(10) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): if exit_code == 0: color = '#004905' message = self.tr( 'Interlis model(s) successfully imported into the database!') else: color = '#aa2222' message = self.tr('Finished with errors!') self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append(message) self.progress_bar.setValue(50) def updated_configuration(self): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ configuration = ImportConfiguration() if self.type_combo_box.currentData() in ['ili2pg', 'pg']: # PostgreSQL specific options configuration.tool_name = 'ili2pg' configuration.host = self.pg_host_line_edit.text().strip() configuration.port = self.pg_port_line_edit.text().strip() configuration.user = self.pg_user_line_edit.text().strip() configuration.database = self.pg_database_line_edit.text().strip() configuration.schema = self.pg_schema_line_edit.text().strip( ).lower() configuration.password = self.pg_password_line_edit.text() elif self.type_combo_box.currentData() in ['ili2gpkg', 'gpkg']: configuration.tool_name = 'ili2gpkg' configuration.dbfile = self.gpkg_file_line_edit.text().strip() configuration.epsg = self.epsg configuration.inheritance = self.ili2db_options.get_inheritance_type() configuration.base_configuration = self.base_configuration if self.ili_file_line_edit.text().strip(): configuration.ilifile = self.ili_file_line_edit.text().strip() if self.ili_models_line_edit.text().strip(): configuration.ilimodels = self.ili_models_line_edit.text().strip() return configuration def save_configuration(self, configuration): settings = QSettings() settings.setValue('QgsProjectGenerator/ili2db/ilifile', configuration.ilifile) settings.setValue('QgsProjectGenerator/ili2db/epsg', self.epsg) settings.setValue('QgsProjectGenerator/importtype', self.type_combo_box.currentData()) if self.type_combo_box.currentData() in ['ili2pg', 'pg']: # PostgreSQL specific options settings.setValue('QgsProjectGenerator/ili2pg/host', configuration.host) settings.setValue('QgsProjectGenerator/ili2pg/port', configuration.port) settings.setValue('QgsProjectGenerator/ili2pg/user', configuration.user) settings.setValue('QgsProjectGenerator/ili2pg/database', configuration.database) settings.setValue('QgsProjectGenerator/ili2pg/schema', configuration.schema) settings.setValue('QgsProjectGenerator/ili2pg/password', configuration.password) elif self.type_combo_box.currentData() in ['ili2gpkg', 'gpkg']: settings.setValue('QgsProjectGenerator/ili2gpkg/dbfile', configuration.dbfile) def restore_configuration(self): settings = QSettings() self.ili_file_line_edit.setText( settings.value('QgsProjectGenerator/ili2db/ilifile')) self.crs = QgsCoordinateReferenceSystem( settings.value('QgsProjectGenerator/ili2db/epsg', 21781, int)) self.update_crs_info() self.pg_host_line_edit.setText( settings.value('QgsProjectGenerator/ili2pg/host', 'localhost')) self.pg_port_line_edit.setText( settings.value('QgsProjectGenerator/ili2pg/port')) self.pg_user_line_edit.setText( settings.value('QgsProjectGenerator/ili2pg/user')) self.pg_database_line_edit.setText( settings.value('QgsProjectGenerator/ili2pg/database')) self.pg_schema_line_edit.setText( settings.value('QgsProjectGenerator/ili2pg/schema')) self.pg_password_line_edit.setText( settings.value('QgsProjectGenerator/ili2pg/password')) self.gpkg_file_line_edit.setText( settings.value('QgsProjectGenerator/ili2gpkg/dbfile')) self.type_combo_box.setCurrentIndex( self.type_combo_box.findData( settings.value('QgsProjectGenerator/importtype', 'pg'))) self.type_changed() self.crs_changed() def disable(self): self.pg_config.setEnabled(False) self.ili_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.pg_config.setEnabled(True) self.ili_config.setEnabled(True) self.buttonBox.setEnabled(True) def type_changed(self): self.progress_bar.hide() if self.type_combo_box.currentData() == 'ili2pg': self.ili_config.show() self.pg_config.show() self.gpkg_config.hide() self.pg_schema_line_edit.setPlaceholderText( self.tr("[Leave empty to create a default schema]")) elif self.type_combo_box.currentData() == 'pg': self.ili_config.hide() self.pg_config.show() self.gpkg_config.hide() self.pg_schema_line_edit.setPlaceholderText( self.tr("[Leave empty to load all schemas in the database]")) elif self.type_combo_box.currentData() == 'gpkg': self.ili_config.hide() self.pg_config.hide() self.gpkg_config.show() self.gpkg_file_line_edit.setValidator(self.gpkgOpenFileValidator) self.gpkg_file_line_edit.textChanged.emit( self.gpkg_file_line_edit.text()) try: self.gpkg_file_browse_button.clicked.disconnect() except: pass self.gpkg_file_browse_button.clicked.connect( make_file_selector( self.gpkg_file_line_edit, title=self.tr('Open GeoPackage database file'), file_filter=self.tr('GeoPackage Database (*.gpkg)'))) elif self.type_combo_box.currentData() == 'ili2gpkg': self.ili_config.show() self.pg_config.hide() self.gpkg_config.show() self.gpkg_file_line_edit.setValidator(self.gpkgSaveFileValidator) self.gpkg_file_line_edit.textChanged.emit( self.gpkg_file_line_edit.text()) try: self.gpkg_file_browse_button.clicked.disconnect() except: pass self.gpkg_file_browse_button.clicked.connect( make_save_file_selector( self.gpkg_file_line_edit, title=self.tr('Open GeoPackage database file'), file_filter=self.tr('GeoPackage Database (*.gpkg)'), extension='.gpkg')) def on_model_changed(self, text): for pattern, crs in CRS_PATTERNS.items(): if re.search(pattern, text): self.crs = QgsCoordinateReferenceSystem(crs) self.update_crs_info() break def link_activated(self, link): if link.url() == '#configure': cfg = OptionsDialog(self.base_configuration) if cfg.exec_(): settings = QSettings() settings.beginGroup('QgsProjectGenerator/ili2db') self.base_configuration.save(settings) else: QDesktopServices.openUrl(link) def update_crs_info(self): self.crsSelector.setCrs(self.crs) def crs_changed(self): if self.crsSelector.crs().authid()[:5] != 'EPSG:': self.crs_label.setStyleSheet('color: orange') self.crs_label.setToolTip( self.tr('Please select an EPSG Coordinate Reference System')) self.epsg = 21781 else: self.crs_label.setStyleSheet('') self.crs_label.setToolTip(self.tr('Coordinate Reference System')) authid = self.crsSelector.crs().authid() self.epsg = int(authid[5:]) def ili_file_changed(self): # If ili file is valid, models is optional if self.ili_file_line_edit.text().strip() and \ self.ili_file_line_edit.validator().validate(self.ili_file_line_edit.text().strip(), 0)[0] == QValidator.Acceptable: self.ili_models_line_edit.setValidator(None) self.ili_models_line_edit.textChanged.emit( self.ili_models_line_edit.text()) # Update completer to add models from given ili file self.iliFileCache = IliCache( self.base_configuration, self.ili_file_line_edit.text().strip()) self.iliFileCache.models_changed.connect( self.update_models_completer) self.iliFileCache.new_message.connect(self.show_message) self.iliFileCache.refresh() else: nonEmptyValidator = NonEmptyStringValidator() self.ili_models_line_edit.setValidator(nonEmptyValidator) self.ili_models_line_edit.textChanged.emit( self.ili_models_line_edit.text()) # Update completer to show repository models in models dir self.ilicache.models_changed.emit() def update_models_completer(self): completer = QCompleter(self.sender().model_names) completer.setCaseSensitivity(Qt.CaseInsensitive) self.ili_models_line_edit.setCompleter(completer) self.multiple_models_dialog.models_line_edit.setCompleter(completer) def show_message(self, level, message): if level == Qgis.Warning: self.bar.pushMessage(message, Qgis.Info, 10) elif level == Qgis.Critical: self.bar.pushMessage(message, Qgis.Warning, 10) def fill_models_line_edit(self): self.ili_models_line_edit.setText( self.multiple_models_dialog.get_models_string()) def help_requested(self): os_language = QLocale( QSettings().value('locale/userLocale')).name()[:2] if os_language in ['es', 'de']: webbrowser.open( "https://opengisch.github.io/projectgenerator/docs/{}/user-guide.html#generate-project" .format(os_language)) else: webbrowser.open( "https://opengisch.github.io/projectgenerator/docs/user-guide.html#generate-project" ) def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models...': self.progress_bar.setValue(20) elif text.strip() == 'Info: create table structure...': self.progress_bar.setValue(30)
class ModelerDialog(BASE, WIDGET): CANVAS_SIZE = 4000 def __init__(self, alg=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(1, self.bar) self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QSettings() self.splitter.restoreState( settings.value("/Processing/splitterModeler", QByteArray())) self.restoreGeometry( settings.value("/Processing/geometryModeler", QByteArray())) self.tabWidget.setCurrentIndex(0) self.scene = ModelerScene(self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = algList.getAlgorithm(text) if alg is not None: self._addAlgorithm(alg.getCopy(), event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) factor = 1.05 if event.angleDelta().y() > 0: factor = 1 / factor self.view.scale(factor, factor) self.repaintModel() def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): QGraphicsView.mousePressEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseReleaseEvent = _mouseReleaseEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.commandLineName()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) # Set icons self.btnOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.btnSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.btnSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.btnExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.btnExportPython.setIcon( QgsApplication.getThemeIcon('/console/iconSaveAsConsole.png')) self.btnEditHelp.setIcon( QIcon(os.path.join(pluginPath, 'images', 'edithelp.png'))) self.btnRun.setIcon( QIcon(os.path.join(pluginPath, 'images', 'runalgorithm.png'))) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText( self.tr('[Enter model name here]')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText( self.tr('[Enter group name here]')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.fillAlgorithmTree) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) self.btnOpen.clicked.connect(self.openModel) self.btnSave.clicked.connect(self.save) self.btnSaveAs.clicked.connect(self.saveAs) self.btnExportImage.clicked.connect(self.exportAsImage) self.btnExportPython.clicked.connect(self.exportAsPython) self.btnEditHelp.clicked.connect(self.editHelp) self.btnRun.clicked.connect(self.runModel) if alg is not None: self.alg = alg self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() else: self.alg = ModelerAlgorithm() self.alg.modelerdialog = self self.fillInputsTree() self.fillAlgorithmTree() self.view.centerOn(0, 0) self.alg.setModelerView(self) self.help = None # Indicates whether to update or not the toolbox after # closing this dialog self.update = False self.hasChanged = False def closeEvent(self, evt): settings = QSettings() settings.setValue("/Processing/splitterModeler", self.splitter.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Unsaved changes'), self.tr('There are unsaved changes in model. Continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if ret == QMessageBox.Yes: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = algList.getProviderFromName('model') alg = self.alg.getCopy() dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.alg.helpContent = dlg.descriptions self.hasChanged = True def runModel(self): if len(self.alg.algs) == 0: QMessageBox.warning( self, self.tr('Empty model'), self.tr("Model doesn't contains any algorithms and/or " "parameters and can't be executed")) return if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = algList.getProviderFromName('model') alg = self.alg.getCopy() dlg = AlgorithmDialog(alg) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def exportAsImage(self): filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = QRectF(0, 0, 1, 1) for item in list(self.scene.items()): totalRect = totalRect.united(item.sceneBoundingRect()) totalRect.adjust(-10, -10, 10, 10) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, totalRect, totalRect) painter.end() img.save(filename) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.alg.toPython() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) QMessageBox.information(self, self.tr('Model exported'), self.tr('Model was correctly exported.')) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving')) return self.alg.name = str(self.textName.text()) self.alg.group = str(self.textGroup.text()) if self.alg.descriptionFile is not None and not saveAs: filename = self.alg.descriptionFile else: filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model)')) if filename: if not filename.endswith('.model'): filename += '.model' self.alg.descriptionFile = filename if filename: text = self.alg.toJson() try: fout = codecs.open(filename, 'w', encoding='utf-8') except: if saveAs: QMessageBox.warning( self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n %s') % str(sys.exc_info()[1])) else: QMessageBox.warning( self, self.tr("Can't save model"), self.tr("This model can't be saved in its " "original location (probably you do not " "have permission to do it). Please, use " "the 'Save as...' option.")) return fout.write(text) fout.close() self.update = True self.bar.pushMessage("", "Model was correctly saved", level=QgsMessageBar.SUCCESS, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = str( QFileDialog.getOpenFileName( self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model *.MODEL)'))) if filename: try: alg = ModelerAlgorithm.fromFile(filename) self.alg = alg self.alg.setModelerView(self) self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False except WrongModelException as e: ProcessingLog.addToLog( ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.msg)) QMessageBox.critical( self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) except Exception as e: ProcessingLog.addToLog( ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.args[0])) QMessageBox.critical( self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self): self.scene = ModelerScene() self.scene.setSceneRect( QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE, ModelerAlgorithm.CANVAS_SIZE)) self.scene.paintModel(self.alg) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.alg, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) self.alg.addParameter(ModelerParameter(dlg.param, pos)) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.inputs: maxX = max([i.pos.x() for i in list(self.alg.inputs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.png')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in ModelerParameterDefinitionDialog.paramTypes: paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = algList.getAlgorithm(item.alg.commandLineName()) self._addAlgorithm(alg.getCopy()) def _addAlgorithm(self, alg, pos=None): dlg = alg.getCustomModelerParametersDialog(self.alg) if not dlg: dlg = ModelerParametersDialog(alg, self.alg) dlg.exec_() if dlg.alg is not None: if pos is None: dlg.alg.pos = self.getPositionForAlgorithmItem() else: dlg.alg.pos = pos if isinstance(dlg.alg.pos, QPoint): dlg.alg.pos = QPointF(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(dlg.alg.outputs): dlg.alg.outputs[out].pos = dlg.alg.pos + QPointF( ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT) self.alg.addAlgorithm(dlg.alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.algs: maxX = max([alg.pos.x() for alg in list(self.alg.algs.values())]) maxY = max([alg.pos.y() for alg in list(self.alg.algs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillAlgorithmTree(self): self.fillAlgorithmTreeUsingProviders() self.algorithmTree.sortItems(0, Qt.AscendingOrder) text = str(self.searchBox.text()) if text != '': self.algorithmTree.expandAll() def fillAlgorithmTreeUsingProviders(self): self.algorithmTree.clear() text = str(self.searchBox.text()) allAlgs = algList.algs for providerName in list(allAlgs.keys()): name = 'ACTIVATE_' + providerName.upper().replace(' ', '_') if not ProcessingConfig.getSetting(name): continue groups = {} algs = list(allAlgs[providerName].values()) # Add algorithms for alg in algs: if not alg.showInModeler: continue if alg.commandLineName() == self.alg.commandLineName(): continue if text == '' or text.lower() in alg.name.lower(): if alg.group in groups: groupItem = groups[alg.group] else: groupItem = QTreeWidgetItem() name = alg.i18n_group or alg.group groupItem.setText(0, name) groupItem.setToolTip(0, name) groups[alg.group] = groupItem algItem = TreeAlgorithmItem(alg) groupItem.addChild(algItem) if len(groups) > 0: providerItem = QTreeWidgetItem() provider = algList.getProviderFromName(providerName) providerItem.setText(0, provider.getDescription()) providerItem.setToolTip(0, provider.getDescription()) providerItem.setIcon(0, provider.getIcon()) for groupItem in list(groups.values()): providerItem.addChild(groupItem) self.algorithmTree.addTopLevelItem(providerItem) providerItem.setExpanded(text != '') for groupItem in list(groups.values()): if text != '': groupItem.setExpanded(True) self.algorithmTree.sortItems(0, Qt.AscendingOrder)
class ShellOutputScintilla(QgsCodeEditorPython): def __init__(self, parent=None): super().__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.setContext(Qt.WidgetWithChildrenShortcut) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.setContext(Qt.WidgetWithChildrenShortcut) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console\n" "Use iface to access QGIS API interface or type help(iface) for more info\n" "Security warning: typing commands from an untrusted source can harm your computer") txtInit = '\n'.join(['# ' + line for line in txtInit.split('\n')]) # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def initializeLexer(self): super().initializeLexer() self.setFoldingVisible(False) self.setEdgeMode(QsciScintilla.EdgeNone) def refreshSettingsOutput(self): # Set Python lexer self.initializeLexer() self.setReadOnly(True) self.setCaretWidth(0) # NO (blinking) caret in the output def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) menu.addAction(QgsApplication.getThemeIcon("console/iconHideToolConsole.svg"), QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg"), QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(QgsApplication.getThemeIcon("console/mIconRunConsole.svg"), QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(QgsApplication.getThemeIcon("console/iconClearConsole.svg"), QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) pyQGISHelpAction = menu.addAction(QgsApplication.getThemeIcon("console/iconHelpConsole.svg"), QCoreApplication.translate("PythonConsole", "Search Selected in PyQGIS docs"), self.searchSelectedTextInPyQGISDocs) menu.addSeparator() copyAction = menu.addAction( QgsApplication.getThemeIcon("mActionEditCopy.svg"), QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"), QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) pyQGISHelpAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) pyQGISHelpAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
class CobolBaseDialog(QDialog, DIALOG_LOG_EXCEL_UI): on_result = pyqtSignal(bool) # whether the tool was run successfully or not def __init__(self, qgis_utils, db, conn_manager, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.qgis_utils = qgis_utils self._db = db self.conn_manager = conn_manager self.parent = parent self.logger = Logger() self._dialog_mode = None self._running_tool = False self._etl_result = False self.tool_name = "" self.names = self._db.names self._db_was_changed = False # To postpone calling refresh gui until we close this dialog instead of settings self.validators = Validators() self.initialize_feedback() self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.button(QDialogButtonBox.Ok).setText(QCoreApplication.translate("CobolBaseDialog", "Import")) self.finished.connect(self.finished_slot) self._layers = dict() self.initialize_layers() self.btn_browse_file_blo.clicked.connect( make_file_selector(self.txt_file_path_blo, QCoreApplication.translate("CobolBaseDialog", "Select the BLO .lis file with Cobol data "), QCoreApplication.translate("CobolBaseDialog", 'lis File (*.lis)'))) self.btn_browse_file_uni.clicked.connect( make_file_selector(self.txt_file_path_uni, QCoreApplication.translate("CobolBaseDialog", "Select the UNI .lis file with Cobol data "), QCoreApplication.translate("CobolBaseDialog", 'lis File (*.lis)'))) self.btn_browse_file_ter.clicked.connect( make_file_selector(self.txt_file_path_ter, QCoreApplication.translate("CobolBaseDialog", "Select the TER .lis file with Cobol data "), QCoreApplication.translate("CobolBaseDialog", 'lis File (*.lis)'))) self.btn_browse_file_pro.clicked.connect( make_file_selector(self.txt_file_path_pro, QCoreApplication.translate("CobolBaseDialog", "Select the PRO .lis file with Cobol data "), QCoreApplication.translate("CobolBaseDialog", 'lis File (*.lis)'))) self.btn_browse_file_gdb.clicked.connect( make_folder_selector(self.txt_file_path_gdb, title=QCoreApplication.translate( 'CobolBaseDialog', 'Open GDB folder'), parent=None)) file_validator_blo = FileValidator(pattern='*.lis', allow_empty=True) file_validator_lis = FileValidator(pattern='*.lis', allow_non_existing=False) dir_validator_gdb = DirValidator(pattern='*.gdb', allow_non_existing=False) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.txt_file_path_blo.setValidator(file_validator_blo) self.txt_file_path_uni.setValidator(file_validator_lis) self.txt_file_path_ter.setValidator(file_validator_lis) self.txt_file_path_pro.setValidator(file_validator_lis) self.txt_file_path_gdb.setValidator(dir_validator_gdb) self.txt_file_path_blo.textChanged.connect(self.validators.validate_line_edits) self.txt_file_path_uni.textChanged.connect(self.validators.validate_line_edits) self.txt_file_path_ter.textChanged.connect(self.validators.validate_line_edits) self.txt_file_path_pro.textChanged.connect(self.validators.validate_line_edits) self.txt_file_path_gdb.textChanged.connect(self.validators.validate_line_edits) self.txt_file_path_blo.textChanged.connect(self.input_data_changed) self.txt_file_path_uni.textChanged.connect(self.input_data_changed) self.txt_file_path_ter.textChanged.connect(self.input_data_changed) self.txt_file_path_pro.textChanged.connect(self.input_data_changed) self.txt_file_path_gdb.textChanged.connect(self.input_data_changed) 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 initialize_layers(self): self._layers = { self.names.GC_PARCEL_T: {'name': self.names.GC_PARCEL_T, 'geometry': None, LAYER: None}, self.names.GC_OWNER_T: {'name': self.names.GC_OWNER_T, 'geometry': None, LAYER: None}, self.names.GC_ADDRESS_T: {'name': self.names.GC_ADDRESS_T, 'geometry': QgsWkbTypes.LineGeometry, LAYER: None}, self.names.GC_BUILDING_UNIT_T: {'name': self.names.GC_BUILDING_UNIT_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_BUILDING_T: {'name': self.names.GC_BUILDING_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_PLOT_T: {'name': self.names.GC_PLOT_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_RURAL_DIVISION_T: {'name': self.names.GC_RURAL_DIVISION_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_URBAN_SECTOR_T: {'name': self.names.GC_URBAN_SECTOR_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_RURAL_SECTOR_T: {'name': self.names.GC_RURAL_SECTOR_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_PERIMETER_T: {'name': self.names.GC_PERIMETER_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_BLOCK_T: {'name': self.names.GC_BLOCK_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_NEIGHBOURHOOD_T: {'name': self.names.GC_NEIGHBOURHOOD_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_COMMISSION_BUILDING_T: {'name': self.names.GC_COMMISSION_BUILDING_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_COMMISSION_PLOT_T: {'name': self.names.GC_COMMISSION_PLOT_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, self.names.GC_COMMISSION_BUILDING_UNIT_T: {'name': self.names.GC_COMMISSION_BUILDING_UNIT_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None}, } def reject(self): if self._running_tool: reply = QMessageBox.question(self, QCoreApplication.translate("CobolBaseDialog", "Warning"), QCoreApplication.translate("CobolBaseDialog", "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("CobolBaseDialog", "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.done(1) def finished_slot(self, result): self.bar.clearWidgets() def input_data_changed(self): self.set_import_button_enabled(self.validate_inputs()) def set_import_button_enabled(self, enable): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable) def validate_inputs(self): raise NotImplementedError def validate_common_inputs(self): """ :return: Boolean """ return self.txt_file_path_uni.validator().validate(self.txt_file_path_uni.text().strip(), 0)[0] == QValidator.Acceptable and \ self.txt_file_path_gdb.validator().validate(self.txt_file_path_gdb.text().strip(), 0)[0] == QValidator.Acceptable 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 set_gui_controls_enabled(self, enable): self.gbx_data_source.setEnabled(enable) self.target_data.setEnabled(enable) if self.buttonBox.button(QDialogButtonBox.Ok) is not None: # It's None if the tool finished successfully self.set_import_button_enabled(enable) 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 save_settings(self): settings = QSettings() settings.setValue('Asistente-LADM_COL/etl_cobol/blo_path', self.txt_file_path_blo.text()) settings.setValue('Asistente-LADM_COL/etl_cobol/uni_path', self.txt_file_path_uni.text()) settings.setValue('Asistente-LADM_COL/etl_cobol/ter_path', self.txt_file_path_ter.text()) settings.setValue('Asistente-LADM_COL/etl_cobol/pro_path', self.txt_file_path_pro.text()) settings.setValue('Asistente-LADM_COL/etl_cobol/gdb_path', self.txt_file_path_gdb.text()) def restore_settings(self): settings = QSettings() self.txt_file_path_blo.setText(settings.value('Asistente-LADM_COL/etl_cobol/blo_path', '')) self.txt_file_path_uni.setText(settings.value('Asistente-LADM_COL/etl_cobol/uni_path', '')) self.txt_file_path_ter.setText(settings.value('Asistente-LADM_COL/etl_cobol/ter_path', '')) self.txt_file_path_pro.setText(settings.value('Asistente-LADM_COL/etl_cobol/pro_path', '')) self.txt_file_path_gdb.setText(settings.value('Asistente-LADM_COL/etl_cobol/gdb_path', '')) def load_lis_files(self, lis_paths): self.lis_paths = lis_paths root = QgsProject.instance().layerTreeRoot() lis_group = root.addGroup(QCoreApplication.translate("CobolBaseDialog", "LIS Supplies")) for name in self.lis_paths: uri = 'file:///{}?type=csv&delimiter=;&detectTypes=yes&geomType=none&subsetIndex=no&watchFile=no'.format(self.lis_paths[name]) layer = QgsVectorLayer(uri, name, 'delimitedtext') if layer.isValid(): self.lis_paths[name] = layer QgsProject.instance().addMapLayer(layer, False) lis_group.addLayer(layer) else: if name == 'blo': # BLO is kind of optional, if it is not given, we pass a default one uri = 'file:///{}?type=csv&delimiter=;&detectTypes=yes&geomType=none&subsetIndex=no&watchFile=no'.format(BLO_LIS_FILE_PATH) layer = QgsVectorLayer(uri, name, 'delimitedtext') self.lis_paths[name] = layer QgsProject.instance().addMapLayer(layer, False) lis_group.addLayer(layer) else: return False, QCoreApplication.translate("CobolBaseDialog", "There were troubles loading the LIS file called '{}'.".format(name)) return True, '' def load_gdb_files(self, required_layers): self.gdb_paths = {} gdb_path = self.txt_file_path_gdb.text() layer = QgsVectorLayer(gdb_path, 'layer name', 'ogr') if not layer.isValid(): return False, QCoreApplication.translate("CobolBaseDialog", "There were troubles loading the GDB.") sublayers = layer.dataProvider().subLayers() root = QgsProject.instance().layerTreeRoot() gdb_group = root.addGroup(QCoreApplication.translate("CobolBaseDialog", "GDB Supplies")) for data in sublayers: sublayer = data.split('!!::!!')[1] if sublayer in required_layers: layer = QgsVectorLayer(gdb_path + '|layername=' + sublayer, sublayer, 'ogr') self.gdb_paths[sublayer] = layer QgsProject.instance().addMapLayer(layer, False) gdb_group.addLayer(layer) if len(self.gdb_paths) != len(required_layers): return False, QCoreApplication.translate("CobolBaseDialog", "The GDB does not have the required layers!") return True, '' def load_model_layers(self): self.qgis_utils.get_layers(self._db, self._layers, load=True) if not self._layers: return False, QCoreApplication.translate("CobolBaseDialog", "There was a problem loading layers from the 'Supplies' model!") return True, '' def show_message(self, message, level): self.bar.clearWidgets() # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 15)
class ExtentDialog(BASE, WIDGET): # None - don't do anything (normal) # ExtentDialog.TEST_CASE_OVERRIDE = {"radioRestrictedExtent":True, "radioFullExtent":False, "Extent":[xmin,ymin,xmax,ymax]} # ExtentDialog.TEST_CASE_OVERRIDE = {"radioRestrictedExtent":False, "radioFullExtent":False, "Extent":None} TEST_CASE_OVERRIDE = None def __init__(self, parent=None): super(ExtentDialog, self).__init__(parent) self.setupUi(self) self.buttonCanvasExtent.clicked.connect(self.useCanvasExtent) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.radioFullExtent.toggled.connect(self.toggled) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.extent = None self.ok = False if ExtentDialog.TEST_CASE_OVERRIDE is not None: self.radioRestrictedExtent.setChecked( ExtentDialog.TEST_CASE_OVERRIDE["radioRestrictedExtent"]) self.radioFullExtent.setChecked( ExtentDialog.TEST_CASE_OVERRIDE["radioFullExtent"]) if ExtentDialog.TEST_CASE_OVERRIDE["extent"] is not None: self.textXMin.setText( str(ExtentDialog.TEST_CASE_OVERRIDE["extent"][0])) self.textYMin.setText( str(ExtentDialog.TEST_CASE_OVERRIDE["extent"][1])) self.textXMax.setText( str(ExtentDialog.TEST_CASE_OVERRIDE["extent"][2])) self.textYMax.setText( str(ExtentDialog.TEST_CASE_OVERRIDE["extent"][3])) self.accept() def exec_(self): if ExtentDialog.TEST_CASE_OVERRIDE is None: super(ExtentDialog, self).exec_() def toggled(self): checked = self.radioRestrictedExtent.isChecked() self.widgetRestrictedExtent.setEnabled(checked) def useCanvasExtent(self): self.radioRestrictedExtent.setChecked(True) self.radioFullExtent.setChecked(False) self.setExtent(iface.mapCanvas().extent()) def setExtent(self, extent): self.textXMin.setText(str(extent.xMinimum())) self.textYMin.setText(str(extent.yMinimum())) self.textXMax.setText(str(extent.xMaximum())) self.textYMax.setText(str(extent.yMaximum())) # iface.mapCanvas().setMapTool(self.prevMapTool) def accept(self): def _check(w): try: v = w.text() return float(v) except ValueError: raise Exception("Wrong Value: " + v) if self.radioFullExtent.isChecked(): self.extent = None self.ok = True else: widgets = [ self.textXMin, self.textYMin, self.textXMax, self.textYMax ] try: self.extent = [_check(widget) for widget in widgets] self.ok = True except Exception as e: self.extent = None self.bar.pushMessage("Error", str(e), level=Qgis.Warning) return self.close()
class MissingSuppliesBaseDialog(QDialog, DIALOG_LOG_EXCEL_UI): on_result = pyqtSignal( bool) # whether the tool was run successfully or not def __init__(self, db, conn_manager, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self._db = db self.conn_manager = conn_manager self.parent = parent self.logger = Logger() self.app = AppInterface() self.validators = Validators() self.names = self._db.names self._dialog_mode = None self._running_tool = False self._db_was_changed = False # To postpone calling refresh gui until we close this dialog instead of settings self.tool_name = "" self.gdb_layer_paths = dict() self.alphanumeric_file_paths = dict() self.spreadsheet_structure = 'TABLA_RESUMEN OMISIONES_TERRENO COMISIONES_TERRENO OMISIONES_MEJORAS COMISIONES_MEJORAS OMISIONES_UNID_PH COMISIONES_UNID_PH OMISIONES_MZ COMISIONES_MZ OMISIONES_VR COMISIONES_VR' # Initialize self.initialize_feedback() # Set MessageBar for QDialog self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) # Set connections self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.button(QDialogButtonBox.Ok).setText( QCoreApplication.translate("MissingSuppliesBaseDialog", "Import")) self.finished.connect(self.finished_slot) self.btn_browse_file_predio.clicked.connect( make_file_selector( self.txt_file_path_predio, QCoreApplication.translate( "MissingSuppliesBaseDialog", "Select the Predio .csv file with SNC data "), QCoreApplication.translate("MissingSuppliesBaseDialog", 'CSV File (*.csv)'))) self.btn_browse_file_uni.clicked.connect( make_file_selector( self.txt_file_path_uni, QCoreApplication.translate( "MissingSuppliesBaseDialog", "Select the UNI .lis file with Cobol data "), QCoreApplication.translate("MissingSuppliesBaseDialog", 'lis File (*.lis)'))) self.btn_browse_file_gdb.clicked.connect( make_folder_selector(self.txt_file_path_gdb, title=QCoreApplication.translate( 'MissingSuppliesBaseDialog', 'Open GDB folder'), parent=None)) self.btn_browse_file_folder_supplies.clicked.connect( make_folder_selector(self.txt_file_path_folder_supplies, title=QCoreApplication.translate( "MissingCobolSuppliesDialog", "Select folder to save data"), parent=None)) # Set validations file_validator_predio = FileValidator(pattern='*.csv', allow_non_existing=False) file_validator_lis = FileValidator(pattern='*.lis', allow_non_existing=False) dir_validator_gdb = DirValidator(pattern='*.gdb', allow_non_existing=False) dir_validator_folder = DirValidator(pattern=None, allow_empty_dir=True) non_empty_validator_name = NonEmptyStringValidator() self.txt_file_path_predio.setValidator(file_validator_predio) self.txt_file_path_uni.setValidator(file_validator_lis) self.txt_file_path_gdb.setValidator(dir_validator_gdb) self.txt_file_path_folder_supplies.setValidator(dir_validator_folder) self.txt_file_names_supplies.setValidator(non_empty_validator_name) self.txt_file_path_predio.textChanged.connect( self.validators.validate_line_edits) self.txt_file_path_uni.textChanged.connect( self.validators.validate_line_edits) self.txt_file_path_gdb.textChanged.connect( self.validators.validate_line_edits) self.txt_file_path_folder_supplies.textChanged.connect( self.validators.validate_line_edits) self.txt_file_names_supplies.textChanged.connect( self.validators.validate_line_edits) self.txt_file_path_predio.textChanged.connect(self.input_data_changed) self.txt_file_path_uni.textChanged.connect(self.input_data_changed) self.txt_file_path_gdb.textChanged.connect(self.input_data_changed) self.txt_file_path_folder_supplies.textChanged.connect( self.input_data_changed) self.txt_file_names_supplies.textChanged.connect( self.input_data_changed) 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 reject(self): if self._running_tool: reply = QMessageBox.question( self, QCoreApplication.translate("MissingSuppliesBaseDialog", "Warning"), QCoreApplication.translate( "MissingSuppliesBaseDialog", "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( "MissingSuppliesBaseDialog", "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.done(1) def finished_slot(self, result): self.bar.clearWidgets() def input_data_changed(self): self.set_import_button_enabled(self.validate_inputs()) def set_import_button_enabled(self, enable): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable) def validate_inputs(self): raise NotImplementedError def validate_common_inputs(self): return self.txt_file_path_gdb.validator().validate( self.txt_file_path_gdb.text().strip(), 0)[0] == QValidator.Acceptable 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 set_gui_controls_enabled(self, enable): self.gbx_data_source.setEnabled(enable) if self.buttonBox.button( QDialogButtonBox.Ok ) is not None: # It's None if the tool finished successfully self.set_import_button_enabled(enable) 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 save_settings(self, system): settings = QSettings() settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/predio_path'.format( system), self.txt_file_path_predio.text()) settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/uni_path'.format(system), self.txt_file_path_uni.text()) settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/gdb_path'.format(system), self.txt_file_path_gdb.text()) settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/folder_path'.format( system), self.txt_file_path_folder_supplies.text()) settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/file_names'.format(system), self.txt_file_names_supplies.text()) def restore_settings(self, system): settings = QSettings() self.txt_file_path_predio.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/predio_path'.format( system), '')) self.txt_file_path_uni.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/uni_path'.format( system), '')) self.txt_file_path_gdb.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/gdb_path'.format( system), '')) self.txt_file_path_folder_supplies.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/folder_path'.format( system), '')) self.txt_file_names_supplies.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/file_names'.format( system), '')) def load_lis_files(self, alphanumeric_file_paths): root = QgsProject.instance().layerTreeRoot() alphanumeric_group = root.addGroup( QCoreApplication.translate("MissingSuppliesBaseDialog", "LIS Supplies")) for name in alphanumeric_file_paths: uri = 'file:///{}?type=csv&delimiter=;&detectTypes=yes&geomType=none&subsetIndex=no&watchFile=no'.format( normalize_local_url(alphanumeric_file_paths[name])) layer = QgsVectorLayer(uri, name, 'delimitedtext') if layer.isValid(): self.alphanumeric_file_paths[name] = layer QgsProject.instance().addMapLayer(layer, False) alphanumeric_group.addLayer(layer) else: return False, QCoreApplication.translate( "MissingSuppliesBaseDialog", "There were troubles loading the LIS file called '{}'.". format(name)) return True, '' def load_csv_files(self, alphanumeric_file_paths): root = QgsProject.instance().layerTreeRoot() alphanumeric_group = root.addGroup( QCoreApplication.translate("MissingSuppliesBaseDialog", "SNC Alphanumeric Supplies")) for name in alphanumeric_file_paths: layer = QgsVectorLayer(alphanumeric_file_paths[name], name, 'ogr') if layer.isValid(): self.alphanumeric_file_paths[name] = layer QgsProject.instance().addMapLayer(layer, False) alphanumeric_group.addLayer(layer) else: return False, QCoreApplication.translate( "MissingSuppliesBaseDialog", "There were troubles loading the CSV file called '{}'.". format(name)) return True, '' def load_gdb_files(self, required_layers): gdb_path = self.txt_file_path_gdb.text() layer = QgsVectorLayer(gdb_path, 'layer name', 'ogr') if not layer.isValid(): return False, QCoreApplication.translate( "MissingSuppliesBaseDialog", "There were troubles loading the GDB.") sublayers = layer.dataProvider().subLayers() root = QgsProject.instance().layerTreeRoot() gdb_group = root.addGroup( QCoreApplication.translate("MissingSuppliesBaseDialog", "GDB Supplies")) for data in sublayers: sublayer = data.split('!!::!!')[1] if sublayer in required_layers: layer = QgsVectorLayer(gdb_path + '|layername=' + sublayer, sublayer, 'ogr') self.gdb_layer_paths[sublayer] = layer QgsProject.instance().addMapLayer(layer, False) gdb_group.addLayer(layer) if len(self.gdb_layer_paths) != len(required_layers): return False, QCoreApplication.translate( "MissingSuppliesBaseDialog", "The GDB does not have the required layers!") return True, '' def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 15)
class CreatePointsCadastreWizard(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.help_strings = HelpStrings() 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())} # Set connections self.btn_browse_file.clicked.connect( make_file_selector(self.txt_file_path, file_filter=QCoreApplication.translate("CreatePointsCadastreWizard",'CSV File (*.csv *.txt)'))) self.txt_file_path.textChanged.connect(self.file_path_changed) self.crsSelector.crsChanged.connect(self.crs_changed) self.crs = QgsCoordinateReferenceSystem() self.txt_delimiter.textChanged.connect(self.fill_long_lat_combos) 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.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.PointLayer) self.txt_help_page_2.setHtml(self.help_strings.WIZ_ADD_POINTS_CADASTRE_PAGE_2_OPTION_CSV) self.wizardPage2.setButtonText(QWizard.FinishButton, QCoreApplication.translate("CreatePointsCadastreWizard", "Import")) self.txt_help_page_3.setHtml(self.help_strings.WIZ_ADD_POINTS_CADASTRE_PAGE_3_OPTION_CSV) self.txt_help_page_3.anchorClicked.connect(self.save_template) self.button(QWizard.HelpButton).clicked.connect(self.show_help) # 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.qgis_utils.get_layer(self._db, self.current_point_name(), load=True) if not QgsWkbTypes().hasZ(self.target_layer.wkbType()): self.labelZ.setEnabled(False) self.cbo_elevation.setEnabled(False) msg = QCoreApplication.translate("CreatePointsCadastreWizard", "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): if self.rad_refactor.isChecked(): self.lbl_refactor_source.setEnabled(True) self.mMapLayerComboBox.setEnabled(True) disable_next_wizard(self) self.wizardPage2.setFinalPage(True) self.txt_help_page_2.setHtml( self.help_strings.get_refactor_help_string( self.current_point_name(), True)) elif self.rad_csv.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) enable_next_wizard(self) self.wizardPage2.setFinalPage(False) self.txt_help_page_2.setHtml(self.help_strings.WIZ_ADD_POINTS_CADASTRE_PAGE_2_OPTION_CSV) def point_option_changed(self): if self.rad_boundary_point.isChecked(): self.gbx_page_2.setTitle(QCoreApplication.translate("CreatePointsCadastreWizard", "Load data to Boundary Points...")) self.gbx_page_3.setTitle(QCoreApplication.translate("CreatePointsCadastreWizard", "Configure CSV data source for Boundary Points...")) self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_CADASTRE_PAGE_1_OPTION_BP) elif self.rad_survey_point.isChecked(): # self.rad_survey_point is checked self.gbx_page_2.setTitle(QCoreApplication.translate("CreatePointsCadastreWizard", "Load data to Survey Points...")) self.gbx_page_3.setTitle(QCoreApplication.translate("CreatePointsCadastreWizard", "Configure CSV data source for Survey Points...")) self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_CADASTRE_PAGE_1_OPTION_SP) else: # self.rad_control_point is checked self.gbx_page_2.setTitle(QCoreApplication.translate("CreatePointsCadastreWizard", "Load data to Control Points...")) self.gbx_page_3.setTitle(QCoreApplication.translate("CreatePointsCadastreWizard", "Configure CSV data source for Control Points...")) self.txt_help_page_1.setHtml(self.help_strings.WIZ_ADD_POINTS_CADASTRE_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: self.qgis_utils.show_etl_model(self._db, self.mMapLayerComboBox.currentLayer(), output_layer_name) else: self.iface.messageBar().pushMessage("Asistente LADM_COL", QCoreApplication.translate("CreatePointsCadastreWizard", "Select a source layer to set the field mapping to '{}'.").format(output_layer_name), Qgis.Warning) elif self.rad_csv.isChecked(): automatic_fields_definition = self.qgis_utils.check_if_and_disable_automatic_fields(self._db, self.current_point_name()) self.prepare_copy_csv_points_to_db() self.qgis_utils.check_if_and_enable_automatic_fields(self._db, automatic_fields_definition, self.current_point_name()) def current_point_name(self): if self.rad_boundary_point.isChecked(): return BOUNDARY_POINT_TABLE elif self.rad_survey_point.isChecked(): return SURVEY_POINT_TABLE else: return CONTROL_POINT_TABLE 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.iface.messageBar().pushMessage("Asistente LADM_COL", QCoreApplication.translate("CreatePointsCadastreWizard", "No CSV file given or file doesn't exist."), Qgis.Warning) return target_layer = self.current_point_name() res = self.qgis_utils.copy_csv_to_db(csv_path, self.txt_delimiter.text(), self.cbo_longitude.currentText(), self.cbo_latitude.currentText(), self._db, self.epsg, target_layer, self.cbo_elevation.currentText() or None) def file_path_changed(self): self.autodetect_separator() self.fill_long_lat_combos("") self.cbo_delimiter.currentTextChanged.connect(self.separator_changed) 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(self.crs) def crs_changed(self): authid = self.crsSelector.crs().authid() self.epsg = int(authid[5:]) 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 [] errorReading = False try: reader = open(csv_path, "r") except IOError: errorReading = True line = reader.readline().replace("\n", "") reader.close() if not line: errorReading = True else: return line.split(self.txt_delimiter.text()) if errorReading: self.iface.messageBar().pushMessage("Asistente LADM_COL", QCoreApplication.translate("CreatePointsCadastreWizard", "It was not possible to read field names from the CSV. Check the file and try again."), Qgis.Warning) 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('CreatePointsCadastreWizard', 'Save File'), os.path.join(settings.value('Asistente-LADM_COL/wizards/points_download_csv_path', '.'), filename), QCoreApplication.translate('CreatePointsCadastreWizard', '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.log.logMessage("CSV doesn't exist! Probably due to a missing 'make' execution to generate resources...", PLUGIN_NAME, Qgis.Critical) msg = QCoreApplication.translate('CreatePointsCadastreWizard', '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.log.logMessage('Removing existing file {}...'.format(new_filename), PLUGIN_NAME, Qgis.Info) 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('CreatePointsCadastreWizard', 'The file <a href="file://{}">{}</a> was successfully saved!').format(new_filename, os.path.basename(new_filename)) self.show_message(msg, Qgis.Info) else: self.log.logMessage('There was an error copying the CSV file {}!'.format(new_filename), PLUGIN_NAME, Qgis.Info) msg = QCoreApplication.translate('CreatePointsCadastreWizard', 'The file couldn\'t be saved.') self.show_message(msg, Qgis.Warning) def show_message(self, message, level): 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_epsg', self.epsg) 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 = QgsCoordinateReferenceSystem( settings.value('Asistente-LADM_COL/wizards/points_csv_epsg', DEFAULT_EPSG, int)) self.update_crs_info() def show_help(self): self.qgis_utils.show_help("create_points")
class geopunt4QgisElevationDialog(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", "en") if not locale: locale == 'en' 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_elevationDlg() self.ui.setupUi(self) #get settings self.s = QSettings() self.loadSettings() self.gh = geometryHelper( self.iface ) self.eh = elevationHelper( self.iface, self.startDir) #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) ##graph global vars self.Rubberline = None self.profile = None self.pt = None self.ax = None self.ano = None self.anoLbl = None self.counter = 0 self.xscaleUnit = (1, "m") # a figure instance to plot on self.figure = Figure() #create the Canvas widget and toolbar and set graphWgt as parent self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) ### #self.ui.toolbar.layout().insertWidget(0, self.toolbar) self.ui.graphWgt.layout().addWidget(self.canvas) self.createCanvasToolbar() #events self.ui.drawBtn.clicked.connect(self.drawBtnClicked) self.figure.canvas.mpl_connect('motion_notify_event', self.showGraphMotion) self.ui.saveLineBtn.clicked.connect(self.saveLineClicked) self.ui.savePntBtn.clicked.connect(self.savePntClicked) self.ui.addDHMbtn.clicked.connect(self.addDHMasWMS) self.ui.refreshBtn.clicked.connect( self.onRefresh ) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.rejected.connect(self.clean ) def createCanvasToolbar (self): ''' 1 Reset original view 2 Back to previous view 3 Forward to next view 4 Pan axes with left mouse, zoom with right 5 Zoom to rectangle 6 Save the figure 7 Edit curves line and axes parameters ''' self.toolbar.setVisible(False) toolbarBtns = self.ui.toolbar.findChildren(QToolButton) self.ui.toolbar.setStyleSheet("""QToolButton {border-width: 2px; border-style: outset; border-color: #fbd837; border-radius: 5px ; background-color: white } QToolButton:pressed { border-style: inset; background-color: grey } """) toolbarBtns[0].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Keer terug naar overzicht")) toolbarBtns[0].setIcon( QIcon(":/plugins/geopunt4Qgis/images/full_extent.png")) toolbarBtns[0].clicked.connect( self.toolbar.home ) toolbarBtns[1].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Vorige")) toolbarBtns[1].setIcon( QIcon(":/plugins/geopunt4Qgis/images/previous.png")) toolbarBtns[1].clicked.connect( self.toolbar.back ) toolbarBtns[2].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Volgende")) toolbarBtns[2].setIcon( QIcon(":/plugins/geopunt4Qgis/images/next.png")) toolbarBtns[2].clicked.connect( self.toolbar.forward ) toolbarBtns[3].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Pannen")) toolbarBtns[3].setIcon( QIcon(":/plugins/geopunt4Qgis/images/pan.png")) toolbarBtns[3].clicked.connect( self.toolbar.pan ) toolbarBtns[4].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Zoom naar rechthoek")) toolbarBtns[4].setIcon( QIcon(":/plugins/geopunt4Qgis/images/rectangleZoom.png")) toolbarBtns[4].clicked.connect( self.toolbar.zoom ) toolbarBtns[5].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Opslaan als afbeelding")) toolbarBtns[5].setIcon( QIcon(":/plugins/geopunt4Qgis/images/save.png")) toolbarBtns[5].clicked.connect( self.save_fig ) #semf.toolbar.save_figure toolbarBtns[6].setToolTip(QCoreApplication.translate("geopunt4QgisElevationDialog", "Vorm grafiek aanpassen")) toolbarBtns[6].setIcon( QIcon(":/plugins/geopunt4Qgis/images/wrench.png")) toolbarBtns[6].clicked.connect( self.toolbar.edit_parameters) toolbarBtns[7].setIcon( QIcon(":/plugins/geopunt4Qgis/images/fill.png")) toolbarBtns[7].setToolTip( QCoreApplication.translate("geopunt4QgisElevationDialog", "Kies de vulkleur")) toolbarBtns[7].clicked.connect( self.setFill) def loadSettings(self): self.timeout = int( self.s.value("geopunt4qgis/timeout" ,15)) if settings().proxyUrl: self.proxy = settings().proxyUrl else: self.proxy = "" self.samplesSavetoFile = int( self.s.value("geopunt4qgis/samplesSavetoFile" , 1)) sampleLayer = self.s.value("geopunt4qgis/sampleLayerTxt", "") if sampleLayer: self.sampleLayerTxt = sampleLayer self.profileLineSavetoFile = int( self.s.value("geopunt4qgis/profileLineSavetoFile" , 1)) profileLineLayer= self.s.value("geopunt4qgis/profileLineLayerTxt", "") if profileLineLayer: self.profileLineLayerTxt = profileLineLayer self.startDir = self.s.value("geopunt4qgis/startDir", os.path.expanduser("~")) self.elevation = elevation(self.timeout, self.proxy ) def resizeEvent(self, event): QDialog.resizeEvent(self, event) if self.ax: self.figure.tight_layout() #eventhandlers def save_fig(self): formats = ( "Joint Photographic Experts Group (*.jpg) (*.jpg);;Scalable Vector Grapics (*.svg) (*.svg);;"+ "Portable Document Format (*.pdf) (*.pdf);;Tagged Image File Format (*.tif) (*.tif)"+ ";;Encapsulated Postscript (*.eps) (*.eps)") if not(sys.platform == 'win32'): formats += ";;Portable Network Graphics (*.png) (*.png)" fileName, __ = QFileDialog.getSaveFileName( self , "Save File", self.startDir, formats); self.figure.savefig(fileName) def onRefresh(self): if self.ano: self.ano.remove() self.ano = None if self.anoLbl: self.anoLbl.remove() self.anoLbl = None self.plot() def onResize(self, event): self.figure.tight_layout() def openHelp(self): webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/hoogteprofiel") def drawBtnClicked(self): self.clean() #self.reSetFigure() self.tool = lineTool(self.iface, self.callBack ) self.iface.mapCanvas().setMapTool(self.tool) self.showMinimized() self.counter += 1 def showGraphMotion(self, event): if self.ax == None: return if event.xdata != None and event.ydata != None: if self.ano != None: self.ano.remove() self.ano = None if self.anoLbl != None: self.anoLbl.remove() self.anoLbl = None xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] )# if n[3] > -9999 ] zx = np.interp( event.xdata, xdata, ydata ) xmax = np.max( xdata ) xmin = np.min( xdata ) zmax = np.max( ydata ) zmin = np.max( [n[3] for n in self.profile if n[3] > -9999 ] ) if event.xdata <= xmax and event.xdata >= xmin : self.ano = self.ax.arrow( event.xdata , -9999, 0, zx + 9999, fc="k", ec="k" ) box_props = dict(boxstyle="Round,pad=0.3", fc="cyan", ec="b", lw=2) self.anoLbl = self.ax.annotate( str( round(zx, 2)) + " m", xy= (event.xdata, zx ) , xytext= (event.xdata , zx + (0.2 * ( zmax - zmin )) ), bbox=box_props ) self.setMapPt( event.xdata / self.xscaleUnit[0] ) else: self.setMapPt() event.canvas.draw() def saveLineClicked(self): if not hasattr(self, 'profileLineLayerTxt'): layerName, accept = QInputDialog.getText(None, QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return else: self.profileLineLayerTxt = layerName if self.profile != None and self.Rubberline != None: title = self.ax.get_title() self.eh.save_profile( self.Rubberline.asGeometry(), self.profile, title, self.profileLineLayerTxt, self.profileLineSavetoFile, sender=self ) def savePntClicked(self): if not hasattr(self, 'sampleLayerTxt'): layerName, accept = QInputDialog.getText(None, QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return else: self.sampleLayerTxt = layerName if self.profile != None: title = self.ax.get_title() self.eh.save_sample_points( self.profile, title, self.sampleLayerTxt, self.samplesSavetoFile, sender=self ) def setFill( self ): if self.profile == None: return if self.ax == None: return clr = QColorDialog.getColor( Qt.white, self, QCoreApplication.translate( "geopunt4QgisElevationDialog", "Kies de vulkleur") ) if clr.isValid(): xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] ) self.ax.fill_between( xdata, ydata, -9999, color=clr.name() ) def addDHMasWMS(self): crs = self.gh.getGetMapCrs(self.iface).authid() if crs != 'EPSG:31370' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:31370' dhmUrl = "url=https://geoservices.informatievlaanderen.be/raadpleegdiensten/DHMV/wms&layers=DHMVII_DTM_1m&&format=image/png&styles=default&crs="+ crs try: rlayer = QgsRasterLayer(dhmUrl, 'Hoogtemodel', 'wms') if rlayer.isValid(): rlayer.renderer().setOpacity(0.8) QgsProject.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QCoreApplication.translate("geopunt4QgisElevationDialog", "Kan WMS niet laden"), level=Qgis.Critical, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=Qgis.Critical, duration=10) return def plot(self): if self.Rubberline == None: return wgsLine = self.gh.prjLineFromMapCrs( self.Rubberline.asGeometry() ) lineString = [ list(n) for n in wgsLine.asPolyline()] nrSamples = self.ui.nrOfSampleSpin.value() #try: self.profile = self.elevation.fetchElevaton( lineString, 4326, nrSamples) #except geopuntError as ge: # self.bar.pushMessage("Error", ge.message, level=Qgis.Critical, duration=10) # return if np.max( [n[0] for n in self.profile ] ) > 1000: self.xscaleUnit = (0.001 , "km" ) else: self.xscaleUnit = (1 , "m" ) xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] ) #need at least 3 values if len(xdata) <= 2 or len([n for n in self.profile if n[3] > -9999 ]) <= 2: self.bar.pushMessage("Error", QCoreApplication.translate( "geopunt4QgisElevationDialog", "Er werd geen of onvoldoende data gevonden"), level=Qgis.Warning, duration=5) self.profile = None return ymin = np.min( [n[3] for n in self.profile if n[3] > -9999 ] ) ymax = np.max( ydata ) # create an axis self.ax = self.figure.add_subplot(111) # discards the old graph self.ax.hold(False) # plot data self.ax.plot( xdata, ydata,'r*') self.ax.fill_between(xdata, ydata, -9999, color='#F8E6E0' ) self.ax.set_ylim([ymin , ymax]) self.ax.set_xlim([0 , None ]) self.ax.set_ylabel("hoogte (m)") self.ax.set_xlabel("afstand (%s)" % self.xscaleUnit[1] ) self.ax.set_title("Hoogteprofiel " + str( self.counter) ) # refresh canvas self.figure.tight_layout() self.canvas.draw() def callBack(self, geom): self.iface.mapCanvas().unsetMapTool(self.tool) self.Rubberline = geom self.showNormal() self.activateWindow() self.plot() self.ui.saveWgt.setEnabled(True) def setMapPt(self, dist=None ): if self.pt: self.iface.mapCanvas().scene().removeItem(self.pt) if dist==None: return if self.Rubberline == None: return # dist is measured in lambert 72 in meters lb72Line = self.gh.prjLineFromMapCrs( self.Rubberline.asGeometry() , 31370 ) lb72pt = lb72Line.interpolate(dist).asPoint() pt = self.gh.prjPtToMapCrs(lb72pt, 31370) self.pt = QgsVertexMarker(self.iface.mapCanvas()) self.pt.setCenter( pt ) self.pt.setColor(QColor(0,255,250)) self.pt.setIconSize(5) self.pt.setIconType(QgsVertexMarker.ICON_BOX ) # or ICON_CROSS, ICON_X self.pt.setPenWidth(7) if self.xscaleUnit[0] != 1: msg= "lengte= %s %s" % (round( dist * self.xscaleUnit[0], 2) , self.xscaleUnit[1]) else: msg= "lengte= %s %s" % (int( dist * self.xscaleUnit[0]) , self.xscaleUnit[1]) self.ui.mgsLbl.setText( msg ) def clean(self): if self.pt: self.iface.mapCanvas().scene().removeItem(self.pt) if self.Rubberline: self.iface.mapCanvas().scene().removeItem(self.Rubberline) if self.ano: self.ano.remove() self.ano = None if self.anoLbl: self.anoLbl.remove() self.anoLbl = None if self.ax: self.ax.hold(False) self.ax.clear() self.ax = None self.figure.clf() self.canvas.draw() self.ui.saveWgt.setEnabled(False) self.profile = None self.Rubberline = None self.ui.mgsLbl.setText("")
class GeobricksTRMM: def __init__(self, iface): self.iface = iface self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'geobricks_trmm_qgis_{}.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) self.layout = QVBoxLayout() self.username = QLineEdit() self.username.setPlaceholderText(self.tr('e.g. [email protected]')) self.password = QLineEdit() self.password.setEchoMode(QLineEdit.Password) self.password.setPlaceholderText(self.tr('e.g. [email protected]')) self.download_folder = QLineEdit() try: if self.last_download_folder is not None: self.download_folder.setText(self.last_download_folder) except: self.last_download_folder = None self.frequency = QComboBox() self.frequency.addItem(self.tr('Daily Sum'), 'SUM') self.frequency.addItem(self.tr('Daily Average'), 'AVG') self.frequency.addItem(self.tr('None'), 'NONE') self.from_date = QCalendarWidget() self.to_date = QCalendarWidget() self.bar = QgsMessageBar() self.lbl_0 = QLabel('<b>' + self.tr('Username') + '</b>') self.lbl_1 = QLabel('<b>' + self.tr('Password') + '</b>') self.lbl_2 = QLabel('<b>' + self.tr('Aggregation') + '</b>') self.from_date_label = QLabel('<b>' + self.tr('From Date') + '</b>: ' + QDate(2015, 7, 31).toString('MMMM d, yyyy')) self.to_date_label = QLabel('<b>' + self.tr('To Date') + '</b>: ' + QDate(2015, 7, 31).toString('MMMM d, yyyy')) self.lbl_5 = QLabel('<b>' + self.tr('Download Folder') + '</b>') self.lbl_6 = QLabel('<i style="color: blue;">' + self.tr('Create an account') + '</i>') self.lbl_7 = QLabel('<b>' + self.tr('Data availability') + '</b>: ' + self.tr('from January 1st 1998 to July 31st 2015')) self.palette = QPalette() self.from_date_widget = QWidget() self.from_date_widget_layout = QVBoxLayout() self.dates_widget = QWidget() self.dates_widget_layout = QHBoxLayout() self.username_widget = QWidget() self.username_layout = QVBoxLayout() self.password_widget = QWidget() self.password_layout = QVBoxLayout() self.progressBar = QProgressBar() self.progress_label = QLabel('<b>' + self.tr('Progress') + '</b>') self.login_widget = QWidget() self.login_layout = QHBoxLayout() self.download_folder_widget = QWidget() self.download_folder_layout = QHBoxLayout() self.download_folder_button = QPushButton(self.tr('...')) self.download_button = QPushButton(self.tr('Start Download')) self.close_button = QPushButton(self.tr('Close Window')) self.add_to_canvas = QCheckBox(self.tr('Add output layer to canvas')) self.add_to_canvas.setChecked(True) self.to_date_widget = QWidget() self.to_date_widget_layout = QVBoxLayout() self.spacing = 16 self.dlg = GeobricksTRMMDialog() self.actions = [] self.menu = self.tr('Download Data') self.toolbar = self.iface.addToolBar(self.tr('TRMM Data Downloader')) self.toolbar.setObjectName('TRMMDataDownloader') self.is_rendered = False def run(self): # Build UI self.build_ui() def build_ui(self): # Link label self.lbl_6.mousePressEvent = open_browser_registration self.palette.setColor(QPalette.Foreground, QColor('blue')) self.lbl_6.setPalette(self.palette) # Calendars self.from_date.setMinimumDate(QDate(1998, 1, 1)) self.from_date.setMaximumDate(QDate(2015, 7, 31)) self.to_date.setMinimumDate(QDate(1998, 1, 1)) self.to_date.setMaximumDate(QDate(2015, 7, 31)) # Message bar self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout.addWidget(self.bar) # From date panel self.from_date_widget_layout.setContentsMargins(0, 0, 0, 0) self.from_date_widget_layout.setSpacing(self.spacing) self.from_date_widget.setLayout(self.from_date_widget_layout) self.from_date_widget_layout.addWidget(self.from_date_label) self.from_date_widget_layout.addWidget(self.from_date) self.from_date.clicked[QDate].connect(self.update_from_label) # To date panel self.to_date_widget_layout.setContentsMargins(0, 0, 0, 0) self.to_date_widget_layout.setSpacing(self.spacing) self.to_date_widget.setLayout(self.to_date_widget_layout) self.to_date_widget_layout.addWidget(self.to_date_label) self.to_date_widget_layout.addWidget(self.to_date) self.to_date.clicked[QDate].connect(self.update_to_label) # Dates panel self.dates_widget_layout.setContentsMargins(0, 0, 0, 0) self.dates_widget_layout.setSpacing(self.spacing) self.dates_widget.setLayout(self.dates_widget_layout) self.dates_widget_layout.addWidget(self.from_date_widget) self.dates_widget_layout.addWidget(self.to_date_widget) # Username panel self.username_layout.setContentsMargins(0, 0, 0, 0) self.username_layout.setSpacing(self.spacing) self.username_widget.setLayout(self.username_layout) self.username_layout.addWidget(self.lbl_0) self.username_layout.addWidget(self.username) # Password panel self.password_layout.setContentsMargins(0, 0, 0, 0) self.password_layout.setSpacing(self.spacing) self.password_widget.setLayout(self.password_layout) self.password_layout.addWidget(self.lbl_1) self.password_layout.addWidget(self.password) # Login panel self.login_layout.setContentsMargins(0, 0, 0, 0) self.login_layout.setSpacing(self.spacing) self.login_widget.setLayout(self.login_layout) self.login_layout.addWidget(self.username_widget) self.login_layout.addWidget(self.password_widget) # Download folder panel self.download_folder_layout.setContentsMargins(0, 0, 0, 0) self.download_folder_layout.setSpacing(0) self.download_folder_widget.setLayout(self.download_folder_layout) self.download_folder_button.clicked.connect(self.select_output_file) self.download_folder_layout.addWidget(self.download_folder) self.download_folder_layout.addWidget(self.download_folder_button) # Download button self.download_button.clicked.connect(self.start) # Close button self.close_button.clicked.connect(self.close) # Add widgets to layout self.layout.addWidget(self.login_widget) self.layout.addWidget(self.lbl_6) self.layout.addWidget(self.lbl_2) self.layout.addWidget(self.frequency) self.layout.addWidget(self.dates_widget) self.layout.addWidget(self.lbl_5) self.layout.addWidget(self.download_folder_widget) self.layout.addWidget(self.add_to_canvas) self.layout.addWidget(self.download_button) self.layout.addWidget(self.progress_label) self.layout.addWidget(self.progressBar) self.layout.addWidget(self.close_button) # Set layout self.dlg.setLayout(self.layout) # Show dialog self.dlg.show() def update_from_label(self, date): self.from_date_label.setText('<b>' + self.tr('From Date') + '</b>: ' + date.toString('MMMM d, yyyy')) def update_to_label(self, date): self.to_date_label.setText('<b>' + self.tr('To Date') + '</b>: ' + date.toString('MMMM d, yyyy')) def select_output_file(self): filename = QFileDialog.getExistingDirectory(self.dlg, self.tr('Select Folder')) self.last_download_folder = filename self.download_folder.setText(self.last_download_folder) def tr(self, message): return QCoreApplication.translate('geobricks_trmm_qgis', 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): 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): icon_path = ':/plugins/geobricks_qgis_plugin_trmm/icon.png' self.add_action( icon_path, text=self.tr('TRMM Data Downloader'), callback=self.run, parent=self.iface.mainWindow()) def unload(self): for action in self.actions: self.iface.removePluginMenu( 'TRMMDataDownloader', action) self.iface.removeToolBarIcon(action) del self.toolbar def close(self): self.dlg.close() def start(self): p = self.collect_parameters() if p is not None: self.progressBar.setMaximum(100) self.progressBar.setValue(0) i = 0 try: range = date_range(p['from_date'], p['to_date']) for current_date in range: layers = list_layers(p['username'], p['password'], current_date.year, current_date.month, current_date.day, p['download_path']) if p['frequency'] is not 'NONE': self.aggregate_layers(layers, current_date) else: if p['open_in_qgis'] is True: for l in layers: if '.tfw' not in l: self.iface.addRasterLayer(l, str(l)) i += 1 percent = (i/float(len(range))) * 100 self.progressBar.setValue(percent) except Exception, e: self.bar.pushMessage(None, str(e), level=QgsMessageBar.CRITICAL)
class ServerConnectionsWidget(BASE, WIDGET): def __init__(self): super(ServerConnectionsWidget, self).__init__() self.currentServer = None self.setupUi(self) self.addMenuToButtonNew() self.addAuthWidgets() self.buttonRemove.clicked.connect(self.buttonRemoveClicked) self.populateServers() self.populatePostgisCombo() self.listServers.currentItemChanged.connect(self.currentServerChanged) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.setCurrentServer(None) self.buttonSave.clicked.connect(self.saveButtonClicked) self.radioUploadData.toggled.connect(self.datastoreChanged) self.radioLocalPath.toggled.connect(self.mapserverStorageChanged) self.btnConnectGeoserver.clicked.connect(self.testConnectionGeoserver) self.btnConnectPostgis.clicked.connect(self.testConnectionPostgis) self.btnConnectGeocatLive.clicked.connect( self.testConnectionGeocatLive) self.btnConnectCsw.clicked.connect(self.testConnectionCsw) self.chkManagedWorkspace.stateChanged.connect( self.managedWorkspaceChanged) self.txtCswName.textChanged.connect(self._setCurrentServerHasChanges) self.txtCswNode.textChanged.connect(self._setCurrentServerHasChanges) self.txtGeoserverName.textChanged.connect( self._setCurrentServerHasChanges) self.txtPostgisName.textChanged.connect( self._setCurrentServerHasChanges) self.txtGeoserverUrl.textChanged.connect( self._setCurrentServerHasChanges) self.txtGeocatLiveName.textChanged.connect( self._setCurrentServerHasChanges) self.txtCswUrl.textChanged.connect(self._setCurrentServerHasChanges) self.txtPostgisServerAddress.textChanged.connect( self._setCurrentServerHasChanges) self.txtPostgisPort.textChanged.connect( self._setCurrentServerHasChanges) self.txtPostgisSchema.textChanged.connect( self._setCurrentServerHasChanges) self.txtPostgisDatabase.textChanged.connect( self._setCurrentServerHasChanges) self.txtGeoserverWorkspace.textChanged.connect( self._setCurrentServerHasChanges) self.txtGeocatLiveIdentifier.textChanged.connect( self._setCurrentServerHasChanges) self.comboMetadataProfile.currentIndexChanged.connect( self._setCurrentServerHasChanges) self.comboDatastore.currentIndexChanged.connect( self._setCurrentServerHasChanges) self.fileMapserver.setStorageMode(QgsFileWidget.GetDirectory) def datastoreChanged(self, checked): self.comboDatastore.setEnabled(not checked) #self.btnNewDatastore.setEnabled(not checked) self._setCurrentServerHasChanges() def managedWorkspaceChanged(self, state): self.txtGeoserverWorkspace.setEnabled(state == Qt.Unchecked) def mapserverStorageChanged(self, checked): self.labelLocalFolder.setVisible(checked) self.labelRemoteFolder.setVisible(not checked) self.fileMapserver.setVisible(checked) self.txtRemoteFolder.setVisible(not checked) self.labelHost.setVisible(not checked) self.labelPort.setVisible(not checked) self.labelMapserverCredentials.setVisible(not checked) self.txtMapserverHost.setVisible(not checked) self.txtMapserverPort.setVisible(not checked) self.mapserverAuthWidget.setVisible(not checked) self._setCurrentServerHasChanges() def currentServerChanged(self, new, old): if new is None: self.setCurrentServer(new) return else: name = self.listServers.itemWidget(new).serverName() server = allServers()[name] if self.currentServer is not None and new is not None: if server.name == self.currentServer.name: return if self.currentServerHasChanges: res = QMessageBox.question( self, self.tr("Servers"), self.tr( "Do you want to save changes to the current server?"), QMessageBox.Cancel | QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes) if res == QMessageBox.Yes: if not self.saveCurrentServer(): self.bar.pushMessage( self.tr("Error"), self.tr("Wrong values in current item"), level=Qgis.Warning, duration=5) self.listServers.setCurrentItem(old) else: self.setCurrentServer(server) elif res == QMessageBox.Cancel: self.listServers.setCurrentItem(old) else: self.setCurrentServer(server) else: self.setCurrentServer(server) def _testConnection(self, server): if server is None: self.bar.pushMessage(self.tr("Error"), self.tr("Wrong values in current item"), level=Qgis.Warning, duration=5) else: if execute(server.testConnection): self.bar.pushMessage( self.tr("Success"), self.tr("Connection succesfully established with server"), level=Qgis.Success, duration=5) else: self.bar.pushMessage(self.tr("Error"), self.tr("Could not connect with server"), level=Qgis.Warning, duration=5) def testConnectionPostgis(self): server = self.createPostgisServer() self._testConnection(server) def testConnectionGeoserver(self): server = self.createGeoserverServer() self._testConnection(server) def testConnectionGeocatLive(self): server = self.createGeocatLiveServer() self._testConnection(server) def testConnectionCsw(self): server = self.createGeonetworkServer() self._testConnection(server) def saveCurrentServer(self): w = self.stackedWidget.currentWidget() server = None if w == self.widgetEmpty: return True elif w == self.widgetGeoserver: server = self.createGeoserverServer() elif w == self.widgetMapserver: server = self.createMapserverServer() elif w == self.widgetPostgis: server = self.createPostgisServer() elif w == self.widgetMetadataCatalog: server = self.createGeonetworkServer() elif w == self.widgetGeocatLive: server = self.createGeocatLiveServer() if server is None: return False else: if self.currentServer is not None: removeServer(self.currentServer.name) item = self.itemFromServerName(self.currentServer.name) self.listServers.itemWidget(item).setServerName(server.name) addServer(server) self.currentServer = server return True def itemFromServerName(self, name): for i in range(self.listServers.count()): item = self.listServers.item(i) if name == self.listServers.itemWidget(item).serverName(): return item def createGeoserverServer(self): ##TODO check validity of name and values name = self.txtGeoserverName.text().strip() url = self.txtGeoserverUrl.text().strip() if self.chkManagedWorkspace.isChecked(): workspace = None else: workspace = self.txtGeoserverWorkspace.text().strip() authid = self.geoserverAuth.configId() if self.radioUploadData.isChecked(): storage = GeoserverServer.UPLOAD_DATA postgisdb = None else: storage = GeoserverServer.STORE_IN_POSTGIS postgisdb = self.comboDatastore.currentText() if "" in [name, url, workspace]: return None server = GeoserverServer(name, url, authid, storage, workspace, postgisdb) return server def createPostgisServer(self): ##TODO check validity of name and values name = self.txtPostgisName.text() host = self.txtPostgisServerAddress.text() port = self.txtPostgisPort.text() schema = self.txtPostgisSchema.text() database = self.txtPostgisDatabase.text() authid = self.postgisAuth.configId() server = PostgisServer(name, authid, host, port, schema, database) return server def createGeonetworkServer(self): ##TODO check validity of name and values name = self.txtCswName.text() node = self.txtCswNode.text() authid = self.cswAuth.configId() url = self.txtCswUrl.text() profile = self.comboMetadataProfile.currentIndex() server = GeonetworkServer(name, url, authid, profile, node) return server def createMapserverServer(self): ##TODO check validity of name and values name = self.txtMapserverName.text() authid = self.mapserverAuth.configId() host = self.txtMapserverHost.text() try: port = int(self.txtMapserverPort.text()) except: return None local = self.radioLocalPath.isChecked() if local: folder = self.fileMapserver.filePath() else: folder = self.txtRemoteFolder.text() url = self.txtMapserverUrl.text() servicesPath = self.txtMapServicesPath.text() projFolder = self.txtProjFolder.text() server = MapserverServer(name, url, local, folder, authid, host, port, servicesPath, projFolder) return server def createGeocatLiveServer(self): name = self.txtGeocatLiveName.text() geoserverAuthid = self.geocatLiveGeoserverAuth.configId() geonetworkAuthid = self.geocatLiveGeonetworkAuth.configId() userid = self.txtGeocatLiveIdentifier.text() server = GeocatLiveServer(name, userid, geoserverAuthid, geonetworkAuthid) return server def addAuthWidgets(self): self.geoserverAuth = QgsAuthConfigSelect() self.geoserverAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.geoserverAuth) self.geoserverAuthWidget.setLayout(layout) self.geoserverAuthWidget.setFixedHeight(self.txtGeoserverUrl.height()) self.mapserverAuth = QgsAuthConfigSelect() self.mapserverAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.mapserverAuth) self.mapserverAuthWidget.setLayout(layout) self.mapserverAuthWidget.setFixedHeight(self.txtGeoserverUrl.height()) self.postgisAuth = QgsAuthConfigSelect() self.postgisAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.postgisAuth) self.postgisAuthWidget.setLayout(layout) self.postgisAuthWidget.setFixedHeight(self.txtGeoserverUrl.height()) self.cswAuth = QgsAuthConfigSelect() self.cswAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.cswAuth) self.cswAuthWidget.setLayout(layout) self.cswAuthWidget.setFixedHeight(self.txtGeoserverUrl.height()) self.geocatLiveGeoserverAuth = QgsAuthConfigSelect() self.geocatLiveGeoserverAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.geocatLiveGeoserverAuth) self.geocatLiveGeoserverAuthWidget.setLayout(layout) self.geocatLiveGeoserverAuthWidget.setFixedHeight( self.txtGeoserverUrl.height()) self.geocatLiveGeonetworkAuth = QgsAuthConfigSelect() self.geocatLiveGeonetworkAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.geocatLiveGeonetworkAuth) self.geocatLiveGeonetworkAuthWidget.setLayout(layout) self.geocatLiveGeonetworkAuthWidget.setFixedHeight( self.txtGeoserverUrl.height()) def addMenuToButtonNew(self): menu = QMenu() menu.addAction("GeoServer", lambda: self._addServer("GeoServer", GeoserverServer)) menu.addAction("MapServer", lambda: self._addServer("MapServer", MapserverServer)) menu.addAction( "GeoCat Live", lambda: self._addServer("GeoCat Live", GeocatLiveServer)) menu.addAction("GeoNetwork", lambda: self._addServer("GeoNetwork", GeonetworkServer)) #menu.addAction("CSW", lambda: self._addServer("CSW", CswServer)) menu.addAction("PostGIS", lambda: self._addServer("PostGIS", PostgisServer)) self.buttonNew.setMenu(menu) def buttonRemoveClicked(self): item = self.listServers.currentItem() if item is None: return name = self.listServers.itemWidget(item).serverName() removeServer(name) self.listServers.takeItem(self.listServers.currentRow()) self.listServers.setCurrentItem(None) def populateServers(self): self.listServers.clear() servers = allServers().values() for server in servers: self.addServerItem(server) def addServerItem(self, server): widget = ServerItemWidget(server) item = QListWidgetItem(self.listServers) item.setSizeHint(widget.sizeHint()) self.listServers.addItem(item) self.listServers.setItemWidget(item, widget) return item def _addServer(self, name, clazz): if self.currentServerHasChanges: self.bar.pushMessage( self.tr("Save changes to current server before creating one"), level=Qgis.Warning, duration=5) else: name = self.getNewName(name) server = clazz(name) addServer(server) self.setCurrentServer(server) item = self.addServerItem(server) self.listServers.setCurrentItem(item) def populatePostgisCombo(self): self.comboDatastore.clear() servers = allServers().values() for s in servers: if isinstance(s, PostgisServer): self.comboDatastore.addItem(s.name) def _setCurrentServerHasChanges(self): self.currentServerHasChanges = True def setCurrentServer(self, server): self.currentServer = server if server is None: self.stackedWidget.setCurrentWidget(self.widgetEmpty) elif isinstance(server, GeoserverServer): self.stackedWidget.setCurrentWidget(self.widgetGeoserver) self.txtGeoserverName.setText(server.name) self.txtGeoserverUrl.setText(server.url) if server.workspace is None: self.txtGeoserverWorkspace.setText("") self.chkManagedWorkspace.setChecked(True) else: self.txtGeoserverWorkspace.setText(server.workspace) self.chkManagedWorkspace.setChecked(False) self.geoserverAuth.setConfigId(server.authid) self.populatePostgisCombo() if server.postgisdb is not None: self.comboDatastore.setCurrentText(server.postgisdb) self.radioUploadData.setChecked( server.storage == server.UPLOAD_DATA) self.radioStoreInPostgis.setChecked( server.storage == server.STORE_IN_POSTGIS) elif isinstance(server, MapserverServer): self.stackedWidget.setCurrentWidget(self.widgetMapserver) self.txtMapserverName.setText(server.name) self.fileMapserver.setFilePath(server.folder) self.txtRemoteFolder.setText(server.folder) self.txtMapserverHost.setText(server.host) self.txtMapserverPort.setText(str(server.port)) self.mapserverAuth.setConfigId(server.authid) self.txtMapserverUrl.setText(server.url) self.txtMapServicesPath.setText(server.servicesPath) self.txtProjFolder.setText(server.projFolder) self.radioLocalPath.setChecked(server.useLocalFolder) self.radioFtp.setChecked(not server.useLocalFolder) self.mapserverStorageChanged(server.useLocalFolder) elif isinstance(server, PostgisServer): self.stackedWidget.setCurrentWidget(self.widgetPostgis) self.txtPostgisName.setText(server.name) self.txtPostgisDatabase.setText(server.database) self.txtPostgisPort.setText(server.port) self.txtPostgisServerAddress.setText(server.host) self.txtPostgisSchema.setText(server.schema) self.postgisAuth.setConfigId(server.authid) elif isinstance(server, (GeonetworkServer, CswServer)): self.stackedWidget.setCurrentWidget(self.widgetMetadataCatalog) self.txtCswName.setText(server.name) self.txtCswNode.setText(server.node) self.txtCswUrl.setText(server.url) self.cswAuth.setConfigId(server.authid) self.comboMetadataProfile.setCurrentIndex(server.profile) elif isinstance(server, GeocatLiveServer): self.stackedWidget.setCurrentWidget(self.widgetGeocatLive) self.txtGeocatLiveName.setText(server.name) self.txtGeocatLiveIdentifier.setText(server.userid) self.geocatLiveGeoserverAuth.setConfigId(server.geoserverAuthid) self.geocatLiveGeonetworkAuth.setConfigId(server.geonetworkAuthid) self.currentServerHasChanges = False def getNewName(self, name): servers = list(allServers().keys()) i = 1 while True: n = name + str(i) if n not in servers: return n else: i += 1 def saveButtonClicked(self): if self.saveCurrentServer(): self.currentServerHasChanges = False else: self.bar.pushMessage(self.tr("Error"), self.tr("Wrong values in current item"), level=Qgis.Warning, duration=5) def onClose(self, evt): if self.currentServerHasChanges: res = QMessageBox.question( self, self.tr("Servers"), self. tr("Do you want to close without saving the current changes?"), QMessageBox.Cancel | QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes) if res == QMessageBox.Yes: evt.accept() else: evt.ignore() else: evt.accept()
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 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")[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 ) #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.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 ) 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://kgis.be/index.html#!geopuntPoi.md") def onZoekActivated(self): txt = self.ui.poiText.text() self.ui.resultLijst.clearContents() self.ui.resultLijst.setRowCount(0) #filters: if self.ui.filterBox.isChecked(): 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 = "" else: poitheme, poiCategorie, poiType, 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) self.ui.msgLbl.setText(QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Aantal getoond: %s gevonden: %s" % ( self.ui.resultLijst.rowCount() , self.poi.resultCount ) )) elif len(suggesties) == 0: self.bar.pushMessage( QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Geen resultaten gevonden voor: "), 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 addMinModel(self): if not self.layernameValid(): return self.clearGraphicsLayer() txt = self.ui.poiText.text() if self.ui.filterBox.isChecked(): 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 = "" else: poitheme, poiCategorie, poiType, 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 _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.resultLijst.clearContents() self.ui.resultLijst.setRowCount(0) self.clearGraphicsLayer()
class LoadByCategory(QtGui.QDialog, load_by_category_dialog.Ui_LoadByCategory): def __init__(self, parent=None): """Constructor.""" super(LoadByCategory, 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.filename = "" self.dbLoaded = False self.epsg = 0 self.crs = None self.categories = [] self.selectedClasses = [] self.point = [] self.line = [] self.polygon = [] self.pointWithElement = [] self.lineWithElement = [] self.polygonWithElement = [] #Sql factory generator self.isSpatialite = True self.setupUi(self) self.tabWidget.setCurrentIndex(0) self.factory = SqlGeneratorFactory() self.gen = self.factory.createSqlGenerator(self.isSpatialite) self.utils = Utils() self.parentTreeNode = None self.comboBoxPostgis.setCurrentIndex(0) self.checkBoxPoint.setCheckState(0) self.checkBoxLine.setCheckState(0) self.checkBoxPolygon.setCheckState(0) self.checkBoxAll.setCheckState(0) self.bar = QgsMessageBar() self.setLayout(QtGui.QGridLayout(self)) self.layout().setContentsMargins(0,0,0,0) self.layout().setAlignment(QtCore.Qt.AlignTop) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.bar.setSizePolicy(sizePolicy) self.layout().addWidget(self.bar, 0,0,1,1) #Objects Connections QtCore.QObject.connect(self.pushButtonOpenFile, QtCore.SIGNAL(("clicked()")), self.loadDatabase) QtCore.QObject.connect(self.pushButtonCancel, QtCore.SIGNAL(("clicked()")), self.cancel) QtCore.QObject.connect(self.pushButtonOk, QtCore.SIGNAL(("clicked()")), self.okSelected) QtCore.QObject.connect(self.tabWidget,QtCore.SIGNAL(("currentChanged(int)")), self.restoreInitialState) QtCore.QObject.connect(self.pushButtonSelectAll, QtCore.SIGNAL(("clicked()")), self.selectAll) QtCore.QObject.connect(self.pushButtonDeselectAll, QtCore.SIGNAL(("clicked()")), self.deselectAll) QtCore.QObject.connect(self.pushButtonSelectOne, QtCore.SIGNAL(("clicked()")), self.selectOne) QtCore.QObject.connect(self.pushButtonDeselectOne, QtCore.SIGNAL(("clicked()")), self.deselectOne) QtCore.QObject.connect(self.checkBoxAll, QtCore.SIGNAL(("stateChanged(int)")), self.setAllGroup) self.db = None #populating the postgis combobox self.populatePostGISConnectionsCombo() def __del__(self): self.closeDatabase() def closeDatabase(self): if self.db: self.db.close() self.db = None def restoreInitialState(self): self.filename = "" self.dbLoaded = False self.epsg = 0 self.crs = None self.categories = [] self.selectedClasses = [] self.spatialiteFileEdit.setText(self.filename) self.postGISCrsEdit.setText('') self.postGISCrsEdit.setReadOnly(True) self.spatialiteCrsEdit.setText('') self.spatialiteCrsEdit.setReadOnly(True) self.listWidgetCategoryFrom.clear() self.listWidgetCategoryTo.clear() self.point = [] self.line = [] self.polygon = [] self.pointWithElement = [] self.lineWithElement = [] self.polygonWithElement = [] self.parentTreeNode = None #Setting the database type if self.tabWidget.currentIndex() == 0: self.isSpatialite = True else: self.isSpatialite = False #getting the sql generator according to the database type self.gen = self.factory.createSqlGenerator(self.isSpatialite) self.comboBoxPostgis.setCurrentIndex(0) self.checkBoxPoint.setCheckState(0) self.checkBoxLine.setCheckState(0) self.checkBoxPolygon.setCheckState(0) self.checkBoxAll.setCheckState(0) def updateBDField(self): if self.dbLoaded == True: self.spatialiteFileEdit.setText(self.filename) else: self.filename = "" self.spatialiteFileEdit.setText(self.filename) def getDatabaseVersion(self): self.dbVersion = self.utils.getDatabaseVersion(self.db) self.qmlPath = self.utils.getQmlDir(self.db) def listCategoriesFromDatabase(self): self.listWidgetCategoryFrom.clear() self.listWidgetCategoryTo.clear() sql = self.gen.getTablesFromDatabase() query = QSqlQuery(sql, self.db) self.getDatabaseVersion() while query.next(): if self.isSpatialite: tableName = query.value(0) layerName = tableName split = tableName.split('_') if len(split) < 2: continue if self.dbVersion == '3.0' or self.dbVersion == '2.1.3': schema = split[0] category = split[1] categoryName = schema+'.'+category else: categoryName = split[0] #done this way to have back compatibility with spatialites already in production else: tableSchema = query.value(0) tableName = query.value(1) split = tableName.split('_') category = split[0] categoryName = tableSchema+'.'+category layerName = tableSchema+'.'+tableName if layerName.split("_")[-1] == "p": self.point.append(layerName) if layerName.split("_")[-1] == "l": self.line.append(layerName) if layerName.split("_")[-1] == "a": self.polygon.append(layerName) if tableName.split("_")[-1] == "p" or tableName.split("_")[-1] == "l" \ or tableName.split("_")[-1] == "a": self.insertIntoListView(categoryName) self.listWidgetCategoryFrom.sortItems() self.setCRS() def insertIntoListView(self, item_name): found = self.listWidgetCategoryFrom.findItems(item_name, Qt.MatchExactly) if len(found) == 0: item = QtGui.QListWidgetItem(item_name) self.listWidgetCategoryFrom.addItem(item) def selectAll(self): tam = self.listWidgetCategoryFrom.__len__() for i in range(tam+1,1,-1): item = self.listWidgetCategoryFrom.takeItem(i-2) self.listWidgetCategoryTo.addItem(item) self.listWidgetCategoryTo.sortItems() def deselectAll(self): tam = self.listWidgetCategoryTo.__len__() for i in range(tam+1,1,-1): item = self.listWidgetCategoryTo.takeItem(i-2) self.listWidgetCategoryFrom.addItem(item) self.listWidgetCategoryFrom.sortItems() def selectOne(self): listedItems = self.listWidgetCategoryFrom.selectedItems() for i in listedItems: item = self.listWidgetCategoryFrom.takeItem(self.listWidgetCategoryFrom.row(i)) self.listWidgetCategoryTo.addItem(item) self.listWidgetCategoryTo.sortItems() def deselectOne(self): listedItems = self.listWidgetCategoryTo.selectedItems() for i in listedItems: item = self.listWidgetCategoryTo.takeItem(self.listWidgetCategoryTo.row(i)) self.listWidgetCategoryFrom.addItem(item) self.listWidgetCategoryFrom.sortItems() def setAllGroup(self): if self.checkBoxAll.isChecked(): self.checkBoxPoint.setCheckState(2) self.checkBoxLine.setCheckState(2) self.checkBoxPolygon.setCheckState(2) else: self.checkBoxPoint.setCheckState(0) self.checkBoxLine.setCheckState(0) self.checkBoxPolygon.setCheckState(0) def setCRS(self): try: self.epsg = self.utils.findEPSG(self.db) if self.epsg == -1: self.bar.pushMessage("", self.tr("Coordinate Reference System not set or invalid!"), level=QgsMessageBar.WARNING) else: self.crs = QgsCoordinateReferenceSystem(self.epsg, QgsCoordinateReferenceSystem.EpsgCrsId) if self.isSpatialite: self.spatialiteCrsEdit.setText(self.crs.description()) self.spatialiteCrsEdit.setReadOnly(True) else: self.postGISCrsEdit.setText(self.crs.description()) self.postGISCrsEdit.setReadOnly(True) except: pass @pyqtSlot(int) def on_comboBoxPostgis_currentIndexChanged(self): if self.comboBoxPostgis.currentIndex() > 0: self.loadDatabase() def loadDatabase(self): self.closeDatabase() if self.isSpatialite: (self.filename, self.db) = self.utils.getSpatialiteDatabase() if self.filename: self.spatialiteFileEdit.setText(self.filename) else: self.db = self.utils.getPostGISDatabase(self.comboBoxPostgis.currentText()) try: if not self.db.open(): QgsMessageLog.logMessage(self.db.lastError().text(), "DSG Tools Plugin", QgsMessageLog.CRITICAL) else: self.dbLoaded = True self.listCategoriesFromDatabase() except: pass def populatePostGISConnectionsCombo(self): self.comboBoxPostgis.clear() self.comboBoxPostgis.addItem("Select Database") self.comboBoxPostgis.addItems(self.utils.getPostGISConnections()) def cancel(self): self.restoreInitialState() self.close() def getSelectedItems(self): lista = self.classesListWidget.selectedItems() self.selectedClasses = [] tam = len(lista) for i in range(tam): self.selectedClasses.append(lista[i].text()) self.selectedClasses.sort() def okSelected(self): try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) if self.checkBoxOnlyWithElements.isChecked(): self.setLayersWithElements() ponto = self.pointWithElement linha = self.lineWithElement area = self.polygonWithElement else: ponto = self.point linha = self.line area = self.polygon if self.db and self.crs and len(self.listWidgetCategoryTo)>0: categoriasSelecionadas = [] for i in range(self.listWidgetCategoryTo.__len__()): categoriasSelecionadas.append(self.listWidgetCategoryTo.item(i).text()) try: if self.checkBoxPoint.isChecked(): self.loadLayers('p',categoriasSelecionadas,ponto) if self.checkBoxLine.isChecked(): self.loadLayers('l',categoriasSelecionadas,linha) if self.checkBoxPolygon.isChecked(): self.loadLayers('a',categoriasSelecionadas,area) if self.checkBoxPoint.isChecked()== False and self.checkBoxLine.isChecked() == False and self.checkBoxPolygon.isChecked() == False: self.bar.pushMessage(self.tr("WARNING!"), self.tr("Please, select at least one type of layer!"), level=QgsMessageBar.WARNING) else: self.restoreInitialState() self.close() except: qgis.utils.iface.messageBar().pushMessage(self.tr("CRITICAL!"), self.tr("Problem loading the categories!"), level=QgsMessageBar.CRITICAL) pass else: if self.db and not self.crs: self.bar.pushMessage(self.tr("CRITICAL!"), self.tr("Could not determine the coordinate reference system!"), level=QgsMessageBar.CRITICAL) if not self.db and not self.crs: self.bar.pushMessage(self.tr("CRITICAL!"), self.tr("Database not loaded properly!"), level=QgsMessageBar.CRITICAL) self.bar.pushMessage(self.tr("CRITICAL!"), self.tr("Could not determine the coordinate reference system!"), level=QgsMessageBar.CRITICAL) if len(self.listWidgetCategoryTo)==0: self.bar.pushMessage(self.tr("WARNING!"), self.tr("Please, select at least one category!"), level=QgsMessageBar.WARNING) categoriasSelecionadas = [] self.pointWithElement = [] self.lineWithElement = [] self.polygonWithElement = [] QApplication.restoreOverrideCursor() except: QApplication.restoreOverrideCursor() def loadLayers(self, type, categories, layer_names): if self.isSpatialite: self.loadSpatialiteLayers(type, categories, layer_names) else: self.loadPostGISLayers(type, categories, layer_names) def setLayersWithElements(self): self.pointWithElement = [] self.lineWithElement = [] self.polygonWithElement = [] pontoAux = self.countElements(self.point) linhaAux = self.countElements(self.line) areaAux = self.countElements(self.polygon) for i in pontoAux: if i[1] > 0: self.pointWithElement.append(i[0]) for i in linhaAux: if i[1] > 0: self.lineWithElement.append(i[0]) for i in areaAux: if i[1] > 0: self.polygonWithElement.append(i[0]) def countElements(self, layers): listaQuantidades = [] for layer in layers: sql = self.gen.getElementCountFromLayer(layer) query = QSqlQuery(sql,self.db) query.next() number = query.value(0) if not query.exec_(sql): QgsMessageLog.logMessage(self.tr("Problem counting elements: ")+query.lastError().text(), "DSG Tools Plugin", QgsMessageLog.CRITICAL) listaQuantidades.append([layer, number]) return listaQuantidades def loadPostGISLayers(self, type, categories, layer_names): (database, host, port, user, password) = self.utils.getPostGISConnectionParameters(self.comboBoxPostgis.currentText()) uri = QgsDataSourceURI() uri.setConnection(str(host),str(port), str(database), str(user), str(password)) geom_column = 'geom' if self.parentTreeNode is None: self.parentTreeNode = qgis.utils.iface.legendInterface (). addGroup (database, -1) if type == 'p': idGrupo = qgis.utils.iface.legendInterface (). addGroup ("Ponto", True,self.parentTreeNode) for categoria in categories: self.preparePostGISToLoad(uri, categoria, layer_names, idGrupo, geom_column) if type == 'l': idGrupo = qgis.utils.iface.legendInterface (). addGroup ("Linha", True,self.parentTreeNode) for categoria in categories: self.preparePostGISToLoad(uri, categoria, layer_names, idGrupo, geom_column) if type == 'a': idGrupo = qgis.utils.iface.legendInterface (). addGroup ("Area", True,self.parentTreeNode) for categoria in categories: self.preparePostGISToLoad(uri, categoria, layer_names, idGrupo, geom_column) def preparePostGISToLoad(self, uri, categoria, layer_names, idGrupo, geom_column): idSubgrupo = qgis.utils.iface.legendInterface().addGroup(categoria, True, idGrupo) layer_names.sort(reverse=True) for layer_name in layer_names: split = layer_name.split('_') category = split[0] schema = category.split('.')[0] name = layer_name.replace(schema+'.', '') if category == categoria: sql = self.gen.loadLayerFromDatabase(layer_name) uri.setDataSource(schema, name, geom_column, sql, 'id') uri.disableSelectAtId(True) self.loadEDGVLayer(uri, name, 'postgres', idSubgrupo) def prepareSpatialiteToLoad(self, uri, categoria, layer_names, idGrupo, geom_column): idSubgrupo = qgis.utils.iface.legendInterface().addGroup(categoria, True, idGrupo) layer_names.sort(reverse=True) for layer_name in layer_names: split = layer_name.split('_') if self.dbVersion == '3.0' or self.dbVersion == '2.1.3': category = split[0]+'.'+split[1] else: category = split[0] if category == categoria: uri.setDataSource('', layer_name, geom_column) self.loadEDGVLayer(uri, layer_name, 'spatialite', idSubgrupo) def loadSpatialiteLayers(self, type, categories, layer_names): uri = QgsDataSourceURI() uri.setDatabase(self.filename) geom_column = 'GEOMETRY' if self.parentTreeNode is None: self.parentTreeNode = qgis.utils.iface.legendInterface(). addGroup(self.filename.split('.sqlite')[0].split('/')[-1], -1) if type == 'p': idGrupo = qgis.utils.iface.legendInterface(). addGroup("Ponto", True, self.parentTreeNode) for categoria in categories: self.prepareSpatialiteToLoad(uri, categoria, layer_names, idGrupo, geom_column) if type == 'l': idGrupo = qgis.utils.iface.legendInterface(). addGroup("Linha", True, self.parentTreeNode) for categoria in categories: self.prepareSpatialiteToLoad(uri, categoria, layer_names, idGrupo, geom_column) if type == 'a': idGrupo = qgis.utils.iface.legendInterface(). addGroup("Area", True, self.parentTreeNode) for categoria in categories: self.prepareSpatialiteToLoad(uri, categoria, layer_names, idGrupo, geom_column) def loadEDGVLayer(self, uri, layer_name, provider, idSubgrupo): vlayer = QgsVectorLayer(uri.uri(), layer_name, provider) vlayer.setCrs(self.crs) QgsMapLayerRegistry.instance().addMapLayer(vlayer) #added due to api changes if self.isSpatialite and (self.dbVersion == '3.0' or self.dbVersion == '2.1.3'): lyr = '_'.join(layer_name.replace('\r', '').split('_')[1::]) else: lyr = layer_name.replace('\r','') vlayerQml = os.path.join(self.qmlPath, lyr+'.qml') vlayer.loadNamedStyle(vlayerQml, False) QgsMapLayerRegistry.instance().addMapLayer(vlayer) qgis.utils.iface.legendInterface().moveLayer(vlayer, idSubgrupo) if not vlayer.isValid(): QgsMessageLog.logMessage(vlayer.error().summary(), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
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 Qgis2threejsDialog(QDialog): STYLE_MAX_COUNT = 3 def __init__(self, iface): QDialog.__init__(self, iface.mainWindow()) self.iface = iface self.apiChanged22 = False # not QgsApplication.prefixPath().startswith("C:/OSGeo4W") # QGis.QGIS_VERSION_INT >= 20200 # Set up the user interface from Designer. self.ui = ui = Ui_Qgis2threejsDialog() ui.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint) ui.lineEdit_OutputFilename.setPlaceholderText("[Temporary file]") ui.pushButton_Run.clicked.connect(self.run) ui.pushButton_Close.clicked.connect(self.reject) # DEM tab ui.toolButton_switchFocusMode.setVisible(False) ui.toolButton_PointTool.setVisible(False) ui.progressBar.setVisible(False) self.switchFocusMode(True) ui.toolButton_Browse.clicked.connect(self.browseClicked) ui.radioButton_Simple.toggled.connect(self.samplingModeChanged) ui.horizontalSlider_Resolution.valueChanged.connect(self.calculateResolution) ui.spinBox_Height.valueChanged.connect(self.updateQuads) ui.toolButton_switchFocusMode.clicked.connect(self.switchFocusModeClicked) ui.toolButton_PointTool.clicked.connect(self.startPointSelection) # Vector tab ui.treeWidget_VectorLayers.setHeaderLabel("Vector layers") self.initVectorStyleWidgets() ui.treeWidget_VectorLayers.currentItemChanged.connect(self.currentVectorLayerChanged) ui.treeWidget_VectorLayers.itemChanged.connect(self.vectorLayerItemChanged) ui.comboBox_ObjectType.currentIndexChanged.connect(self.objectTypeSelectionChanged) self.bar = None self.localBrowsingMode = True self.rb_quads = self.rb_point = None self.currentVectorLayer = None self.vectorPropertiesDict = {} self.objectTypeManager = ObjectTypeManager() # set map tool self.previousMapTool = None self.mapTool = RectangleMapTool(iface.mapCanvas()) self.connect(self.mapTool, SIGNAL("rectangleCreated()"), self.rectangleSelected) # self.mapTool = PointMapTool(iface.mapCanvas()) # QObject.connect(self.mapTool, SIGNAL("pointSelected()"), self.pointSelected) iface.mapCanvas().mapToolSet.connect(self.mapToolSet) self.startPointSelection() def exec_(self): ui = self.ui messages = [] # show message if crs unit is degrees mapSettings = self.iface.mapCanvas().mapSettings() if self.apiChanged22 else self.iface.mapCanvas().mapRenderer() if mapSettings.destinationCrs().mapUnits() in [QGis.Degrees]: self.showMessageBar("The unit of current CRS is degrees", "Terrain may not appear well.") # show message if there are no dem layer no_demlayer = ui.comboBox_DEMLayer.count() == 0 ui.pushButton_Run.setEnabled(not no_demlayer) if no_demlayer: self.showMessageBar("No DEM layer", "Load 1-band raster layer with GDAL provider.", QgsMessageBar.WARNING) return QDialog.exec_(self) def showMessageBar(self, title, text, level=QgsMessageBar.INFO): if self.bar is None: self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) ui = self.ui margins = ui.gridLayout.getContentsMargins() vl = ui.gridLayout.takeAt(0) ui.gridLayout.setContentsMargins(0,0,0,0) ui.gridLayout.addWidget(self.bar, 0, 0) ui.gridLayout.addItem(vl, 1, 0) ui.verticalLayout.setContentsMargins(margins[0], margins[1] / 2, margins[2], margins[3]) self.bar.pushMessage(title, text, level=level) def initDEMLayerList(self, layerId=None): # list 1 band raster layers self.ui.comboBox_DEMLayer.clear() for id, layer in QgsMapLayerRegistry().instance().mapLayers().items(): if layer.type() == QgsMapLayer.RasterLayer and layer.providerType() == "gdal" and layer.bandCount() == 1: self.ui.comboBox_DEMLayer.addItem(layer.name(), id) # select the last selected layer if layerId is not None: index = self.ui.comboBox_DEMLayer.findData(layerId) if index != -1: self.ui.comboBox_DEMLayer.setCurrentIndex(index) return index return -1 def initVectorLayerTree(self, vectorPropertiesDict): self.vectorPropertiesDict = vectorPropertiesDict tree = self.ui.treeWidget_VectorLayers tree.clear() # add vector layers into tree widget self.treeTopItems = topItems = {QGis.Point:QTreeWidgetItem(tree, ["Point"]), QGis.Line:QTreeWidgetItem(tree, ["Line"]), QGis.Polygon:QTreeWidgetItem(tree, ["Polygon"])} self.vlItems = {} for layer in self.iface.legendInterface().layers(): if layer.type() != QgsMapLayer.VectorLayer: continue geometry_type = layer.geometryType() if geometry_type in [QGis.Point, QGis.Line, QGis.Polygon]: self.vlItems[layer.id()] = item = QTreeWidgetItem(topItems[geometry_type], [layer.name()]) if layer.id() in self.vectorPropertiesDict: isVisible = self.vectorPropertiesDict[layer.id()]["visible"] else: isVisible = False #self.iface.legendInterface().isLayerVisible(layer) check_state = Qt.Checked if isVisible else Qt.Unchecked item.setData(0, Qt.CheckStateRole, check_state) item.setData(0, Qt.UserRole, layer.id()) #item.setDisabled(True) #item.setData(0, Qt.CheckStateRole, Qt.Unchecked) for item in topItems.values(): tree.expandItem(item) self.setVectorStylesEnabled(False) def initVectorStyleWidgets(self): self.colorWidget = StyleWidget(StyleWidget.COLOR) self.ui.verticalLayout_Styles.addWidget(self.colorWidget) self.heightWidget = StyleWidget(StyleWidget.HEIGHT) self.ui.verticalLayout_zCoordinate.addWidget(self.heightWidget) self.styleWidgets = [] for i in range(self.STYLE_MAX_COUNT): widget = StyleWidget() widget.setVisible(False) self.ui.verticalLayout_Styles.addWidget(widget) self.styleWidgets.append(widget) def currentVectorLayerChanged(self, currentItem, previousItem): # save properties of previous item if previousItem is not None: layerid = previousItem.data(0, Qt.UserRole) if layerid is not None: self.saveVectorProperties(layerid) layerid = currentItem.data(0, Qt.UserRole) if layerid is None: self.currentVectorLayer = None return self.currentVectorLayer = layer = QgsMapLayerRegistry().instance().mapLayer(layerid) if layer is None: return for i in range(self.STYLE_MAX_COUNT): self.styleWidgets[i].hide() obj_types = self.objectTypeManager.objectTypeNames(layer.geometryType()) ui = self.ui ui.comboBox_ObjectType.blockSignals(True) ui.comboBox_ObjectType.clear() ui.comboBox_ObjectType.addItems(obj_types) ui.comboBox_ObjectType.blockSignals(False) # set up property widgets self.objectTypeSelectionChanged() if layerid in self.vectorPropertiesDict: # restore properties self.restoreVectorProperties(layerid) self.setVectorStylesEnabled(currentItem.data(0, Qt.CheckStateRole) == Qt.Checked) def vectorLayerItemChanged(self, item, column): # update style form enablement currentItem = self.ui.treeWidget_VectorLayers.currentItem() if currentItem: self.setVectorStylesEnabled(currentItem.data(0, Qt.CheckStateRole) == Qt.Checked) def objectTypeSelectionChanged(self, idx=None): layer = self.currentVectorLayer try: ve = float(ui.lineEdit_zFactor.text()) except: ve = 1 mapTo3d = MapTo3D(self.iface.mapCanvas(), verticalExaggeration=ve) self.objectTypeManager.setupForm(self, mapTo3d, layer, layer.geometryType(), self.ui.comboBox_ObjectType.currentIndex()) def numericFields(self, layer): # get attributes of a sample feature and create numeric field name list numeric_fields = [] f = QgsFeature() layer.getFeatures().nextFeature(f) for field in f.fields(): isNumeric = False try: float(f.attribute(field.name())) isNumeric = True except: pass if isNumeric: numeric_fields.append(field.name()) return numeric_fields def setVectorStylesEnabled(self, enabled): self.ui.comboBox_ObjectType.setEnabled(enabled) self.ui.label_ObjectType.setEnabled(enabled) self.colorWidget.setEnabled(enabled) self.heightWidget.setEnabled(enabled) for i in range(self.STYLE_MAX_COUNT): self.styleWidgets[i].setEnabled(enabled) def saveVectorProperties(self, layerid): properties = {} layer = QgsMapLayerRegistry().instance().mapLayer(layerid) itemIndex = self.ui.comboBox_ObjectType.currentIndex() properties["itemindex"] = itemIndex properties["typeitem"] = self.objectTypeManager.objectTypeItem(layer.geometryType(), itemIndex) properties["visible"] = self.vlItems[self.currentVectorLayer.id()].data(0, Qt.CheckStateRole) == Qt.Checked properties["color"] = self.colorWidget.values() properties["height"] = self.heightWidget.values() for i in range(self.STYLE_MAX_COUNT): if self.styleWidgets[i].isVisible(): properties[i] = self.styleWidgets[i].values() self.vectorPropertiesDict[layerid] = properties def restoreVectorProperties(self, layerid): properties = self.vectorPropertiesDict[layerid] self.ui.comboBox_ObjectType.setCurrentIndex(properties["itemindex"]) self.colorWidget.setValues(properties["color"]) self.heightWidget.setValues(properties["height"]) for i in range(self.STYLE_MAX_COUNT): if i in properties: self.styleWidgets[i].setValues(properties[i]) def calculateResolution(self, v=None): extent = self.iface.mapCanvas().extent() renderer = self.iface.mapCanvas().mapRenderer() size = 100 * self.ui.horizontalSlider_Resolution.value() self.ui.label_Resolution.setText("about {0} x {0} px".format(size)) # calculate resolution and size width, height = renderer.width(), renderer.height() s = (size * size / float(width * height)) ** 0.5 if s < 1: width = int(width * s) height = int(height * s) xres = extent.width() / width yres = extent.height() / height self.ui.lineEdit_HRes.setText(str(xres)) self.ui.lineEdit_VRes.setText(str(yres)) self.ui.lineEdit_Width.setText(str(width + 1)) self.ui.lineEdit_Height.setText(str(height + 1)) def progress(self, percentage): self.ui.progressBar.setValue(percentage) self.ui.progressBar.setVisible(percentage != 100) def run(self): ui = self.ui filename = ui.lineEdit_OutputFilename.text() # ""=Temporary file if filename != "" and QFileInfo(filename).exists() and QMessageBox.question(None, "Qgis2threejs", "Output file already exists. Overwrite it?", QMessageBox.Ok | QMessageBox.Cancel) != QMessageBox.Ok: return self.endPointSelection() item = ui.treeWidget_VectorLayers.currentItem() if item: self.saveVectorProperties(item.data(0, Qt.UserRole)) ui.pushButton_Run.setEnabled(False) self.progress(0) canvas = self.iface.mapCanvas() htmlfilename = ui.lineEdit_OutputFilename.text() demlayerid = ui.comboBox_DEMLayer.itemData(ui.comboBox_DEMLayer.currentIndex()) mapTo3d = MapTo3D(canvas, verticalExaggeration=float(ui.lineEdit_zFactor.text())) if self.ui.radioButton_Simple.isChecked(): dem_width = int(ui.lineEdit_Width.text()) dem_height = int(ui.lineEdit_Height.text()) context = OutputContext(mapTo3d, canvas, demlayerid, self.vectorPropertiesDict, self.objectTypeManager, self.localBrowsingMode, dem_width, dem_height) htmlfilename = runSimple(htmlfilename, context, self.progress) else: context = OutputContext(mapTo3d, canvas, demlayerid, self.vectorPropertiesDict, self.objectTypeManager, self.localBrowsingMode) htmlfilename = runAdvanced(htmlfilename, context, self, self.progress) self.progress(100) ui.pushButton_Run.setEnabled(True) if htmlfilename is None: return self.clearRubberBands() if not tools.openHTMLFile(htmlfilename): return QDialog.accept(self) def reject(self): self.endPointSelection() self.clearRubberBands() QDialog.reject(self) def startPointSelection(self): canvas = self.iface.mapCanvas() self.previousMapTool = canvas.mapTool() canvas.setMapTool(self.mapTool) self.ui.toolButton_PointTool.setVisible(False) def endPointSelection(self): self.mapTool.reset() self.iface.mapCanvas().setMapTool(self.previousMapTool) def rectangleSelected(self): ui = self.ui ui.radioButton_Advanced.setChecked(True) rect = self.mapTool.rectangle() toRect = rect.width() and rect.height() self.switchFocusMode(toRect) ui.lineEdit_xmin.setText(str(rect.xMinimum())) ui.lineEdit_ymin.setText(str(rect.yMinimum())) ui.lineEdit_xmax.setText(str(rect.xMaximum())) ui.lineEdit_ymax.setText(str(rect.yMaximum())) quadtree = QuadTree(self.iface.mapCanvas().extent()) quadtree.buildTreeByRect(rect, self.ui.spinBox_Height.value()) self.createRubberBands(quadtree.quads(), rect.center()) self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) def pointSelected(self): # set values of controls self.ui.lineEdit_CenterX.setText(str(self.mapTool.point.x())) self.ui.lineEdit_CenterY.setText(str(self.mapTool.point.y())) self.ui.radioButton_Advanced.setChecked(True) quadtree = QuadTree(self.iface.mapCanvas().extent(), self.mapTool.point, self.ui.spinBox_Height.value()) self.createRubberBands(quadtree.quads(), self.mapTool.point) self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) def mapToolSet(self, mapTool): if mapTool != self.mapTool: self.ui.toolButton_PointTool.setVisible(True) def createQuadTree(self): ui = self.ui try: c = map(float, [ui.lineEdit_xmin.text(), ui.lineEdit_ymin.text(), ui.lineEdit_xmax.text(), ui.lineEdit_ymax.text()]) except: return None quadtree = QuadTree(self.iface.mapCanvas().extent()) quadtree.buildTreeByRect(QgsRectangle(c[0], c[1], c[2], c[3]), ui.spinBox_Height.value()) return quadtree def createRubberBands(self, quads, point=None): self.clearRubberBands() # create quads with rubber band self.rb_quads = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rb_quads.setColor(Qt.blue) self.rb_quads.setWidth(1) for quad in quads: points = [] extent = quad.extent points.append(QgsPoint(extent.xMinimum(), extent.yMinimum())) points.append(QgsPoint(extent.xMinimum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMinimum())) self.rb_quads.addGeometry(QgsGeometry.fromPolygon([points]), None) self.log(extent.toString()) self.log("Quad count: %d" % len(quads)) # create a point with rubber band if point: self.rb_point = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb_point.setColor(Qt.red) self.rb_point.addPoint(point) def clearRubberBands(self): # clear quads and point if self.rb_quads: self.iface.mapCanvas().scene().removeItem(self.rb_quads) self.rb_quads = None if self.rb_point: self.iface.mapCanvas().scene().removeItem(self.rb_point) self.rb_point = None def browseClicked(self): directory = self.ui.lineEdit_OutputFilename.text() if directory == "": directory = QDir.homePath() filename = QFileDialog.getSaveFileName(self, self.tr("Output filename"), directory, "HTML file (*.html *.htm)", options=QFileDialog.DontConfirmOverwrite) if filename != "": self.ui.lineEdit_OutputFilename.setText(filename) def samplingModeChanged(self): ui = self.ui isSimpleMode = ui.radioButton_Simple.isChecked() simple_widgets = [ui.horizontalSlider_Resolution, ui.lineEdit_Width, ui.lineEdit_Height, ui.lineEdit_HRes, ui.lineEdit_VRes] for w in simple_widgets: w.setEnabled(isSimpleMode) isAdvancedMode = not isSimpleMode advanced_widgets = [ui.spinBox_Height, ui.lineEdit_xmin, ui.lineEdit_ymin, ui.lineEdit_xmax, ui.lineEdit_ymax, ui.toolButton_switchFocusMode] for w in advanced_widgets: w.setEnabled(isAdvancedMode) def updateQuads(self, v=None): quadtree = self.createQuadTree() if quadtree: self.createRubberBands(quadtree.quads(), quadtree.focusRect.center()) else: self.clearRubberBands() def switchFocusModeClicked(self): self.switchFocusMode(not self.ui.label_xmin.isVisible()) def switchFocusMode(self, toRect): ui = self.ui toPoint = not toRect ui.label_xmin.setVisible(toRect) ui.label_ymin.setVisible(toRect) ui.lineEdit_xmin.setVisible(toRect) ui.lineEdit_ymin.setVisible(toRect) suffix = "max" if toRect else "" ui.label_xmax.setText("x" + suffix) ui.label_ymax.setText("y" + suffix) mode = "point" if toRect else "rectangle" ui.toolButton_switchFocusMode.setText("To " + mode + " selection") selection = "area" if toRect else "point" action = "Stroke a rectangle" if toRect else "Click" ui.label_Focus.setText("Focus {0} ({1} on map canvas to set values)".format(selection, action)) def log(self, msg): if debug_mode: qDebug(msg)
class OpenSavedSearchDialog(BASE, WIDGET): def __init__(self): super(OpenSavedSearchDialog, self).__init__(iface.mainWindow()) self.saved_search = None self.setupUi(self) self.bar = QgsMessageBar() self.layout().insertWidget(0, self.bar) self.populate_saved_searches() self.comboSavedSearch.currentIndexChanged.connect( self.saved_search_selected) if self.comboSavedSearch.count(): self.saved_search_selected(0) self.btnLoad.setEnabled(bool(self.comboSavedSearch.count())) self.btnLoad.clicked.connect(self.loadSearch) self.btnCancel.clicked.connect(self.reject) self.btnDelete.clicked.connect(self.delete_search) @waitcursor def populate_saved_searches(self): self.comboSavedSearch.blockSignals(True) self.comboSavedSearch.clear() res = PlanetClient.getInstance().get_searches().get() for search in res["searches"]: self.comboSavedSearch.addItem(search["name"], search) self.comboSavedSearch.blockSignals(False) def saved_search_selected(self, idx): request = self.comboSavedSearch.currentData() if request: analytics_track(SAVED_SEARCH_ACCESSED) self.set_from_request(request) else: self.txtFilters.setPlainText("") self.labelDateRange.setText("---") def delete_search(self): request = self.comboSavedSearch.currentData() if request: PlanetClient.getInstance().delete_search(request["id"]) self.comboSavedSearch.removeItem( self.comboSavedSearch.currentIndex()) self.bar.pushMessage("Delete search", "Search was correctly deleted", Qgis.Success, 5) else: self.bar.pushMessage("Delete search", "No search has been selected", Qgis.Warning, 5) def update_legacy_search(self): request = self.comboSavedSearch.currentData() cleared_request = {} cleared_request["filter"] = request["filter"] cleared_request["item_types"] = list( set(t if t not in ["PSScene3Band", "PSScene4Band"] else "PSScene" for t in request["item_types"])) if "PSScene4Band" in request[ "item_types"] or "PSScene3Band" in request["item_types"]: if "PSScene3Band" in request["item_types"]: assets = PlanetClient.getInstance( ).psscene_asset_types_for_nbands(3) else: assets = PlanetClient.getInstance( ).psscene_asset_types_for_nbands(4) psscene_filter = { "config": [ { "config": assets, "type": "AssetFilter" }, { "config": ["PSScene"], "type": "StringInFilter", "field_name": "item_type", }, ], "type": "AndFilter", } cleared_request["filter"] = { "config": [psscene_filter, request["filter"]], "type": "AndFilter", } cleared_request["name"] = request["name"] PlanetClient.getInstance().update_search(cleared_request, request["id"]) return cleared_request def check_for_legacy_request(self, request): sources = request["item_types"] return "PSScene3Band" in sources or "PSScene4Band" in sources def set_from_request(self, request): filters = filters_from_request(request, "acquired") if filters: tokens = [] gte = filters[0]["config"].get("gte") if gte is not None: tokens.append( QDateTime.fromString(gte, Qt.ISODate).date().toString()) else: tokens.append("---") lte = filters[0]["config"].get("lte") if lte is not None: tokens.append( QDateTime.fromString(lte, Qt.ISODate).date().toString()) else: tokens.append("---") self.labelDateRange.setText(" / ".join(tokens)) self.txtFilters.setPlainText(filters_as_text_from_request(request)) self.check_for_legacy_request(request) def loadSearch(self): request = self.comboSavedSearch.currentData() if request: if self.check_for_legacy_request(request): dlg = LegacyWarningDialog(request, self) ret = dlg.exec() if ret == dlg.Accepted: self.saved_search = self.update_legacy_search() else: self.saved_search = request else: self.saved_search = request self.accept() else: self.bar.pushMessage("Saved search", "No search has been selected", Qgis.Warning, 5)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass self.mToolbar.setIconSize(iface.iconSize()) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) #self.mActionExportPython.setIcon( # QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = QgsApplication.processingRegistry().createAlgorithmById(text) if alg is not None: self._addAlgorithm(alg, event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] mimeData = None if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.id()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.textChanged) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.fillInputsTree() self.fillTreeUsingProviders() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self.tr('There are unsaved changes in this model. Do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage("", "Model doesn't contain any algorithm and/or parameter and can't be executed", level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as SVG", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", "Model was correctly exported as python script", level=Qgis.Success, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format(str(sys.exc_info()[1]))) else: QMessageBox.warning(self, self.tr("Can't save model"), QCoreApplication.translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option.")) ) return self.update_model.emit() self.bar.pushMessage("", "Model was correctly saved", level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical(self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([i.position().x() for i in list(self.model.parameterComponents().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def textChanged(self): text = self.searchBox.text().strip(' ').lower() for item in list(self.disabledProviderItems.values()): item.setHidden(True) self._filterItem(self.algorithmTree.invisibleRootItem(), [t for t in text.split(' ') if t]) if text: self.algorithmTree.expandAll() self.disabledWithMatchingAlgs = [] for provider in QgsApplication.processingRegistry().providers(): if not provider.isActive(): for alg in provider.algorithms(): if text in alg.name(): self.disabledWithMatchingAlgs.append(provider.id()) break else: self.algorithmTree.collapseAll() def _filterItem(self, item, text): if (item.childCount() > 0): show = False for i in range(item.childCount()): child = item.child(i) showChild = self._filterItem(child, text) show = (showChild or show) and item not in list(self.disabledProviderItems.values()) item.setHidden(not show) return show elif isinstance(item, (TreeAlgorithmItem, TreeActionItem)): # hide if every part of text is not contained somewhere in either the item text or item user role item_text = [item.text(0).lower(), item.data(0, ModelerDialog.NAME_ROLE).lower()] if isinstance(item, TreeAlgorithmItem): item_text.append(item.alg.id()) item_text.extend(item.data(0, ModelerDialog.TAG_ROLE)) hide = bool(text) and not all( any(part in t for t in item_text) for part in text) item.setHidden(hide) return not hide else: item.setHidden(True) return False def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in sorted(ModelerParameterDefinitionDialog.paramTypes): paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, ModelerParameterDefinitionDialog.inputTooltip(paramType)) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = QgsApplication.processingRegistry().createAlgorithmById(item.alg.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = None try: dlg = alg.getCustomModelerParametersDialog(self.model) except: pass if not dlg: dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition(alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([alg.position().x() for alg in list(self.model.childAlgorithms().values())]) maxY = max([alg.position().y() for alg in list(self.model.childAlgorithms().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillTreeUsingProviders(self): self.algorithmTree.clear() self.disabledProviderItems = {} # TODO - replace with proper model for toolbox! # first add qgis/native providers, since they create top level groups for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): self.addAlgorithmsFromProvider(provider, self.algorithmTree.invisibleRootItem()) else: continue self.algorithmTree.sortItems(0, Qt.AscendingOrder) for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): # already added continue else: providerItem = TreeProviderItem(provider, self.algorithmTree, self) if not provider.isActive(): providerItem.setHidden(True) self.disabledProviderItems[provider.id()] = providerItem # insert non-native providers at end of tree, alphabetically for i in range(self.algorithmTree.invisibleRootItem().childCount()): child = self.algorithmTree.invisibleRootItem().child(i) if isinstance(child, TreeProviderItem): if child.text(0) > providerItem.text(0): break self.algorithmTree.insertTopLevelItem(i + 1, providerItem) def addAlgorithmsFromProvider(self, provider, parent): groups = {} count = 0 algs = provider.algorithms() active = provider.isActive() # Add algorithms for alg in algs: if alg.flags() & QgsProcessingAlgorithm.FlagHideFromModeler: continue groupItem = None if alg.group() in groups: groupItem = groups[alg.group()] else: # check if group already exists for i in range(parent.childCount()): if parent.child(i).text(0) == alg.group(): groupItem = parent.child(i) groups[alg.group()] = groupItem break if not groupItem: groupItem = TreeGroupItem(alg.group()) if not active: groupItem.setInactive() if provider.id() in ('qgis', 'native', '3d'): groupItem.setIcon(0, provider.icon()) groups[alg.group()] = groupItem algItem = TreeAlgorithmItem(alg) if not active: algItem.setForeground(0, Qt.darkGray) groupItem.addChild(algItem) count += 1 text = provider.name() if not provider.id() in ('qgis', 'native', '3d'): if not active: def activateProvider(): self.activateProvider(provider.id()) label = QLabel(text + " <a href='%s'>Activate</a>") label.setStyleSheet("QLabel {background-color: white; color: grey;}") label.linkActivated.connect(activateProvider) self.algorithmTree.setItemWidget(parent, 0, label) else: parent.setText(0, text) for group, groupItem in sorted(groups.items(), key=operator.itemgetter(1)): parent.addChild(groupItem) if not provider.id() in ('qgis', 'native', '3d'): parent.setHidden(parent.childCount() == 0)
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFontDatabase.systemFont(QFontDatabase.FixedFont) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info" ) # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() font = QFontDatabase.systemFont(QFontDatabase.FixedFont) loadFont = self.settings.value("pythonConsole/fontfamilytext") if loadFont: font.setFamily(loadFont) fontSize = self.settings.value("pythonConsole/fontsize", type=int) if fontSize: font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor( QColor( self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor( QColor( self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor( QColor( self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor( QColor( self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor( QColor( self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor( QColor( self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor( QColor( self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor( self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/mIconRunConsole.svg") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon( "console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon( "console/iconSettingsConsole.png") menu.addAction( iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction( iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction( iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
class ModelerParametersDialog(QDialog): ENTER_NAME = '[Enter name if this is a final result]' NOT_SELECTED = '[Not selected]' USE_MIN_COVERING_EXTENT = '[Use min covering extent]' def __init__(self, alg, model, algName=None): QDialog.__init__(self) self.setModal(True) # The algorithm to define in this dialog. It is an instance of GeoAlgorithm self._alg = alg # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class self.alg = None # The model this algorithm is going to be added to self.model = model # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self.childId = algName self.setupUi() self.params = None settings = QgsSettings() self.restoreGeometry(settings.value("/Processing/modelParametersDialogGeometry", QByteArray())) def closeEvent(self, event): settings = QgsSettings() settings.setValue("/Processing/modelParametersDialogGeometry", self.saveGeometry()) super(ModelerParametersDialog, self).closeEvent(event) def setupUi(self): self.labels = {} self.widgets = {} self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.displayName()) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue desc = param.description() if isinstance(param, QgsProcessingParameterExtent): desc += self.tr('(xmin, xmax, ymin, ymax)') if isinstance(param, QgsProcessingParameterPoint): desc += self.tr('(x, y)') if param.flags() & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') label = QLabel(desc) self.labels[param.name()] = label wrapper = WidgetWrapperFactory.create_wrapper(param, self) self.wrappers[param.name()] = wrapper widget = wrapper.widget if widget is not None: self.valueItems[param.name()] = widget tooltip = param.description() label.setToolTip(tooltip) widget.setToolTip(tooltip) if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.widgets[param.name()] = widget self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for dest in self._alg.destinationParameterDefinitions(): if dest.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if isinstance(dest, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination)): label = QLabel(dest.description()) item = QgsFilterLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[dest.name()] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.displayName()) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.verticalLayout2.addWidget(self.scrollArea) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.helpRequested.connect(self.openHelp) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def getAvailableDependencies(self): # spellok if self.childId is None: dependent = [] else: dependent = list(self.model.dependentChildAlgorithms(self.childId)) dependent.append(self.childId) opts = [] for alg in list(self.model.childAlgorithms().values()): if alg.childId() not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description() for alg in self.getAvailableDependencies()]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.labels[param.name()].setVisible(self.showAdvanced) self.widgets[param.name()].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]): # upgrade paramType to list if paramType is None: paramType = [] elif not isinstance(paramType, list): paramType = [paramType] if outTypes is None: outTypes = [] elif not isinstance(outTypes, list): outTypes = [outTypes] return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if issubclass(p, QgsProcessingParameterDefinition)], [o.typeName() for o in outTypes if issubclass(o, QgsProcessingOutputDefinition)], dataTypes) def resolveValueDescription(self, value): if isinstance(value, QgsProcessingModelChildParameterSource): if value.source() == QgsProcessingModelChildParameterSource.StaticValue: return value.staticValue() elif value.source() == QgsProcessingModelChildParameterSource.ModelParameter: return self.model.parameterDefinition(value.parameterName()).description() elif value.source() == QgsProcessingModelChildParameterSource.ChildOutput: alg = self.model.childAlgorithm(value.outputChildId()) return self.tr("'{0}' from algorithm '{1}'").format( alg.algorithm().outputDefinition(value.outputName()).description(), alg.description()) return value def setPreviousValues(self): if self.childId is not None: alg = self.model.childAlgorithm(self.childId) self.descriptionBox.setText(alg.description()) for param in alg.algorithm().parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue value = None if param.name() in alg.parameterSources(): value = alg.parameterSources()[param.name()] if isinstance(value, list) and len(value) == 1: value = value[0] elif isinstance(value, list) and len(value) == 0: value = None if value is None: value = param.defaultValue() if isinstance(value, QgsProcessingModelChildParameterSource) and value.source() == QgsProcessingModelChildParameterSource.StaticValue: value = value.staticValue() self.wrappers[param.name()].setValue(value) for name, out in list(alg.modelOutputs().items()): if out.childOutputName() in self.valueItems: self.valueItems[out.childOutputName()].setText(out.name()) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.childId() in alg.dependencies(): selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = QgsProcessingModelChildAlgorithm(self._alg.id()) if not self.childId: alg.generateChildId(self.model) else: alg.setChildId(self.childId) alg.setDescription(self.descriptionBox.text()) for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue val = self.wrappers[param.name()].value() if (isinstance(val, QgsProcessingModelChildParameterSource) and val.source() == QgsProcessingModelChildParameterSource.StaticValue and not param.checkValueIsAcceptable( val.staticValue())) \ or (val is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional): self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description(), level=QgsMessageBar.WARNING) return None if val is None: continue elif isinstance(val, QgsProcessingModelChildParameterSource): alg.addParameterSources(param.name(), [val]) elif isinstance(val, list): alg.addParameterSources(param.name(), val) else: alg.addParameterSources(param.name(), [QgsProcessingModelChildParameterSource.fromStaticValue(val)]) outputs = {} for dest in self._alg.destinationParameterDefinitions(): if not dest.flags() & QgsProcessingParameterDefinition.FlagHidden: name = str(self.valueItems[dest.name()].text()) if name.strip() != '' and name != ModelerParametersDialog.ENTER_NAME: output = QgsProcessingModelOutput(name, name) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[name] = output alg.setModelOutputs(outputs) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok dep_ids = [] for selected in selectedOptions: dep_ids.append(availableDependencies[selected].childId()) # spellok alg.setDependencies(dep_ids) try: self._alg.processBeforeAddingToModeler(alg, self.model) except: pass return alg def okPressed(self): self.alg = self.createAlgorithm() if self.alg is not None: self.close() def cancelPressed(self): self.alg = None self.close() def openHelp(self): algHelp = self._alg.help() if algHelp is not None: webbrowser.open(algHelp)
class BatchAlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setWindowTitle( self.tr('Batch Processing - {0}').format(self.alg.displayName())) self.setMainWidget(BatchPanel(self, self.alg)) self.textShortHelp.setVisible(False) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) def accept(self): alg_parameters = [] load = [] feedback = self.createFeedback() context = dataobjects.createContext(feedback) for row in range(self.mainWidget.tblParameters.rowCount()): col = 0 parameters = {} for param in self.alg.parameterDefinitions(): if param.flags( ) & QgsProcessingParameterDefinition.FlagHidden or param.isDestination( ): continue wrapper = self.mainWidget.wrappers[row][col] parameters[param.name()] = wrapper.value() if not param.checkValueIsAcceptable(wrapper.value(), context): self.bar.pushMessage( "", self.tr( 'Wrong or missing parameter value: {0} (row {1})'). format(param.description(), row + 1), level=QgsMessageBar.WARNING, duration=5) return col += 1 count_visible_outputs = 0 for out in self.alg.destinationParameterDefinitions(): if out.flags() & QgsProcessingParameterDefinition.FlagHidden: continue count_visible_outputs += 1 widget = self.mainWidget.tblParameters.cellWidget(row, col) text = widget.getValue() if param.checkValueIsAcceptable(text, context): if isinstance(out, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink)): # load rasters and sinks on completion parameters[ out.name()] = QgsProcessingOutputLayerDefinition( text, context.project()) else: parameters[out.name()] = text col += 1 else: self.bar.pushMessage( "", self.tr('Wrong or missing output value: {0} (row {1})' ).format(out.description(), row + 1), level=QgsMessageBar.WARNING, duration=5) return alg_parameters.append(parameters) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.mainWidget.setEnabled(False) self.buttonCancel.setEnabled(True) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass start_time = time.time() algorithm_results = [] for count, parameters in enumerate(alg_parameters): if feedback.isCanceled(): break self.setText( self.tr('\nProcessing algorithm {0}/{1}...').format( count + 1, len(alg_parameters))) 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('') alg_start_time = time.time() ret, results = execute(self.alg, parameters, context, feedback) if ret: self.setInfo( self.tr('Algorithm {0} correctly executed...').format( self.alg.displayName()), escape_html=False) feedback.setProgress(100) feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'.format( time.time() - alg_start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(results)) feedback.pushInfo('') algorithm_results.append(results) else: break feedback.pushInfo( self.tr('Batch execution completed in {0:0.2f} seconds'.format( time.time() - start_time))) handleAlgorithmResults(self.alg, context, feedback, False) self.finish(algorithm_results) self.buttonCancel.setEnabled(False) def finish(self, algorithm_results): for count, results in enumerate(algorithm_results): self.loadHTMLResults(results, count) self.createSummaryTable(algorithm_results) QApplication.restoreOverrideCursor() self.mainWidget.setEnabled(True) QMessageBox.information(self, self.tr('Batch processing'), self.tr('Batch processing completed')) def loadHTMLResults(self, results, num): for out in self.alg.outputDefinitions(): if isinstance(out, QgsProcessingOutputHtml) and out.name( ) in results and results[out.name()]: resultsList.addResult(icon=self.alg.icon(), name='{} [{}]'.format( out.description(), num), result=results[out.name()]) def createSummaryTable(self, algorithm_results): createTable = False for out in self.alg.outputDefinitions(): if isinstance( out, (QgsProcessingOutputNumber, QgsProcessingOutputString)): createTable = True break if not createTable: return outputFile = getTempFilename('html') with codecs.open(outputFile, 'w', encoding='utf-8') as f: for res in algorithm_results: f.write('<hr>\n') for out in self.alg.outputDefinitions(): if isinstance( out, (QgsProcessingOutputNumber, QgsProcessingOutputString)) and out.name() in res: f.write('<p>{}: {}</p>\n'.format( out.description(), res[out.name()])) f.write('<hr>\n') resultsList.addResult(self.alg.icon(), '{} [summary]'.format(self.alg.name()), outputFile)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super().__init__(None) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi(self) self._variables_scope = None # LOTS of bug reports when we include the dock creation in the UI file # see e.g. #16428, #19068 # So just roll it all by hand......! self.propertiesDock = QgsDockWidget(self) self.propertiesDock.setFeatures( QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.propertiesDock.setObjectName("propertiesDock") propertiesDockContents = QWidget() self.verticalDockLayout_1 = QVBoxLayout(propertiesDockContents) self.verticalDockLayout_1.setContentsMargins(0, 0, 0, 0) self.verticalDockLayout_1.setSpacing(0) self.scrollArea_1 = QgsScrollArea(propertiesDockContents) sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.scrollArea_1.sizePolicy().hasHeightForWidth()) self.scrollArea_1.setSizePolicy(sizePolicy) self.scrollArea_1.setFocusPolicy(Qt.WheelFocus) self.scrollArea_1.setFrameShape(QFrame.NoFrame) self.scrollArea_1.setFrameShadow(QFrame.Plain) self.scrollArea_1.setWidgetResizable(True) self.scrollAreaWidgetContents_1 = QWidget() self.gridLayout = QGridLayout(self.scrollAreaWidgetContents_1) self.gridLayout.setContentsMargins(6, 6, 6, 6) self.gridLayout.setSpacing(4) self.label_1 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1) self.textName = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textName, 0, 1, 1, 1) self.label_2 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) self.textGroup = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textGroup, 1, 1, 1, 1) self.label_1.setText(self.tr("Name")) self.textName.setToolTip(self.tr("Enter model name here")) self.label_2.setText(self.tr("Group")) self.textGroup.setToolTip(self.tr("Enter group name here")) self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1) self.verticalDockLayout_1.addWidget(self.scrollArea_1) self.propertiesDock.setWidget(propertiesDockContents) self.propertiesDock.setWindowTitle(self.tr("Model Properties")) self.inputsDock = QgsDockWidget(self) self.inputsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.inputsDock.setObjectName("inputsDock") self.inputsDockContents = QWidget() self.verticalLayout_3 = QVBoxLayout(self.inputsDockContents) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.scrollArea_2 = QgsScrollArea(self.inputsDockContents) sizePolicy.setHeightForWidth(self.scrollArea_2.sizePolicy().hasHeightForWidth()) self.scrollArea_2.setSizePolicy(sizePolicy) self.scrollArea_2.setFocusPolicy(Qt.WheelFocus) self.scrollArea_2.setFrameShape(QFrame.NoFrame) self.scrollArea_2.setFrameShadow(QFrame.Plain) self.scrollArea_2.setWidgetResizable(True) self.scrollAreaWidgetContents_2 = QWidget() self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents_2) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setSpacing(0) self.inputsTree = QTreeWidget(self.scrollAreaWidgetContents_2) self.inputsTree.setAlternatingRowColors(True) self.inputsTree.header().setVisible(False) self.verticalLayout.addWidget(self.inputsTree) self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) self.verticalLayout_3.addWidget(self.scrollArea_2) self.inputsDock.setWidget(self.inputsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.inputsDock) self.inputsDock.setWindowTitle(self.tr("Inputs")) self.algorithmsDock = QgsDockWidget(self) self.algorithmsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.algorithmsDock.setObjectName("algorithmsDock") self.algorithmsDockContents = QWidget() self.verticalLayout_4 = QVBoxLayout(self.algorithmsDockContents) self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.scrollArea_3 = QgsScrollArea(self.algorithmsDockContents) sizePolicy.setHeightForWidth(self.scrollArea_3.sizePolicy().hasHeightForWidth()) self.scrollArea_3.setSizePolicy(sizePolicy) self.scrollArea_3.setFocusPolicy(Qt.WheelFocus) self.scrollArea_3.setFrameShape(QFrame.NoFrame) self.scrollArea_3.setFrameShadow(QFrame.Plain) self.scrollArea_3.setWidgetResizable(True) self.scrollAreaWidgetContents_3 = QWidget() self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents_3) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setSpacing(4) self.searchBox = QgsFilterLineEdit(self.scrollAreaWidgetContents_3) self.verticalLayout_2.addWidget(self.searchBox) self.algorithmTree = QgsProcessingToolboxTreeView(None, QgsApplication.processingRegistry()) self.algorithmTree.setAlternatingRowColors(True) self.algorithmTree.header().setVisible(False) self.verticalLayout_2.addWidget(self.algorithmTree) self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3) self.verticalLayout_4.addWidget(self.scrollArea_3) self.algorithmsDock.setWidget(self.algorithmsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.algorithmsDock) self.algorithmsDock.setWindowTitle(self.tr("Algorithms")) self.searchBox.setToolTip(self.tr("Enter algorithm name to filter list")) self.searchBox.setShowSearchIcon(True) self.variables_dock = QgsDockWidget(self) self.variables_dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.variables_dock.setObjectName("variablesDock") self.variables_dock_contents = QWidget() vl_v = QVBoxLayout() vl_v.setContentsMargins(0, 0, 0, 0) self.variables_editor = QgsVariableEditorWidget() vl_v.addWidget(self.variables_editor) self.variables_dock_contents.setLayout(vl_v) self.variables_dock.setWidget(self.variables_dock_contents) self.addDockWidget(Qt.DockWidgetArea(1), self.variables_dock) self.variables_dock.setWindowTitle(self.tr("Variables")) self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock) self.tabifyDockWidget(self.propertiesDock, self.variables_dock) self.variables_editor.scopeChanged.connect(self.variables_changed) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass if iface is not None: self.mToolbar.setIconSize(iface.iconSize()) self.setStyleSheet(iface.mainWindow().styleSheet()) self.toolbutton_export_to_script = QToolButton() self.toolbutton_export_to_script.setPopupMode(QToolButton.InstantPopup) self.export_to_script_algorithm_action = QAction(QCoreApplication.translate('ModelerDialog', 'Export as Script Algorithm…')) self.toolbutton_export_to_script.addActions([self.export_to_script_algorithm_action]) self.mToolbar.insertWidget(self.mActionExportImage, self.toolbutton_export_to_script) self.export_to_script_algorithm_action.triggered.connect(self.export_as_script_algorithm) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionSaveInProject.setIcon( QgsApplication.getThemeIcon('/mAddToProject.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) self.toolbutton_export_to_script.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) def _dragEnterEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): def alg_dropped(algorithm_id, pos): alg = QgsApplication.processingRegistry().createAlgorithmById(algorithm_id) if alg is not None: self._addAlgorithm(alg, pos) else: assert False, algorithm_id def input_dropped(id, pos): if id in [param.id() for param in QgsApplication.instance().processingRegistry().parameterTypes()]: self.addInputOfType(itemId, pos) if event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): data = event.mimeData().data('application/x-vnd.qgis.qgis.algorithmid') stream = QDataStream(data, QIODevice.ReadOnly) algorithm_id = stream.readQString() QTimer.singleShot(0, lambda id=algorithm_id, pos=self.view.mapToScene(event.pos()): alg_dropped(id, pos)) event.accept() elif event.mimeData().hasText(): itemId = event.mimeData().text() QTimer.singleShot(0, lambda id=itemId, pos=self.view.mapToScene(event.pos()): input_dropped(id, pos)) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].data(0, Qt.UserRole) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) self.algorithms_model = ModelerToolboxModel(self, QgsApplication.processingRegistry()) self.algorithmTree.setToolboxProxyModel(self.algorithms_model) self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) filters = QgsProcessingToolboxProxyModel.Filters(QgsProcessingToolboxProxyModel.FilterModeler) if ProcessingConfig.getSetting(ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES): filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues self.algorithmTree.setFilters(filters) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.algorithmTree.setFilterString) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionSaveInProject.triggered.connect(self.saveInProject) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.update_variables_gui() self.fillInputsTree() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self.tr('There are unsaved changes in this model. Do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def update_variables_gui(self): variables_scope = QgsExpressionContextScope(self.tr('Model Variables')) for k, v in self.model.variables().items(): variables_scope.setVariable(k, v) variables_context = QgsExpressionContext() variables_context.appendScope(variables_scope) self.variables_editor.setContext(variables_context) self.variables_editor.setEditableScopeIndex(0) def variables_changed(self): self.model.setVariables(self.variables_editor.variablesInActiveScope()) def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage("", self.tr("Model doesn't contain any algorithm and/or parameter and can't be executed"), level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model.create(), parent=iface.mainWindow()) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def saveInProject(self): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) self.model.setSourceFilePath(None) project_provider = QgsApplication.processingRegistry().providerById(PROJECT_PROVIDER_ID) project_provider.add_model(self.model) self.update_model.emit() self.bar.pushMessage("", self.tr("Model was saved inside current project"), level=Qgis.Success, duration=5) self.hasChanged = False QgsProject.instance().setDirty(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", self.tr("Successfully exported model as image to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Successfully exported model as PDF to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Successfully exported model as SVG to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Processing scripts (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", self.tr("Successfully exported model as python script to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) def can_save(self): """ Tests whether a model can be saved, or if it is not yet valid :return: bool """ if str(self.textName.text()).strip() == '': self.bar.pushWarning( "", self.tr('Please a enter model name before saving') ) return False return True def saveModel(self, saveAs): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format(str(sys.exc_info()[1]))) else: QMessageBox.warning(self, self.tr("Can't save model"), QCoreApplication.translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option.")) ) return self.update_model.emit() if saveAs: self.bar.pushMessage("", self.tr("Model was correctly saved to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) else: self.bar.pushMessage("", self.tr("Model was correctly saved"), level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.update_variables_gui() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical(self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() param = item.data(0, Qt.UserRole) self.addInputOfType(param) def addInputOfType(self, paramType, pos=None): dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([i.position().x() for i in list(self.model.parameterComponents().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) sortedParams = sorted(QgsApplication.instance().processingRegistry().parameterTypes(), key=lambda pt: pt.name()) for param in sortedParams: if param.flags() & QgsProcessingParameterType.ExposeToModeler: paramItem = QTreeWidgetItem() paramItem.setText(0, param.name()) paramItem.setData(0, Qt.UserRole, param.id()) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, param.description()) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): algorithm = self.algorithmTree.selectedAlgorithm() if algorithm is not None: alg = QgsApplication.processingRegistry().createAlgorithmById(algorithm.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition(alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([alg.position().x() for alg in list(self.model.childAlgorithms().values())]) maxY = max([alg.position().y() for alg in list(self.model.childAlgorithms().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def export_as_script_algorithm(self): dlg = ScriptEditorDialog(None) dlg.editor.setText('\n'.join(self.model.asPythonCode(QgsProcessing.PythonQgsProcessingAlgorithmSubclass, 4))) dlg.show()
class CustomFeatureForm(QDialog, FORM_CLASS): """ Customized form tailor made for reclassification mode of DSGTools: Custom Feature Tool Box. This form was copied from `Ferramentas de Produção` and modified for the DSGTools plugin. """ def __init__(self, fields, layerMap, attributeMap=None): """ Class constructor. :param fields: (QgsFields) set of fields that will be applied to new feature(s). :param layerMap: (dict) a map from vector layer to feature list to be reclassified (allocated to another layer). :param attributeMap: (dict) a map from attribute name to its (reclassified) value. """ super(CustomFeatureForm, self).__init__() self.setupUi(self) self.fields = fields self.layerMap = layerMap self.attributeMap = attributeMap or dict() self._layersWidgets = dict() self.setupReclassifiedLayers() self.widgetsLayout = QGridLayout(self.scrollAreaWidgetContents) self._fieldsWidgets = dict() self.setupFields() self.setWindowTitle(self.tr("DSGTools Feature Reclassification Form")) self.messageBar = QgsMessageBar(self) def resizeEvent(self, e): """ Just make sure if any alert box is being displayed, it matches the dialog size. Method reimplementation. :param e: (QResizeEvent) resize event related to this widget resizing. """ self.messageBar.resize( QSize( self.geometry().size().width(), 40 # this felt nicer than the original height (30) )) def setupReclassifiedLayers(self): """ Fills GUI with the data for reclassified data. Dialog should allow user to choose whether he wants to reclassify all identified layers or just part of it. """ layout = self.vLayout for l, fl in self.layerMap.items(): cb = QCheckBox() size = len(fl) fCount = self.tr("features") if size > 1 else self.tr("feature") cb.setText("{0} ({1} {2})".format(l.name(), size, fCount)) cb.setChecked(True) # just to avoid reading the layout when reading the form self._layersWidgets[l.name()] = cb layout.addWidget(cb) def setupFields(self): """ Setups up all fields and fill up with available data on the attribute map. """ utils = Utils() row = 0 # in case no fields are provided for row, f in enumerate(self.fields): fName = f.name() fMap = self.attributeMap[fName] if fName in self.attributeMap \ else None if fName in self.attributeMap: fMap = self.attributeMap[fName] if fMap["ignored"]: w = QLineEdit() w.setText(self.tr("Field is set to be ignored")) value = None enabled = False else: value = fMap["value"] enabled = fMap["editable"] if fMap["isPk"]: # visually identify primary key attributes text = '<p>{0} <img src=":/plugins/DsgTools/icons/key.png" '\ 'width="16" height="16"></p>'.format(fName) else: text = fName else: value = None enabled = True text = fName if fName in self.attributeMap and self.attributeMap[fName][ "ignored"]: pass elif utils.fieldIsFloat(f): w = QDoubleSpinBox() w.setValue(0 if value is None else value) elif utils.fieldIsInt(f): w = QSpinBox() w.setValue(0 if value is None else value) else: w = QLineEdit() w.setText("" if value is None else value) w.setEnabled(enabled) # also to make easier to read data self._fieldsWidgets[fName] = w label = QLabel(text) label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) w.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.widgetsLayout.addWidget(label, row, 0) self.widgetsLayout.addWidget(w, row, 1) self.widgetsLayout.addItem( QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Expanding), row + 1, 1, 1, 2) # row, col, rowSpan, colSpan def updateAttributeMap(self): """ Reads all filled data and updated attribute map with such values. """ for fName, w in self._fieldsWidgets.items(): if not fName in self.attributeMap: self.attributeMap[fName] = dict() self.attributeMap[fName]["ignored"] = False self.attributeMap[fName]["editable"] = True if self.attributeMap[fName]["ignored"]: continue w = self._fieldsWidgets[fName] if isinstance(w, QSpinBox) or isinstance(w, QDoubleSpinBox): self.attributeMap[fName]["value"] = w.value() else: self.attributeMap[fName]["value"] = w.text() def readSelectedLayers(self): """ Applies a filter over the layer/feature list map based on user selection. :return: (dict) filtered layer-feature list map. """ filtered = dict() for l, fl in self.layerMap.items(): if self._layersWidgets[l.name()].isChecked(): filtered[l] = fl return filtered def readFieldMap(self): """ Reads filled data into the form and sets it to a map from field name to field value to be set. Only fields allowed to be reclassified shall be exported in this method. :return: (dict) a map from field name to its output value. """ fMap = dict() for fName, w in self._fieldsWidgets.items(): if not fName in self.attributeMap: continue w = self._fieldsWidgets[fName] if isinstance(w, QSpinBox) or isinstance(w, QDoubleSpinBox): fMap[fName] = w.value() else: fMap[fName] = w.text() return fMap @pyqtSlot() def on_okPushButton_clicked(self): """ Verifies if at least one layer is selected and either warn user to select one, or closes with status 1 ("OK"). """ if len(self.readSelectedLayers()) > 0: self.updateAttributeMap() self.done(1) else: self.messageBar.pushMessage( self.tr('Invalid layer selection'), self.tr("select at least one layer for reclassification!"), level=Qgis.Warning, duration=5)
class ModelerParametersDialog(QDialog): def __init__(self, alg, model, algName=None, configuration=None): QDialog.__init__(self) self.setModal(True) self._alg = alg # The algorithm to define in this dialog. It is an instance of QgsProcessingAlgorithm self.model = model # The model this algorithm is going to be added to. It is an instance of QgsProcessingModelAlgorithm self.childId = algName # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self.configuration = configuration self.setupUi() self.params = None settings = QgsSettings() self.restoreGeometry(settings.value("/Processing/modelParametersDialogGeometry", QByteArray())) def closeEvent(self, event): settings = QgsSettings() settings.setValue("/Processing/modelParametersDialogGeometry", self.saveGeometry()) super(ModelerParametersDialog, self).closeEvent(event) def setupUi(self): self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.algorithmItem = None self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.displayName()) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) self.algorithmItem = QgsGui.instance().processingGuiRegistry().algorithmConfigurationWidget(self._alg) if self.configuration: self.algorithmItem.setConfiguration(self.configuration) self.verticalLayout.addWidget(self.algorithmItem) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue wrapper = WidgetWrapperFactory.create_wrapper(param, self) self.wrappers[param.name()] = wrapper widget = wrapper.widget if widget is not None: self.valueItems[param.name()] = widget tooltip = param.description() widget.setToolTip(tooltip) if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: wrapper.label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.verticalLayout.addWidget(wrapper.label) self.verticalLayout.addWidget(widget) for dest in self._alg.destinationParameterDefinitions(): if dest.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if isinstance(dest, (QgsProcessingParameterRasterDestination, QgsProcessingParameterVectorDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination)): label = QLabel(dest.description()) item = QgsFilterLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(self.tr('[Enter name if this is a final result]')) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[dest.name()] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.displayName()) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.verticalLayout2.addWidget(self.scrollArea) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.helpRequested.connect(self.openHelp) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def getAvailableDependencies(self): # spellok if self.childId is None: dependent = [] else: dependent = list(self.model.dependentChildAlgorithms(self.childId)) dependent.append(self.childId) opts = [] for alg in list(self.model.childAlgorithms().values()): if alg.childId() not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description() for alg in self.getAvailableDependencies()]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.wrappers[param.name()].widget.setVisible(self.showAdvanced) self.wrappers[param.name()].label.setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]): # upgrade paramType to list if paramType is None: paramType = [] elif not isinstance(paramType, (tuple, list)): paramType = [paramType] if outTypes is None: outTypes = [] elif not isinstance(outTypes, (tuple, list)): outTypes = [outTypes] return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if issubclass(p, QgsProcessingParameterDefinition)], [o.typeName() for o in outTypes if issubclass(o, QgsProcessingOutputDefinition)], dataTypes) def resolveValueDescription(self, value): if isinstance(value, QgsProcessingModelChildParameterSource): if value.source() == QgsProcessingModelChildParameterSource.StaticValue: return value.staticValue() elif value.source() == QgsProcessingModelChildParameterSource.ModelParameter: return self.model.parameterDefinition(value.parameterName()).description() elif value.source() == QgsProcessingModelChildParameterSource.ChildOutput: alg = self.model.childAlgorithm(value.outputChildId()) return self.tr("'{0}' from algorithm '{1}'").format( alg.algorithm().outputDefinition(value.outputName()).description(), alg.description()) return value def setPreviousValues(self): if self.childId is not None: alg = self.model.childAlgorithm(self.childId) self.descriptionBox.setText(alg.description()) for param in alg.algorithm().parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue value = None if param.name() in alg.parameterSources(): value = alg.parameterSources()[param.name()] if isinstance(value, list) and len(value) == 1: value = value[0] elif isinstance(value, list) and len(value) == 0: value = None if value is None: value = param.defaultValue() if isinstance(value, QgsProcessingModelChildParameterSource) and value.source() == QgsProcessingModelChildParameterSource.StaticValue: value = value.staticValue() self.wrappers[param.name()].setValue(value) for name, out in list(alg.modelOutputs().items()): if out.childOutputName() in self.valueItems: self.valueItems[out.childOutputName()].setText(out.name()) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.childId() in alg.dependencies(): selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = QgsProcessingModelChildAlgorithm(self._alg.id()) if not self.childId: alg.generateChildId(self.model) else: alg.setChildId(self.childId) alg.setDescription(self.descriptionBox.text()) if self.algorithmItem: alg.setConfiguration(self.algorithmItem.configuration()) self._alg = alg.algorithm().create(self.algorithmItem.configuration()) for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue try: val = self.wrappers[param.name()].value() except InvalidParameterValue: self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(param.description()), level=Qgis.Warning) return None if isinstance(val, QgsProcessingModelChildParameterSource): val = [val] elif not (isinstance(val, list) and all( [isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])): val = [QgsProcessingModelChildParameterSource.fromStaticValue(val)] for subval in val: if (isinstance(subval, QgsProcessingModelChildParameterSource) and subval.source() == QgsProcessingModelChildParameterSource.StaticValue and not param.checkValueIsAcceptable(subval.staticValue())) \ or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional): self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format( param.description()), level=Qgis.Warning) return None alg.addParameterSources(param.name(), val) outputs = {} for dest in self._alg.destinationParameterDefinitions(): if not dest.flags() & QgsProcessingParameterDefinition.FlagHidden: name = self.valueItems[dest.name()].text() if name.strip() != '': output = QgsProcessingModelOutput(name, name) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[name] = output if dest.flags() & QgsProcessingParameterDefinition.FlagIsModelOutput: if dest.name() not in outputs: output = QgsProcessingModelOutput(dest.name(), dest.name()) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[dest.name()] = output alg.setModelOutputs(outputs) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok dep_ids = [] for selected in selectedOptions: dep_ids.append(availableDependencies[selected].childId()) # spellok alg.setDependencies(dep_ids) #try: # self._alg.processBeforeAddingToModeler(alg, self.model) #except: # pass return alg def okPressed(self): alg = self.createAlgorithm() if alg is not None: self.accept() def cancelPressed(self): self.reject() def openHelp(self): algHelp = self._alg.helpUrl() if not algHelp: algHelp = QgsHelp.helpUrl("processing_algs/{}/{}.html#{}".format( self._alg.provider().helpId(), self._alg.groupId(), "{}{}".format(self._alg.provider().helpId(), self._alg.name()))).toString() if algHelp not in [None, ""]: webbrowser.open(algHelp)
class BatchAlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setWindowTitle(self.tr('Batch Processing - %s') % self.alg.name) self.setMainWidget(BatchPanel(self, self.alg)) self.textShortHelp.setVisible(False) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) def accept(self): self.algs = [] self.load = [] self.canceled = False for row in range(self.mainWidget.tblParameters.rowCount()): alg = self.alg.getCopy() col = 0 for param in alg.parameters: if param.hidden: continue wrapper = self.mainWidget.wrappers[row][col] if not self.mainWidget.setParamValue(param, wrapper, alg): self.bar.pushMessage( "", self.tr( 'Wrong or missing parameter value: %s (row %d)') % (param.description, row + 1), level=QgsMessageBar.WARNING, duration=5) self.algs = None return col += 1 for out in alg.outputs: if out.hidden: continue widget = self.mainWidget.tblParameters.cellWidget(row, col) text = widget.getValue() if text.strip() != '': out.value = text col += 1 else: self.bar.pushMessage( "", self.tr('Wrong or missing output value: %s (row %d)') % (out.description, row + 1), level=QgsMessageBar.WARNING, duration=5) self.algs = None return self.algs.append(alg) if self.alg.getVisibleOutputsCount(): widget = self.mainWidget.tblParameters.cellWidget(row, col) self.load.append(widget.currentIndex() == 0) else: self.load.append(False) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.mainWidget.setEnabled(False) self.progressBar.setMaximum(len(self.algs)) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass for count, alg in enumerate(self.algs): self.setText( self.tr('\nProcessing algorithm %d/%d...') % (count + 1, len(self.algs))) self.setInfo(self.tr('<b>Algorithm %s starting...</b>' % alg.name)) if runalg(alg, self) and not self.canceled: if self.load[count]: handleAlgorithmResults(alg, self, False) self.setInfo( self.tr('Algorithm %s correctly executed...') % alg.name) else: QApplication.restoreOverrideCursor() return self.finish() def finish(self): for count, alg in enumerate(self.algs): self.loadHTMLResults(alg, count) self.createSummaryTable() QApplication.restoreOverrideCursor() self.mainWidget.setEnabled(True) QMessageBox.information(self, self.tr('Batch processing'), self.tr('Batch processing completed')) def loadHTMLResults(self, alg, num): for out in alg.outputs: if out.hidden or not out.open: continue if isinstance(out, OutputHTML): ProcessingResults.addResult( '{} [{}]'.format(out.description, num), out.value) def createSummaryTable(self): createTable = False for out in self.algs[0].outputs: if isinstance(out, (OutputNumber, OutputString)): createTable = True break if not createTable: return outputFile = getTempFilename('html') with codecs.open(outputFile, 'w', encoding='utf-8') as f: for alg in self.algs: f.write('<hr>\n') for out in alg.outputs: if isinstance(out, (OutputNumber, OutputString)): f.write('<p>{}: {}</p>\n'.format( out.description, out.value)) f.write('<hr>\n') ProcessingResults.addResult('{} [summary]'.format(self.algs[0].name), outputFile)
class geobricks_qgis_plugin_faostat: def __init__(self, iface): self.iface = iface self.layout = QVBoxLayout() self.cbGroups = QComboBox() self.cbDomains = QComboBox() self.cbElements = QComboBox() self.cbItems = QComboBox() self.download_folder = QLineEdit() try: if self.last_download_folder is not None: self.download_folder.setText(self.last_download_folder) except: self.last_download_folder = None self.download_folder_button = QPushButton(self.tr('...')) self.download_folder_button.clicked.connect(self.select_output_file) self.progress = QProgressBar() self.add_to_canvas = QCheckBox(self.tr('Add output layer to canvas')) self.start_download_button = QPushButton(self.tr('Start Download')) self.start_download_button.clicked.connect(self.download_data) self.progress_label = QLabel('<b>' + self.tr('Progress') + '</b>') self.bar = QgsMessageBar() self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'geobricks_qgis_plugin_faostat_{}.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) self.dlg = geobricks_qgis_plugin_faostatDialog() self.actions = [] self.menu = self.tr('FAOSTAT Data Downloader') self.toolbar = self.iface.addToolBar('geobricks_qgis_plugin_faostat') self.toolbar.setObjectName('geobricks_qgis_plugin_faostat') self.initialized = False def run(self): # Build UI self.build_ui() # Populate domains groups = get_groups() for group in groups: self.cbGroups.addItem(group['label'], group['code']) # Test message bar self.bar.pushMessage(None, str(len(groups)) + self.tr(' groups added'), level=QgsMessageBar.INFO) def build_ui(self): # Reset layout self.layout = QVBoxLayout() # Groups lbl_0 = QLabel('<b>' + self.tr('Groups') + '</b>') self.cbGroups.addItem(self.tr('Please select a groups...')) self.cbGroups.activated[str].connect(self.on_groups_change) # Domains lbl_1 = QLabel('<b>' + self.tr('Domains') + '</b>') self.cbDomains.addItem(self.tr('Please select a group to populate this combo-box...')) self.cbDomains.activated[str].connect(self.on_domain_change) # Elements lbl_2 = QLabel('<b>' + self.tr('Elements') + '</b>') self.cbElements.addItem(self.tr('Please select a domain to populate this combo-box...')) # Items lbl_3 = QLabel('<b>' + self.tr('Items') + '</b>') self.cbItems.addItem(self.tr('Please select a domain to populate this combo-box...')) # Download Folder lbl_4 = QLabel('<b>' + self.tr('Download Folder') + '</b>') download_folder_widget = QWidget() download_folder_layout = QHBoxLayout() download_folder_widget.setLayout(download_folder_layout) download_folder_layout.addWidget(self.download_folder) download_folder_layout.addWidget(self.download_folder_button) # Progress bar self.progress.setValue(0) # Add to canvas self.add_to_canvas.toggle() # Message bar self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout.addWidget(self.bar) # Add widgets to layout self.layout.addWidget(lbl_0) self.layout.addWidget(self.cbGroups) self.layout.addWidget(lbl_1) self.layout.addWidget(self.cbDomains) self.layout.addWidget(lbl_2) self.layout.addWidget(self.cbElements) self.layout.addWidget(lbl_3) self.layout.addWidget(self.cbItems) self.layout.addWidget(lbl_4) self.layout.addWidget(download_folder_widget) self.layout.addWidget(self.add_to_canvas) self.layout.addWidget(self.start_download_button) self.layout.addWidget(self.progress_label) self.layout.addWidget(self.progress) # Set layout self.dlg.setLayout(self.layout) # Show dialog self.dlg.show() def download_data(self): # Get user selection group_code = self.cbGroups.itemData(self.cbGroups.currentIndex()) domain_code = self.cbDomains.itemData(self.cbDomains.currentIndex()) element_code = self.cbElements.itemData(self.cbElements.currentIndex()) item_code = self.cbItems.itemData(self.cbItems.currentIndex()) download_folder = self.download_folder.text() # Check selection if group_code is None: self.bar.pushMessage(None, self.tr('Please select a group'), level=QgsMessageBar.CRITICAL) elif domain_code is None: self.bar.pushMessage(None, self.tr('Please select a domain'), level=QgsMessageBar.CRITICAL) elif element_code is None: self.bar.pushMessage(None, self.tr('Please select an element'), level=QgsMessageBar.CRITICAL) elif item_code is None: self.bar.pushMessage(None, self.tr('Please select an item'), level=QgsMessageBar.CRITICAL) elif download_folder is None or len(download_folder) == 0: self.bar.pushMessage(None, self.tr('Please select a download folder'), level=QgsMessageBar.CRITICAL) else: # Get data data = get_data(domain_code, element_code, item_code) # Notify the user self.bar.pushMessage(None, self.tr('Downloaded rows: ') + str(len(data)), level=QgsMessageBar.INFO) # Layer name layer_name = self.cbItems.currentText().replace(' ', '_') + '_' + self.cbElements.currentText().replace(' ', '_') folder_name = os.path.join(download_folder, group_code, domain_code) if not os.path.exists(folder_name): os.makedirs(folder_name) # Copy template layer output_file = copy_layer(folder_name, layer_name) layer = QgsVectorLayer(output_file, 'layer_name', 'ogr') # Add all the years to the layer feature_idx = 64 year_to_be_shown = 2014 number_of_nulls = 0 for year in range(2014, 1960, -1): progress = (1 + (feature_idx - 64)) * 1.86 self.progress.setValue(progress) self.progress_label.setText('<b>' + self.tr('Progress') + ': ' + '</b> ' + self.tr('Adding Year ') + str(year)) year_data = self.get_year_data(data, year) layer.dataProvider().addAttributes([QgsField(str(year), QVariant.Double)]) if len(year_data) > 0: layer.startEditing() for feature in layer.getFeatures(): if feature['FAOSTAT'] is not None: feature_code = str(feature['FAOSTAT']) for d in year_data: data_code = str(d['code']) if data_code == feature_code: value = d['value'] layer.changeAttributeValue(feature.id(), (feature_idx), float(value)) tmp_feature = QgsFeature() tmp_feature.setAttributes([float(value)]) layer.dataProvider().addFeatures([tmp_feature]) if value is None: number_of_nulls += 1 layer.commitChanges() else: year_to_be_shown -= 1 feature_idx += 1 # Add layer to canvas if self.add_to_canvas.isChecked(): renderer = self.create_join_renderer(layer, str(year_to_be_shown), 11, QgsGraduatedSymbolRendererV2.Pretty) l = QgsVectorLayer(output_file, layer_name + '(' + str(year_to_be_shown) + ')', 'ogr') r = renderer.clone() r.setClassAttribute(str(year_to_be_shown)) l.setRendererV2(r) QgsMapLayerRegistry.instance().addMapLayer(l) self.iface.legendInterface().setLayerVisible(l, True) # Close pop-up self.dlg.close() def create_join_renderer(self, layer, field, classes, mode, color='PuBu'): symbol = QgsSymbolV2.defaultSymbol(layer.geometryType()) style = QgsStyleV2().defaultStyle() colorRamp = style.colorRampRef(color) renderer = QgsGraduatedSymbolRendererV2.createRenderer(layer, field, classes, mode, symbol, colorRamp) label_format = self.create_join_label_format(2) renderer.setLabelFormat(label_format) return renderer def create_join_label_format(self, precision): format = QgsRendererRangeV2LabelFormat() template = '%1 - %2' format.setFormat(template) format.setPrecision(precision) format.setTrimTrailingZeroes(True) return format def get_year_data(self, data, year): out = [] for d in data: if d['year'] == str(year): out.append(d) return out def on_groups_change(self, text): # Get selected group code group_code = self.cbGroups.itemData(self.cbGroups.currentIndex()) # Update domains list domains = get_domains(group_code) self.cbDomains.clear() self.cbDomains.addItem(self.tr('Please select a domain')) for domain in domains: self.cbDomains.addItem(domain['label'], domain['code']) def on_domain_change(self, text): # Get selected domain code domain_code = self.cbDomains.itemData(self.cbDomains.currentIndex()) # Check domain code if domain_code is not None: # Update elements list try: elements = get_elements(domain_code) self.cbElements.clear() self.cbElements.addItem(self.tr('Please select an element')) for element in elements: self.cbElements.addItem(element['label'], element['code']) except ValueError: self.bar.pushMessage(None, self.tr('No elements available for this domain. Please select another domain.'), level=QgsMessageBar.CRITICAL) # Update items list try: items = get_items(domain_code) self.cbItems.clear() self.cbItems.addItem(self.tr('Please select an item')) for item in items: self.cbItems.addItem(item['label'], item['code']) except: self.bar.pushMessage(None, self.tr('No items available for this domain. Please select another domain.'), level=QgsMessageBar.CRITICAL) else: self.bar.pushMessage(None, self.tr('No domain selected. Please select a domain.'), level=QgsMessageBar.CRITICAL) def tr(self, message): return QCoreApplication.translate('geobricks_qgis_plugin_faostat', 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): 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): icon_path = ':/plugins/geobricks_qgis_plugin_faostat/icon.png' self.add_action( icon_path, text=self.tr('FAOSTAT Data Downloader'), callback=self.run, parent=self.iface.mainWindow()) def unload(self): for action in self.actions: self.iface.removePluginMenu( self.tr(self.tr('FAOSTAT Data Downloader')), action) self.iface.removeToolBarIcon(action) del self.toolbar def select_output_file(self): filename = QFileDialog.getExistingDirectory(self.dlg, self.tr('Select Folder')) self.last_download_folder = filename self.download_folder.setText(self.last_download_folder)
class CriaSpatialiteDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, parent=None): """Constructor.""" super(CriaSpatialiteDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.filepath = "" self.carregado = False self.coordSysDefinido = False self.epsgCriaSpatialite = 0 self.srsCriaSpatialite = '' self.sqliteFileName = '' self.bar = QgsMessageBar() self.setLayout(QtGui.QGridLayout(self)) self.layout().setContentsMargins(0,0,0,0) self.layout().setAlignment(QtCore.Qt.AlignTop) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.bar.setSizePolicy(sizePolicy) self.layout().addWidget(self.bar, 0,0,1,1) QtCore.QObject.connect(self.pushButtonBuscarPastaDestinoCriaSpatialite, QtCore.SIGNAL(("clicked()")), self.definePastaDestino) QtCore.QObject.connect(self.pushButtonBuscarSistCoordCriaSpatialite, QtCore.SIGNAL(("clicked()")), self.setaSistCoordCriaSpatialite) QtCore.QObject.connect(self.pushButtonOkCriaSpatialite, QtCore.SIGNAL(("clicked()")), self.okselecionadoCriaSpatialite) def getTemplateLocation(self): """ gets the template location """ currentPath = os.path.dirname(__file__) if self.versionComboBox.currentText() == '2.1.3': edgvPath = os.path.join(currentPath, 'template', '213', 'seed_edgv213.sqlite') elif self.versionComboBox.currentText() == 'FTer_2a_Ed': edgvPath = os.path.join(currentPath, 'template', 'FTer_2a_Ed', 'seed_edgvfter_2a_ed.sqlite') elif self.versionComboBox.currentText() == '3.0': edgvPath = os.path.join(currentPath, 'template', '3', 'seed_edgv3.sqlite') return edgvPath def restauraInicio(self): """ Stes the initial state """ self.filepath = "" self.carregado = False self.coordSysDefinido = False self.epsgCriaSpatialite = 0 self.srsCriaSpatialite = '' self.sqliteFileName = '' self.pastaDestinoCriaSpatialiteLineEdit.setText("") self.coordSysCriaSpatialiteLineEdit.setText("") self.nomeLineEdit.setText("") def definePastaDestino(self): """ Defines destination folder """ fd = QtGui.QFileDialog() self.filepath = fd.getExistingDirectory() if self.filepath <> "": self.carregado = True self.pastaDestinoCriaSpatialiteLineEdit.setText(self.filepath) def setaSistCoordCriaSpatialite(self): """ Opens the CRS selector """ projSelector = QgsGenericProjectionSelector() projSelector.setMessage(theMessage=self.tr('Please, select the coordinate system')) projSelector.exec_() try: self.epsgCriaSpatialite = int(projSelector.selectedAuthId().split(':')[-1]) self.srsCriaSpatialite = QgsCoordinateReferenceSystem(self.epsgCriaSpatialite, QgsCoordinateReferenceSystem.EpsgCrsId) if self.srsCriaSpatialite <> "": self.coordSysDefinido = True self.coordSysCriaSpatialiteLineEdit.setText(self.srsCriaSpatialite.description()) except: self.bar.pushMessage("", self.tr('Please, select the coordinate system'), level=QgsMessageBar.WARNING) pass def copiaSemente(self, destino, srid): """ Copies the spatialite seed template """ f = open(self.getTemplateLocation(),'rb') g = open(destino,'wb') x = f.readline() while x: g.write(x) x = f.readline() g.close() con = sqlite3.connect(destino) cursor = con.cursor() srid_sql = (srid,) cursor.execute("UPDATE geometry_columns SET srid=?",srid_sql) con.commit() con.close() def okselecionadoCriaSpatialite(self): """ Performs the database creation """ if self.carregado and self.coordSysDefinido and len(self.nomeLineEdit.text()) > 0: try: self.sqliteFileName = self.filepath+'/'+self.nomeLineEdit.text()+'.sqlite' destino = self.sqliteFileName self.copiaSemente(destino,self.epsgCriaSpatialite) self.close() self.restauraInicio() QtGui.QMessageBox.information(self, self.tr('Information'), self.tr('Spatialite created successfully!')) except: qgis.utils.iface.messageBar().pushMessage(self.tr("Error!"), self.tr("Problem creating the database!"), level=QgsMessageBar.CRITICAL) self.restauraInicio() pass else: if self.coordSysDefinido == False: self.bar.pushMessage(self.tr("Warning!"), self.tr('Please, select the coordinate system'), level=QgsMessageBar.WARNING) if self.carregado == False: self.bar.pushMessage(self.tr("Warning!"), self.tr('Please, select a folder to save the database'), level=QgsMessageBar.CRITICAL) if len(self.nomeLineEdit.text()) == 0: self.bar.pushMessage(self.tr("Warning!"), self.tr('Please, fill the file name.'), level=QgsMessageBar.CRITICAL)
class ModelerParametersDialog(QDialog): ENTER_NAME = '[Enter name if this is a final result]' NOT_SELECTED = '[Not selected]' USE_MIN_COVERING_EXTENT = '[Use min covering extent]' def __init__(self, alg, model, algName=None): QDialog.__init__(self) self.setModal(True) # The algorithm to define in this dialog. It is an instance of GeoAlgorithm self._alg = alg # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class self.alg = None # The model this algorithm is going to be added to self.model = model # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self._algName = algName self.setupUi() self.params = None def setupUi(self): self.labels = {} self.widgets = {} self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | + QDialogButtonBox.Ok | + QDialogButtonBox.Help) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.displayName()) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) for param in self._alg.parameters: if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue desc = param.description() if isinstance(param, ParameterExtent): desc += self.tr('(xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr('(x, y)') if param.flags() & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') label = QLabel(desc) self.labels[param.name()] = label wrapper = WidgetWrapperFactory.create_wrapper(param, self) self.wrappers[param.name()] = wrapper widget = wrapper.widget if widget is not None: self.valueItems[param.name()] = widget tooltip = param.description() label.setToolTip(tooltip) widget.setToolTip(tooltip) if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.widgets[param.name()] = widget self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for output in self._alg.outputs: if output.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if isinstance(output, (OutputRaster, OutputVector, OutputTable, OutputHTML, OutputFile, OutputDirectory)): label = QLabel(output.description() + '<' + output.__class__.__name__ + '>') item = QLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[output.name] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.displayName()) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.verticalLayout2.addWidget(self.scrollArea) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.helpRequested.connect(self.openHelp) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def getAvailableDependencies(self): # spellok if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) opts = [] for alg in list(self.model.algs.values()): if alg.modeler_name not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description for alg in self.getAvailableDependencies()]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameters: if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.labels[param.name].setVisible(self.showAdvanced) self.widgets[param.name].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outType=None, dataType=None): # upgrade paramType to list if type(paramType) is not list: paramType = [paramType] values = [] inputs = self.model.inputs for i in list(inputs.values()): param = i.param for t in paramType: if isinstance(param, t): if dataType is not None: if param.datatype in dataType: values.append(ValueFromInput(param.name)) else: values.append(ValueFromInput(param.name)) break if outType is None: return values if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) for alg in list(self.model.algs.values()): if alg.modeler_name not in dependent: for out in alg.algorithm.outputs: if isinstance(out, outType): if dataType is not None and out.datatype in dataType: values.append(ValueFromOutput(alg.modeler_name, out.name)) else: values.append(ValueFromOutput(alg.modeler_name, out.name)) return values def resolveValueDescription(self, value): if isinstance(value, ValueFromInput): return self.model.inputs[value.name].param.description() else: alg = self.model.algs[value.alg] return self.tr("'{0}' from algorithm '{1}'").format(alg.algorithm.getOutputFromName(value.output).description(), alg.description) def setPreviousValues(self): if self._algName is not None: alg = self.model.algs[self._algName] self.descriptionBox.setText(alg.description) for param in alg.algorithm.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if param.name() in alg.params: value = alg.params[param.name()] else: value = param.defaultValue() self.wrappers[param.name()].setValue(value) for name, out in list(alg.outputs.items()): self.valueItems[name].setText(out.description()) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.name in alg.dependencies: selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = Algorithm(self._alg.id()) alg.setName(self.model) alg.description = self.descriptionBox.text() params = self._alg.parameterDefinitions() outputs = self._alg.outputs for param in params: if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if not param.checkValueIsAcceptable(self.wrappers[param.name()].value): self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description(), level=QgsMessageBar.WARNING) return None for output in outputs: if not output.flags() & QgsProcessingParameterDefinition.FlagHidden: name = str(self.valueItems[output.name()].text()) if name.strip() != '' and name != ModelerParametersDialog.ENTER_NAME: alg.outputs[output.name()] = ModelerOutput(name) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok for selected in selectedOptions: alg.dependencies.append(availableDependencies[selected].name) # spellok self._alg.processBeforeAddingToModeler(alg, self.model) return alg def okPressed(self): self.alg = self.createAlgorithm() if self.alg is not None: self.close() def cancelPressed(self): self.alg = None self.close() def openHelp(self): algHelp = self._alg.help() if algHelp is not None: webbrowser.open(algHelp)
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, conn_manager, context, 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.db = self.conn_manager.get_db_connector_from_source( self.db_source) self.logger = Logger() self.app = AppInterface() 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.base_configuration = BaseConfiguration() 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 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 in LADMColModelRegistry().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.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.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 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.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("DialogExportData", "Source DB Connection Settings")) dlg.show_tip( QCoreApplication.translate( "DialogExportData", "Configure which DB you want to export data from.")) 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.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.txtStdout.clear() self.progress_bar.setValue(0) self.bar.clearWidgets() java_home_set = self.java_dependency.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_dependency.get_java_on_demand() self.disable() return configuration = self.update_configuration() if configuration.disable_validation: # If data validation at export is disabled, we ask for confirmation self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Question) self.msg.setText( QCoreApplication.translate( "DialogExportData", "Are you sure you want to export your data without validation?" )) self.msg.setWindowTitle( QCoreApplication.translate("DialogExportData", "Export 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 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 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_model_baker_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() configuration.with_exporttid = 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( 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): show_plugin_help("export_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) 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 Qgis2threejsDialog(QDialog): STYLE_MAX_COUNT = 4 def __init__(self, iface, properties=None): QDialog.__init__(self, iface.mainWindow()) self.iface = iface self.currentItem = None self.currentPage = None topItemCount = len(ObjectTreeItem.topItemNames) if properties is None: self.properties = [None] * topItemCount for i in range(ObjectTreeItem.ITEM_OPTDEM, topItemCount): self.properties[i] = {} else: self.properties = properties # Set up the user interface from Designer. self.ui = ui = Ui_Qgis2threejsDialog() ui.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint) ui.lineEdit_OutputFilename.setPlaceholderText("[Temporary file]") ui.pushButton_Run.clicked.connect(self.run) ui.pushButton_Close.clicked.connect(self.reject) # set up map tool self.previousMapTool = None self.mapTool = RectangleMapTool(iface.mapCanvas()) #self.mapTool = PointMapTool(iface.mapCanvas()) # set up the template combo box self.initTemplateList() # set up the properties pages self.pages = {} self.pages[ppages.PAGE_WORLD] = ppages.WorldPropertyPage(self) self.pages[ppages.PAGE_CONTROLS] = ppages.ControlsPropertyPage(self) self.pages[ppages.PAGE_DEM] = ppages.DEMPropertyPage(self) self.pages[ppages.PAGE_VECTOR] = ppages.VectorPropertyPage(self) container = ui.propertyPagesContainer for page in self.pages.itervalues(): page.hide() container.addWidget(page) # build object tree self.topItemPages = {ObjectTreeItem.ITEM_WORLD: ppages.PAGE_WORLD, ObjectTreeItem.ITEM_CONTROLS: ppages.PAGE_CONTROLS, ObjectTreeItem.ITEM_DEM: ppages.PAGE_DEM} self.initObjectTree() self.ui.treeWidget.currentItemChanged.connect(self.currentObjectChanged) self.ui.treeWidget.itemChanged.connect(self.objectItemChanged) ui.progressBar.setVisible(False) ui.toolButton_Browse.clicked.connect(self.browseClicked) #iface.mapCanvas().mapToolSet.connect(self.mapToolSet) # to show button to enable own map tool self.bar = None # QgsMessageBar self.localBrowsingMode = True self.rb_quads = self.rb_point = None self.objectTypeManager = ObjectTypeManager() def exec_(self): ui = self.ui messages = [] # show message if crs unit is degrees mapSettings = self.iface.mapCanvas().mapSettings() if QGis.QGIS_VERSION_INT >= 20300 else self.iface.mapCanvas().mapRenderer() if mapSettings.destinationCrs().mapUnits() in [QGis.Degrees]: self.showMessageBar("The unit of current CRS is degrees", "Terrain may not appear well.") self.ui.treeWidget.setCurrentItem(self.ui.treeWidget.topLevelItem(ObjectTreeItem.ITEM_DEM)) return QDialog.exec_(self) def showMessageBar(self, title, text, level=QgsMessageBar.INFO): if self.bar is None: self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) ui = self.ui margins = ui.gridLayout.getContentsMargins() vl = ui.gridLayout.takeAt(0) ui.gridLayout.setContentsMargins(0,0,0,0) ui.gridLayout.addWidget(self.bar, 0, 0) ui.gridLayout.addItem(vl, 1, 0) ui.verticalLayout.setContentsMargins(margins[0], margins[1] / 2, margins[2], margins[3]) self.bar.pushMessage(title, text, level=level) def initTemplateList(self): cbox = self.ui.comboBox_Template cbox.clear() templateDir = QDir(tools.templateDir()) for i, entry in enumerate(templateDir.entryList(["*.html", "*.htm"])): cbox.addItem(entry) # set tool tip text meta = tools.getTemplateMetadata(templateDir.filePath(entry)) desc = meta.get("description", "") if desc: cbox.setItemData(i, desc, Qt.ToolTipRole) # select the last used template templateName = QSettings().value("/Qgis2threejs/lastTemplate", "", type=unicode) if templateName: index = cbox.findText(templateName) if index != -1: cbox.setCurrentIndex(index) return index return -1 def initObjectTree(self): tree = self.ui.treeWidget tree.clear() # add vector and raster layers into tree widget topItems = [] for index, itemName in enumerate(ObjectTreeItem.topItemNames): item = QTreeWidgetItem(tree, [itemName]) item.setData(0, Qt.UserRole, index) topItems.append(item) optDEMChecked = False for layer in self.iface.legendInterface().layers(): layerType = layer.type() if layerType not in (QgsMapLayer.VectorLayer, QgsMapLayer.RasterLayer): continue parentId = None if layerType == QgsMapLayer.VectorLayer: geometry_type = layer.geometryType() if geometry_type in [QGis.Point, QGis.Line, QGis.Polygon]: parentId = ObjectTreeItem.ITEM_POINT + geometry_type # - QGis.Point elif layerType == QgsMapLayer.RasterLayer and layer.providerType() == "gdal" and layer.bandCount() == 1: parentId = ObjectTreeItem.ITEM_OPTDEM if parentId is None: continue item = QTreeWidgetItem(topItems[parentId], [layer.name()]) isVisible = self.properties[parentId].get(layer.id(), {}).get("visible", False) #self.iface.legendInterface().isLayerVisible(layer) check_state = Qt.Checked if isVisible else Qt.Unchecked item.setData(0, Qt.CheckStateRole, check_state) item.setData(0, Qt.UserRole, layer.id()) if parentId == ObjectTreeItem.ITEM_OPTDEM and isVisible: optDEMChecked = True for item in topItems: if item.data(0, Qt.UserRole) != ObjectTreeItem.ITEM_OPTDEM or optDEMChecked: tree.expandItem(item) def saveProperties(self, item, page): properties = page.properties() parent = item.parent() if parent is None: # top item: properties[topItemIndex] self.properties[item.data(0, Qt.UserRole)] = properties else: # layer item: properties[topItemIndex][layerId] topItemIndex = parent.data(0, Qt.UserRole) self.properties[topItemIndex][item.data(0, Qt.UserRole)] = properties if debug_mode: qDebug(str(self.properties)) def currentObjectChanged(self, currentItem, previousItem): # save properties of previous item if previousItem and self.currentPage: self.saveProperties(previousItem, self.currentPage) self.currentItem = currentItem self.currentPage = None # hide all pages for page in self.pages.itervalues(): page.hide() parent = currentItem.parent() if parent is None: topItemIndex = currentItem.data(0, Qt.UserRole) pageType = self.topItemPages.get(topItemIndex, ppages.PAGE_NONE) page = self.pages.get(pageType, None) if page is None: return page.setup(self.properties[topItemIndex]) page.show() else: parentId = parent.data(0, Qt.UserRole) layerId = currentItem.data(0, Qt.UserRole) if layerId is None: return layer = QgsMapLayerRegistry().instance().mapLayer(layerId) if layer is None: return layerType = layer.type() if layerType == QgsMapLayer.RasterLayer: page = self.pages[ppages.PAGE_DEM] page.setup(self.properties[parentId].get(layerId, None), layer, False) elif layerType == QgsMapLayer.VectorLayer: page = self.pages[ppages.PAGE_VECTOR] page.setup(self.properties[parentId].get(layerId, None), layer) else: return page.show() self.currentPage = page def objectItemChanged(self, item, column): parent = item.parent() if parent is None: return if item == self.currentItem: if self.currentPage: # update enablement of property widgets self.currentPage.itemChanged(item) else: # select changed item self.ui.treeWidget.setCurrentItem(item) # set visible property #visible = item.data(0, Qt.CheckStateRole) == Qt.Checked #parentId = parent.data(0, Qt.UserRole) #layerId = item.data(0, Qt.UserRole) #self.properties[parentId].get(layerId, {})["visible"] = visible def primaryDEMChanged(self, layerId): tree = self.ui.treeWidget parent = tree.topLevelItem(ObjectTreeItem.ITEM_OPTDEM) tree.blockSignals(True) for i in range(parent.childCount()): item = parent.child(i) isPrimary = item.data(0, Qt.UserRole) == layerId item.setDisabled(isPrimary) tree.blockSignals(False) def numericFields(self, layer): # get attributes of a sample feature and create numeric field name list numeric_fields = [] f = QgsFeature() layer.getFeatures().nextFeature(f) for field in f.fields(): isNumeric = False try: float(f.attribute(field.name())) isNumeric = True except: pass if isNumeric: numeric_fields.append(field.name()) return numeric_fields def progress(self, percentage): self.ui.progressBar.setValue(percentage) self.ui.progressBar.setVisible(percentage != 100) def run(self): ui = self.ui filename = ui.lineEdit_OutputFilename.text() # ""=Temporary file if filename != "" and QFileInfo(filename).exists() and QMessageBox.question(None, "Qgis2threejs", "Output file already exists. Overwrite it?", QMessageBox.Ok | QMessageBox.Cancel) != QMessageBox.Ok: return self.endPointSelection() # save properties of current object item = self.ui.treeWidget.currentItem() if item and self.currentPage: self.saveProperties(item, self.currentPage) ui.pushButton_Run.setEnabled(False) self.progress(0) canvas = self.iface.mapCanvas() templateName = ui.comboBox_Template.currentText() htmlfilename = ui.lineEdit_OutputFilename.text() # world properties world = self.properties[ObjectTreeItem.ITEM_WORLD] or {} verticalExaggeration = world.get("lineEdit_zFactor", 1.5) verticalShift = world.get("lineEdit_zShift", 0) # export to javascript (three.js) mapTo3d = MapTo3D(canvas, verticalExaggeration=float(verticalExaggeration), verticalShift=float(verticalShift)) context = OutputContext(templateName, mapTo3d, canvas, self.properties, self, self.objectTypeManager, self.localBrowsingMode) htmlfilename = exportToThreeJS(htmlfilename, context, self.progress) self.progress(100) ui.pushButton_Run.setEnabled(True) if htmlfilename is None: return self.clearRubberBands() # store last selections settings = QSettings() settings.setValue("/Qgis2threejs/lastTemplate", templateName) settings.setValue("/Qgis2threejs/lastControls", context.controls) # open browser if not tools.openHTMLFile(htmlfilename): return QDialog.accept(self) def reject(self): # save properties of current object item = self.ui.treeWidget.currentItem() if item and self.currentPage: self.saveProperties(item, self.currentPage) self.endPointSelection() self.clearRubberBands() QDialog.reject(self) def startPointSelection(self): canvas = self.iface.mapCanvas() if self.previousMapTool != self.mapTool: self.previousMapTool = canvas.mapTool() canvas.setMapTool(self.mapTool) self.pages[ppages.PAGE_DEM].toolButton_PointTool.setVisible(False) def endPointSelection(self): self.mapTool.reset() if self.previousMapTool is not None: self.iface.mapCanvas().setMapTool(self.previousMapTool) def mapToolSet(self, mapTool): return #TODO: unstable if mapTool != self.mapTool and self.currentPage is not None: if self.currentPage.pageType == ppages.PAGE_DEM and self.currentPage.isPrimary: self.currentPage.toolButton_PointTool.setVisible(True) def createRubberBands(self, quads, point=None): self.clearRubberBands() # create quads with rubber band self.rb_quads = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rb_quads.setColor(Qt.blue) self.rb_quads.setWidth(1) for quad in quads: points = [] extent = quad.extent points.append(QgsPoint(extent.xMinimum(), extent.yMinimum())) points.append(QgsPoint(extent.xMinimum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMinimum())) self.rb_quads.addGeometry(QgsGeometry.fromPolygon([points]), None) self.log(extent.toString()) self.log("Quad count: %d" % len(quads)) # create a point with rubber band if point: self.rb_point = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb_point.setColor(Qt.red) self.rb_point.addPoint(point) def clearRubberBands(self): # clear quads and point if self.rb_quads: self.iface.mapCanvas().scene().removeItem(self.rb_quads) self.rb_quads = None if self.rb_point: self.iface.mapCanvas().scene().removeItem(self.rb_point) self.rb_point = None def browseClicked(self): directory = os.path.split(self.ui.lineEdit_OutputFilename.text())[0] if directory == "": directory = QDir.homePath() filename = QFileDialog.getSaveFileName(self, self.tr("Output filename"), directory, "HTML file (*.html *.htm)", options=QFileDialog.DontConfirmOverwrite) if filename != "": self.ui.lineEdit_OutputFilename.setText(filename) def log(self, msg): if debug_mode: qDebug(msg)
class CreateGroupPartyCadastre(QDialog, DIALOG_UI): WIZARD_NAME = "CreateGroupPartyCadastreWizard" 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.log = QgsApplication.messageLog() self._db = db self.qgis_utils = qgis_utils 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 = { LA_GROUP_PARTY_TABLE: { 'name': LA_GROUP_PARTY_TABLE, 'geometry': None, LAYER: None }, COL_PARTY_TABLE: { 'name': COL_PARTY_TABLE, 'geometry': None, LAYER: None }, MEMBERS_TABLE: { 'name': MEMBERS_TABLE, 'geometry': None, LAYER: None }, FRACTION_TABLE: { 'name': FRACTION_TABLE, 'geometry': None, LAYER: None }, LA_GROUP_PARTY_TYPE_TABLE: { 'name': LA_GROUP_PARTY_TYPE_TABLE, 'geometry': None, LAYER: None } } # Fill combo of types la_group_party_type_table = self.qgis_utils.get_layer( self._db, LA_GROUP_PARTY_TYPE_TABLE, None, True) if not la_group_party_type_table: return domain_key_index = la_group_party_type_table.fields().indexOf( DOMAIN_KEY_FIELD[self._db.mode]) domain_keys = list( la_group_party_type_table.uniqueValues(domain_key_index)) domain_keys.sort() self.cbo_group_type.addItems(domain_keys) 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): # Load layers self.qgis_utils.get_layers(self._db, self._layers, load=True) if not self._layers: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( self.WIZARD_NAME, "'{}' tool has been closed because there was a problem loading the requeries layers." ).format(self.WIZARD_TOOL_NAME), Qgis.Warning) return False # Check if layers any layer is in editing mode layers_name = list() for layer in self._layers: if self._layers[layer][LAYER].isEditable(): layers_name.append( self._db.get_ladm_layer_name(self._layers[layer][LAYER])) if layers_name: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( self.WIZARD_NAME, "Wizard cannot be opened until the following layers are not in edit mode '{}'." ).format('; '.join([layer_name for layer_name in layers_name])), Qgis.Warning) return False return True def load_parties_data(self): expression = QgsExpression( self._layers[COL_PARTY_TABLE][LAYER].displayExpression()) context = QgsExpressionContext() data = dict() for feature in self._layers[COL_PARTY_TABLE][LAYER].getFeatures(): context.setFeature(feature) expression.prepare(context) data[feature[ID_FIELD]] = [expression.evaluate(context), 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 updat 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( self.WIZARD_NAME, "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( self.WIZARD_NAME, "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.currentText() dict_params = { LA_GROUP_PARTY_NAME_FIELD: name, LA_GROUP_PARTY_GPTYPE_FIELD: 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[LA_GROUP_PARTY_NAME_FIELD] group_party_type = params[LA_GROUP_PARTY_GPTYPE_FIELD] 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 = 0 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." )) break elif porcentajes[t][1] < porcentajes[t][0]: return ( False, QCoreApplication.translate( "CreateGroupParty", "The denominator cannot be less than the numerator." )) break else: 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.pushMessage(message, level, 10) def save_group_party(self, db, params): """ Save group party data into associated tables: LA_GROUP_PARTY_TABLE, MEMBERS_TABLE and FRACTION_TABLE. params: List of dicts, where each dict is an independent group party: { LA_GROUP_PARTY_NAME_FIELD: '', LA_GROUP_PARTY_GPTYPE_FIELD: '', '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[LA_GROUP_PARTY_TABLE][ LAYER].committedFeaturesAdded.connect( partial(self.finish_group_party_saving, group['porcentajes'])) # First save the group party new_feature = QgsVectorLayerUtils().createFeature( self._layers[LA_GROUP_PARTY_TABLE][LAYER]) new_feature.setAttribute(LA_GROUP_PARTY_GPTYPE_FIELD, group[LA_GROUP_PARTY_GPTYPE_FIELD]) new_feature.setAttribute(LA_GROUP_PARTY_NAME_FIELD, group[LA_GROUP_PARTY_NAME_FIELD]) new_feature.setAttribute(LA_GROUP_PARTY_TYPE_FIELD, LA_GROUP_PARTY_TYPE_VALUE) # TODO: Gui should allow users to ented namespace, local_id and date values #new_feature.setAttribute("p_espacio_de_nombres", LA_GROUP_PARTY_TABLE) #new_feature.setAttribute("p_local_id", '0') #new_feature.setAttribute("comienzo_vida_util_version", 'now()') self.log.logMessage("Saving Group Party: {}".format(group), PLUGIN_NAME, Qgis.Info) with edit(self._layers[LA_GROUP_PARTY_TABLE][LAYER]): self._layers[LA_GROUP_PARTY_TABLE][LAYER].addFeature( new_feature) def finish_group_party_saving(self, members, layer_id, features): try: self._layers[LA_GROUP_PARTY_TABLE][ LAYER].committedFeaturesAdded.disconnect() except TypeError as e: pass message = QCoreApplication.translate( self.WIZARD_NAME, "'{}' 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( self.WIZARD_NAME, "'{}' 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.log.logMessage( "We should have got only one group party... We cannot do anything with {} group parties" .format(len(features)), PLUGIN_NAME, Qgis.Warning) else: fid = features[0].id() if not self._layers[LA_GROUP_PARTY_TABLE][LAYER].getFeature( fid).isValid(): self.log.logMessage( "Feature not found in table Group Party...", PLUGIN_NAME, Qgis.Warning) else: group_party_id = self._layers[LA_GROUP_PARTY_TABLE][ LAYER].getFeature(fid)[ID_FIELD] # 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[MEMBERS_TABLE][ LAYER].committedFeaturesAdded.connect( partial(self.finish_member_saving, fraction)) new_feature = QgsVectorLayerUtils().createFeature( self._layers[MEMBERS_TABLE][LAYER]) new_feature.setAttribute(MEMBERS_GROUP_PARTY_FIELD, group_party_id) new_feature.setAttribute(MEMBERS_PARTY_FIELD, party_id) self.log.logMessage( "Saving group party's member ({}: {}).".format( group_party_id, party_id), PLUGIN_NAME, Qgis.Info) with edit(self._layers[MEMBERS_TABLE][LAYER]): self._layers[MEMBERS_TABLE][LAYER].addFeature( new_feature) party_ids.append(party_id) if len(party_ids): message = QCoreApplication.translate( self.WIZARD_NAME, "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( self.WIZARD_NAME, "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[MEMBERS_TABLE][ LAYER].committedFeaturesAdded.disconnect() except TypeError as e: pass if len(features) != 1: self.log.logMessage( "We should have got only one member... We cannot do anything with {} members" .format(len(features)), PLUGIN_NAME, Qgis.Warning) else: fid = features[0].id() if not self._layers[MEMBERS_TABLE][LAYER].getFeature( fid).isValid(): self.log.logMessage("Feature not found in table Members...", PLUGIN_NAME, Qgis.Warning) else: member_id = self._layers[MEMBERS_TABLE][LAYER].getFeature( fid)[ID_FIELD] if fraction == [0, 0]: return # And finally save fractions new_feature = QgsVectorLayerUtils().createFeature( self._layers[FRACTION_TABLE][LAYER]) new_feature.setAttribute(FRACTION_MEMBER_FIELD, member_id) new_feature.setAttribute(FRACTION_NUMERATOR_FIELD, fraction[0]) new_feature.setAttribute(FRACTION_DENOMINATOR_FIELD, fraction[1]) with edit(self._layers[FRACTION_TABLE][LAYER]): self.log.logMessage( "Saving member's fraction ({}: {}).".format( member_id, fraction), PLUGIN_NAME, Qgis.Info) self._layers[FRACTION_TABLE][LAYER].addFeature(new_feature) def close_wizard(self, message=None, show_message=True): if message is None: message = QCoreApplication.translate( self.WIZARD_NAME, "'{}' tool has been closed.").format(self.WIZARD_TOOL_NAME) if show_message: self.qgis_utils.message_emitted.emit(message, Qgis.Info) self.disconnect_signals() self.close() def disconnect_signals(self): try: self._layers[LA_GROUP_PARTY_TABLE][ LAYER].committedFeaturesAdded.disconnect() except TypeError as e: pass try: self._layers[MEMBERS_TABLE][ LAYER].committedFeaturesAdded.disconnect() except TypeError as e: pass def show_help(self): self.qgis_utils.show_help("group_party")
class ModelerDialog(BASE, WIDGET): CANVAS_SIZE = 4000 def __init__(self, alg=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(1, self.bar) self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QSettings() self.splitter.restoreState(settings.value("/Processing/splitterModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.tabWidget.setCurrentIndex(0) self.scene = ModelerScene(self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = algList.getAlgorithm(text) if alg is not None: self._addAlgorithm(alg.getCopy(), event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) factor = 1.05 if event.angleDelta().y() > 0: factor = 1 / factor self.view.scale(factor, factor) self.repaintModel() def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): QGraphicsView.mousePressEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseReleaseEvent = _mouseReleaseEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.commandLineName()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) # Set icons self.btnOpen.setIcon(QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.btnSave.setIcon(QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.btnSaveAs.setIcon(QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.btnExportImage.setIcon(QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.btnExportPython.setIcon(QgsApplication.getThemeIcon('/console/iconSaveAsConsole.png')) self.btnEditHelp.setIcon(QIcon(os.path.join(pluginPath, 'images', 'edithelp.png'))) self.btnRun.setIcon(QIcon(os.path.join(pluginPath, 'images', 'runalgorithm.png'))) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('[Enter model name here]')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('[Enter group name here]')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.fillAlgorithmTree) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) self.btnOpen.clicked.connect(self.openModel) self.btnSave.clicked.connect(self.save) self.btnSaveAs.clicked.connect(self.saveAs) self.btnExportImage.clicked.connect(self.exportAsImage) self.btnExportPython.clicked.connect(self.exportAsPython) self.btnEditHelp.clicked.connect(self.editHelp) self.btnRun.clicked.connect(self.runModel) if alg is not None: self.alg = alg self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() else: self.alg = ModelerAlgorithm() self.alg.modelerdialog = self self.fillInputsTree() self.fillAlgorithmTree() self.view.centerOn(0, 0) self.alg.setModelerView(self) self.help = None # Indicates whether to update or not the toolbox after # closing this dialog self.update = False self.hasChanged = False def closeEvent(self, evt): settings = QSettings() settings.setValue("/Processing/splitterModeler", self.splitter.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Unsaved changes'), self.tr('There are unsaved changes in model. Continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if ret == QMessageBox.Yes: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = algList.getProviderFromName('model') alg = self.alg.getCopy() dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.alg.helpContent = dlg.descriptions self.hasChanged = True def runModel(self): if len(self.alg.algs) == 0: QMessageBox.warning(self, self.tr('Empty model'), self.tr("Model doesn't contains any algorithms and/or " "parameters and can't be executed")) return if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = algList.getProviderFromName('model') alg = self.alg.getCopy() dlg = AlgorithmDialog(alg) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def exportAsImage(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = QRectF(0, 0, 1, 1) for item in list(self.scene.items()): totalRect = totalRect.united(item.sceneBoundingRect()) totalRect.adjust(-10, -10, 10, 10) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, totalRect, totalRect) painter.end() img.save(filename) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.alg.toPython() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) QMessageBox.information(self, self.tr('Model exported'), self.tr('Model was correctly exported.')) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.alg.name = str(self.textName.text()) self.alg.group = str(self.textGroup.text()) if self.alg.descriptionFile is not None and not saveAs: filename = self.alg.descriptionFile else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model)')) if filename: if not filename.endswith('.model'): filename += '.model' self.alg.descriptionFile = filename if filename: text = self.alg.toJson() try: fout = codecs.open(filename, 'w', encoding='utf-8') except: if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n %s') % str(sys.exc_info()[1])) else: QMessageBox.warning(self, self.tr("Can't save model"), self.tr("This model can't be saved in its " "original location (probably you do not " "have permission to do it). Please, use " "the 'Save as...' option.")) return fout.write(text) fout.close() self.update = True self.bar.pushMessage("", "Model was correctly saved", level=QgsMessageBar.SUCCESS, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = str(QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model *.MODEL)'))) if filename: try: alg = ModelerAlgorithm.fromFile(filename) self.alg = alg self.alg.setModelerView(self) self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False except WrongModelException as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.msg)) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) except Exception as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.args[0])) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self): self.scene = ModelerScene() self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE, ModelerAlgorithm.CANVAS_SIZE)) self.scene.paintModel(self.alg) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.alg, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) self.alg.addParameter(ModelerParameter(dlg.param, pos)) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.inputs: maxX = max([i.pos.x() for i in list(self.alg.inputs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.png')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in ModelerParameterDefinitionDialog.paramTypes: paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = algList.getAlgorithm(item.alg.commandLineName()) self._addAlgorithm(alg.getCopy()) def _addAlgorithm(self, alg, pos=None): dlg = alg.getCustomModelerParametersDialog(self.alg) if not dlg: dlg = ModelerParametersDialog(alg, self.alg) dlg.exec_() if dlg.alg is not None: if pos is None: dlg.alg.pos = self.getPositionForAlgorithmItem() else: dlg.alg.pos = pos if isinstance(dlg.alg.pos, QPoint): dlg.alg.pos = QPointF(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(dlg.alg.outputs): dlg.alg.outputs[out].pos = dlg.alg.pos + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT) self.alg.addAlgorithm(dlg.alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.algs: maxX = max([alg.pos.x() for alg in list(self.alg.algs.values())]) maxY = max([alg.pos.y() for alg in list(self.alg.algs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillAlgorithmTree(self): self.fillAlgorithmTreeUsingProviders() self.algorithmTree.sortItems(0, Qt.AscendingOrder) text = str(self.searchBox.text()) if text != '': self.algorithmTree.expandAll() def fillAlgorithmTreeUsingProviders(self): self.algorithmTree.clear() text = str(self.searchBox.text()) allAlgs = algList.algs for providerName in list(allAlgs.keys()): name = 'ACTIVATE_' + providerName.upper().replace(' ', '_') if not ProcessingConfig.getSetting(name): continue groups = {} algs = list(allAlgs[providerName].values()) # Add algorithms for alg in algs: if not alg.showInModeler: continue if alg.commandLineName() == self.alg.commandLineName(): continue if text == '' or text.lower() in alg.name.lower(): if alg.group in groups: groupItem = groups[alg.group] else: groupItem = QTreeWidgetItem() name = alg.i18n_group or alg.group groupItem.setText(0, name) groupItem.setToolTip(0, name) groups[alg.group] = groupItem algItem = TreeAlgorithmItem(alg) groupItem.addChild(algItem) if len(groups) > 0: providerItem = QTreeWidgetItem() provider = algList.getProviderFromName(providerName) providerItem.setText(0, provider.getDescription()) providerItem.setToolTip(0, provider.getDescription()) providerItem.setIcon(0, provider.getIcon()) for groupItem in list(groups.values()): providerItem.addChild(groupItem) self.algorithmTree.addTopLevelItem(providerItem) providerItem.setExpanded(text != '') for groupItem in list(groups.values()): if text != '': groupItem.setExpanded(True) self.algorithmTree.sortItems(0, Qt.AscendingOrder)
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.setLexers() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python {0} on {1}\n" "## Type help(iface) for more info and list of methods.\n").format( sys.version, socket.gethostname()) ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): initText = self.setText(txtInit) else: initText = self.setText(txtInit + '\n') def refreshLexerProperties(self): self.setLexers() def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon( "console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon( "console/iconSettingsConsole.png") hideToolBar = menu.addAction( iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction( iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction( iconClear, QCoreApplication.translate("PythonConsole", "Clear console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() settingsDialog = menu.addAction( iconSettings, QCoreApplication.translate("PythonConsole", "Settings"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) action = menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class dataCatalog(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") locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', '{}.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_dataCatalogDlg() self.ui.setupUi(self) #get settings self.s = settings() self.gh = gh.geometryHelper( self.iface ) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed ) self.ui.verticalLayout.addWidget(self.bar) #vars self.firstShow = True self.wms = None self.wfs = None self.wmts = None self.wcs = None self.dl = None self.zoek = '' #datamodel self.model = QtGui.QStandardItemModel(self) self.proxyModel = QtGui.QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.ui.resultView.setModel( self.proxyModel ) #completer self.completer = QtGui.QCompleter( self ) self.completerModel = QtGui.QStringListModel(self) self.ui.zoekTxt.setCompleter(self.completer ) self.completer.setModel(self.completerModel ) self.md = metadata.MDReader(timeout=self.s.timeout, proxyUrl=self.s.proxyUrl) #eventhandlers self.ui.zoekBtn.clicked.connect(self.onZoekClicked) self.ui.addWMSbtn.clicked.connect(self.addWMS) self.ui.addWFSbtn.clicked.connect(self.addWFS) self.ui.addWMTSbtn.clicked.connect(self.addWMTS) self.ui.addWCSbtn.clicked.connect(self.addWCS) self.ui.DLbtn.clicked.connect(lambda: self.openUrl(self.dl)) self.ui.resultView.clicked.connect(self.resultViewClicked) self.ui.modelFilterCbx.currentIndexChanged.connect(self.modelFilterCbxIndexChanged) self.finished.connect(self.clean) def _setModel(self, records): self.model.clear() reclist = sorted(records, key=lambda k: k['title']) for rec in reclist: title = QtGui.QStandardItem( rec['title'] ) #0 wms = QtGui.QStandardItem( rec['wms'][1] ) #1 downloadLink = QtGui.QStandardItem(rec['download']) #2 id = QtGui.QStandardItem( rec['uuid'] ) #3 abstract = QtGui.QStandardItem( rec['abstract'] ) #4 wfs = QtGui.QStandardItem( rec['wfs'][1] ) #5 wcs = QtGui.QStandardItem( rec['wcs'][1] ) #6 wmts = QtGui.QStandardItem( rec['wmts'][1] ) #7 ### wmsName = QtGui.QStandardItem( rec['wms'][0] ) #8 wfsName = QtGui.QStandardItem( rec['wfs'][0] ) #9 wcsName = QtGui.QStandardItem( rec['wcs'][0] ) #10 wmtsName = QtGui.QStandardItem( rec['wmts'][0] ) #11 self.model.appendRow([title,wms,downloadLink,id,abstract,wfs,wcs,wmts,wmsName,wfsName,wcsName,wmtsName]) #overwrite def show(self): QtGui.QDialog.show(self) self.setWindowModality(0) if self.firstShow: inet = internet_on( self.s.proxyUrl ) if inet: self.ui.organisatiesCbx.addItems( ['']+ self.md.list_organisations() ) keywords = sorted( self.md.list_suggestionKeyword() ) self.completerModel.setStringList( keywords ) self.bronnen = self.md.list_bronnen() #self.ui.keywordCbx.addItems( ['']+ keywords ) self.ui.typeCbx.addItems( ['']+ [ n[0] for n in self.md.dataTypes] ) self.ui.INSPIREannexCbx.addItems( ['']+ self.md.inspireannex ) self.ui.INSPIREserviceCbx.addItems( ['']+ self.md.inspireServiceTypes ) self.ui.INSPIREthemaCbx.addItems( ['']+ self.md.list_inspire_theme() ) self.firstShow = False else: self.bar.pushMessage( QtCore.QCoreApplication.translate("datacatalog", "Waarschuwing "), QtCore.QCoreApplication.translate("datacatalog", "Kan geen verbing maken met het internet."), level=QgsMessageBar.WARNING, duration=3) #eventhandlers def resultViewClicked(self): if self.ui.resultView.selectedIndexes(): row = self.ui.resultView.selectedIndexes()[0].row() self.wms = self.proxyModel.data( self.proxyModel.index( row, 1) ) self.dl = self.proxyModel.data( self.proxyModel.index( row, 2) ) self.wfs = self.proxyModel.data( self.proxyModel.index( row, 5) ) self.wcs = self.proxyModel.data( self.proxyModel.index( row, 6) ) self.wmts= self.proxyModel.data( self.proxyModel.index( row, 7) ) ## self.wmsLr = self.proxyModel.data( self.proxyModel.index( row, 8) ) self.wfsLr = self.proxyModel.data( self.proxyModel.index( row, 9) ) self.wcsLr = self.proxyModel.data( self.proxyModel.index( row, 10) ) self.wmtsLr = self.proxyModel.data( self.proxyModel.index( row, 11) ) ## title = self.proxyModel.data( self.proxyModel.index( row, 0) ) uuid = self.proxyModel.data( self.proxyModel.index( row, 3) ) abstract = self.proxyModel.data( self.proxyModel.index( row, 4) ) self.ui.descriptionText.setText( """<h3>%s</h3><div>%s</div><br/><br/> <a href='http://www.nationaalgeoregister.nl/geonetwork/srv/search/?uuid=%s'> Bekijk in Nationaal Georegister</a>""" % (title , abstract, uuid )) if self.wms: self.ui.addWMSbtn.setEnabled(1) else: self.ui.addWMSbtn.setEnabled(0) if self.wfs: self.ui.addWFSbtn.setEnabled(1) else: self.ui.addWFSbtn.setEnabled(0) if self.wmts: self.ui.addWMTSbtn.setEnabled(1) else: self.ui.addWMTSbtn.setEnabled(0) if self.wcs: self.ui.addWCSbtn.setEnabled(1) else: self.ui.addWCSbtn.setEnabled(0) if self.dl: self.ui.DLbtn.setEnabled(1) else: self.ui.DLbtn.setEnabled(0) def onZoekClicked(self): self.zoek = self.ui.zoekTxt.currentText() self.search() def modelFilterCbxIndexChanged(self): value = self.ui.modelFilterCbx.currentIndex() if value == 1: self.filterModel(1) elif value == 2: self.filterModel(5) elif value == 3: self.filterModel(2) elif value == 4: self.filterModel(6) elif value == 5: self.filterModel(7) else: self.filterModel() def filterModel(self, col=None): if col != None: self.proxyModel.setFilterKeyColumn(col) expr = QtCore.QRegExp("?*", QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard ) self.proxyModel.setFilterRegExp(expr) else: self.proxyModel.setFilterRegExp(None) def search(self): try: QtGui.QApplication.setOverrideCursor( QtGui.QCursor(QtCore.Qt.WaitCursor)) if self.ui.filterBox.isChecked(): orgName= self.ui.organisatiesCbx.currentText() dataTypes= [ n[1] for n in self.md.dataTypes if n[0] == self.ui.typeCbx.currentText()] if dataTypes != []: dataType= dataTypes[0] else: dataType='' #siteIds = [ n[0] for n in self.bronnen if n[1] == self.ui.bronCbx.currentText() ] #if siteIds != []: siteId= siteIds[0] #else: siteId ='' #keyword = self.ui.keywordCbx.currentText() inspiretheme= self.ui.INSPIREthemaCbx.currentText() inspireannex= self.ui.INSPIREannexCbx.currentText() inspireServiceType= self.ui.INSPIREserviceCbx.currentText() searchResult = metadata.MDdata( self.md.searchAll( self.zoek, orgName, dataType, None, inspiretheme, inspireannex, inspireServiceType)) else: searchResult = metadata.MDdata( self.md.searchAll( self.zoek )) except: self.bar.pushMessage("Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=3) return finally: QtGui.QApplication.restoreOverrideCursor() self.ui.countLbl.setText( "Aantal gevonden: %s" % searchResult.count ) self.ui.descriptionText.setText('') self._setModel(searchResult.records) if searchResult.count == 0: self.bar.pushMessage( QtCore.QCoreApplication.translate("datacatalog", "Waarschuwing "), QtCore.QCoreApplication.translate("datacatalog", "Er zijn geen resultaten gevonden voor deze zoekopdracht"), duration=5) def openUrl(self, url): if url: webbrowser.open_new_tab( url.encode("utf-8") ) def addWMS(self): if self.wms == None: return crs = self.gh.getGetMapCrs(self.iface).authid() if crs != 'EPSG:28992' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:28992' try: lyrs = metadata.getWmsLayerNames( self.wms, self.s.proxyUrl ) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WMS", QtCore.QCoreApplication.translate("datacatalog", "Kan geen lagen vinden in: %s" % self.wms ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, "WMS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle ][0] style = [n[2] for n in lyrs if n[1] == layerTitle ][0] url= self.wms.split('?')[0] if crs != 'EPSG:28992' or crs != 'EPSG:3857' : crs = 'EPSG:28992' wmsUrl = "url=%s&layers=%s&format=image/png&styles=%s&crs=%s" % (url, layerName, style , crs) try: rlayer = QgsRasterLayer(wmsUrl, layerTitle, 'wms') if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("datacatalog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def addWFS(self): if self.wfs == None: return try: lyrs = metadata.getWFSLayerNames( self.wfs, self.s.proxyUrl ) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WFS", QtCore.QCoreApplication.translate("datacatalog", "Kan geen lagen vinden in: %s" % self.wfs ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, QtCore.QCoreApplication.translate("datacatalog", "WFS toevoegen"), QtCore.QCoreApplication.translate("datacatalog", "Kies een laag om toe te voegen"), [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle ][0] crs = [n[2] for n in lyrs if n[1] == layerTitle ][0] url = self.wfs.split('?')[0] if self.ui.wfsBboxchk.isChecked(): extent = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs([extent.xMinimum(),extent.yMinimum()], int(crs.split(":")[-1]) ) maxX, maxY = self.gh.prjPtFromMapCrs([extent.xMaximum(),extent.yMaximum()], int(crs.split(":")[-1]) ) bbox = [minX, minY, maxX, maxY] else: bbox = None ###wfsUri = "WFS:{0}|{1}".format(url,layerName) wfsUri = metadata.makeWFSuri( url, layerName, crs, bbox=bbox ) try: vlayer = QgsVectorLayer( wfsUri, layerTitle , "WFS") ###vlayer = QgsVectorLayer( wfsUri, layerTitle , "ogr") QgsMapLayerRegistry.instance().addMapLayer(vlayer) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def addWMTS(self): if self.wmts == None: return try: lyrs = metadata.getWMTSlayersNames( self.wmts, self.s.proxyUrl ) except: self.bar.pushMessage("Error",'Kan niet connecteren met '+ self.wmts, level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WMTS", QtCore.QCoreApplication.translate("datacatalog", "Kan geen lagen vinden in: %s" % self.wmts ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, "WMTS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle ][0] matrix = [n[2] for n in lyrs if n[1] == layerTitle ][0] frmt = [n[3] for n in lyrs if n[1] == layerTitle ][0] srs = [n[4] for n in lyrs if n[1] == layerTitle ][0] wmtsUrl= metadata.makeWMTSuri(self.wmts , layerName, matrix, format=frmt, srsname=srs) try: rlayer = QgsRasterLayer(wmtsUrl, layerTitle, 'wms') if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("datacatalog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def addWCS(self): try: QtGui.QApplication.setOverrideCursor( QtGui.QCursor(QtCore.Qt.WaitCursor)) lyrs = metadata.getWCSlayerNames( self.wcs, self.s.proxyUrl ) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return finally: QtGui.QApplication.restoreOverrideCursor() if len(lyrs) == 0: self.bar.pushMessage("WCS", QtCore.QCoreApplication.translate("datacatalog", "Kan geen lagen vinden in: %s" % self.wcs ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, "WCS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle ][0] layerFormat = [n[2] for n in lyrs if n[1] == layerTitle ][0] srs = [n[3] for n in lyrs if n[1] == layerTitle ][0] axis = [n[4] for n in lyrs if n[1] == layerTitle ][0] wcsUri = metadata.makeWCSuri(self.wcs, layerName, srsname=srs, invertAxis=axis ) try: rlayer = QgsRasterLayer( wcsUri, layerTitle , "wcs") if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) if rlayer.bandCount() > 1: rlayer.setDrawingStyle("MultiBandSingleBandGray") else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("datacatalog", "Kan WCS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def clean(self): self.model.clear() self.wms = None self.wfs = None self.dl = None self.wmts = None self.wcs = None self.ui.zoekTxt.setCurrentIndex(0) self.ui.descriptionText.setText('') self.ui.countLbl.setText( "") self.ui.msgLbl.setText("" ) self.ui.DLbtn.setEnabled(0) self.ui.addWFSbtn.setEnabled(0) self.ui.addWMTSbtn.setEnabled(0) self.ui.addWMSbtn.setEnabled(0) self.ui.addWCSbtn.setEnabled(0) self.ui.modelFilterCbx.setCurrentIndex(0)
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 = QgsProcessingUtils.compatibleLayers(QgsProject.instance()) 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.getLayerFromString(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 {0} starting...</b>').format( self.alg.displayName())) if self.iterateParam: if executeIterating(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 execute(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( "", self.tr("Wrong or missing parameter value: {0}").format( 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( self.tr('Algorithm {0} finished').format(self.alg.displayName())) 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 ModelerDialog(BASE, WIDGET): CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, alg=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = algList.getAlgorithm(text) if alg is not None: self._addAlgorithm(alg.getCopy(), event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) self.repaintModel() def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): QGraphicsView.mousePressEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.commandLineName()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.fillAlgorithmTree) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) iconSize = settings.value("iconsize", 24) self.mToolbar.setIconSize(QSize(iconSize, iconSize)) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if alg is not None: self.alg = alg self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() else: self.alg = ModelerAlgorithm() self.alg.modelerdialog = self self.fillInputsTree() self.fillAlgorithmTree() self.view.centerOn(0, 0) self.alg.setModelerView(self) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save?'), self.tr('There are unsaved changes in this model, do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = QgsApplication.processingRegistry().providerById('model') alg = self.alg.getCopy() dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.alg.helpContent = dlg.descriptions self.hasChanged = True def runModel(self): if len(self.alg.algs) == 0: self.bar.pushMessage("", "Model doesn't contain any algorithm and/or parameter and can't be executed", level=QgsMessageBar.WARNING, duration=5) return if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = QgsApplication.processingRegistry().providerById('model') alg = self.alg.getCopy() dlg = AlgorithmDialog(alg) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('SVG files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.alg.name) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as SVG", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.alg.toPython() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", "Model was correctly exported as python script", level=QgsMessageBar.SUCCESS, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.alg.name = str(self.textName.text()) self.alg.group = str(self.textGroup.text()) if self.alg.descriptionFile is not None and not saveAs: filename = self.alg.descriptionFile else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model)')) if filename: if not filename.endswith('.model'): filename += '.model' self.alg.descriptionFile = filename if filename: text = self.alg.toJson() try: with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) except: if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n %s') % str(sys.exc_info()[1])) else: QMessageBox.warning(self, self.tr("Can't save model"), self.tr("This model can't be saved in its " "original location (probably you do not " "have permission to do it). Please, use " "the 'Save as...' option.")) return self.update_model.emit() self.bar.pushMessage("", "Model was correctly saved", level=QgsMessageBar.SUCCESS, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = str(QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model *.MODEL)'))) if filename: try: alg = ModelerAlgorithm.fromFile(filename) self.alg = alg self.alg.setModelerView(self) self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False except WrongModelException as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.msg)) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) except Exception as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.args[0])) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene() self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE, ModelerAlgorithm.CANVAS_SIZE)) self.scene.paintModel(self.alg, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.alg, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) self.alg.addParameter(ModelerParameter(dlg.param, pos)) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.inputs: maxX = max([i.pos.x() for i in list(self.alg.inputs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in ModelerParameterDefinitionDialog.paramTypes: paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = algList.getAlgorithm(item.alg.commandLineName()) self._addAlgorithm(alg.getCopy()) def _addAlgorithm(self, alg, pos=None): dlg = alg.getCustomModelerParametersDialog(self.alg) if not dlg: dlg = ModelerParametersDialog(alg, self.alg) dlg.exec_() if dlg.alg is not None: if pos is None: dlg.alg.pos = self.getPositionForAlgorithmItem() else: dlg.alg.pos = pos if isinstance(dlg.alg.pos, QPoint): dlg.alg.pos = QPointF(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(dlg.alg.outputs): dlg.alg.outputs[out].pos = dlg.alg.pos + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT) self.alg.addAlgorithm(dlg.alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.algs: maxX = max([alg.pos.x() for alg in list(self.alg.algs.values())]) maxY = max([alg.pos.y() for alg in list(self.alg.algs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillAlgorithmTree(self): self.fillAlgorithmTreeUsingProviders() self.algorithmTree.sortItems(0, Qt.AscendingOrder) text = str(self.searchBox.text()) if text != '': self.algorithmTree.expandAll() def fillAlgorithmTreeUsingProviders(self): self.algorithmTree.clear() text = str(self.searchBox.text()) search_strings = text.split(' ') allAlgs = algList.algs for provider_id in list(allAlgs.keys()): name = 'ACTIVATE_' + provider_id.upper().replace(' ', '_') if not ProcessingConfig.getSetting(name): continue groups = {} algs = list(allAlgs[provider_id].values()) # Add algorithms for alg in algs: if not alg.showInModeler: continue if alg.commandLineName() == self.alg.commandLineName(): continue item_text = [alg.name.lower()] item_text.extend(alg.tags.split(',')) show = not search_strings or all( any(part in t for t in item_text) for part in search_strings) if show: if alg.group in groups: groupItem = groups[alg.group] else: groupItem = QTreeWidgetItem() name = alg.i18n_group or alg.group groupItem.setText(0, name) groupItem.setToolTip(0, name) groups[alg.group] = groupItem algItem = TreeAlgorithmItem(alg) groupItem.addChild(algItem) if len(groups) > 0: providerItem = QTreeWidgetItem() provider = QgsApplication.processingRegistry().providerById(provider_id) providerItem.setText(0, provider.name()) providerItem.setToolTip(0, provider.name()) providerItem.setIcon(0, provider.icon()) for groupItem in list(groups.values()): providerItem.addChild(groupItem) self.algorithmTree.addTopLevelItem(providerItem) providerItem.setExpanded(text != '') for groupItem in list(groups.values()): if text != '': groupItem.setExpanded(True) self.algorithmTree.sortItems(0, Qt.AscendingOrder)
class SaveQueryDialog(QDialog, Ui_ui_save_query): # Signal new query signal_new_query_successful = pyqtSignal( name='signal_new_query_successful') def __init__(self, parent=None, query=None, white_list_values=None, output_geometry_types=None): """ SaveQueryDialog constructor @param query:query to save @type query: str @param white_list_values: doc of layers with columns @type white_list_values: dic @param output_geometry_types: list of layers @type output_geometry_types: list """ super(SaveQueryDialog, self).__init__(parent) QDialog.__init__(self) self.setupUi(self) self.message_bar = QgsMessageBar() self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.message_bar) self.white_list_values = white_list_values self.output_geometry_types = output_geometry_types self.query = query def accept(self): """ On accept, we call the FileQueryWriter """ category = self.lineEdit_category.text() name = self.lineEdit_name.text() # Get folder .qgis2/QuickOSM/queries on linux for instance folder = get_user_query_folder() ini_file = FileQueryWriter( path=folder, name=name, category=category, query=self.query, white_list_values=self.white_list_values, output_geometry_types=self.output_geometry_types) try: if not category: raise MissingParameterException(suffix='category') if not name: raise MissingParameterException(suffix='name') ini_file.save() self.signal_new_query_successful.emit() self.hide() except QuickOsmException, e: self.message_bar.pushMessage(e.msg, level=e.level, duration=e.duration) except Exception, e: # pylint: disable=broad-except self.display_exception(e)
class SymbologySharingDialog(QtGui.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(SymbologySharingDialog, self).__init__(parent) self.setupUi(self) self.iface = iface self.repository_manager = RepositoryManager() # Init the message bar self.message_bar = QgsMessageBar(self) self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.vlayoutRightColumn.insertWidget(0, self.message_bar) # Mock plugin manager dialog self.resize(796, 594) self.setMinimumSize(QSize(790, 0)) self.setModal(True) self.button_edit.setEnabled(False) self.button_delete.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) # 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.menu_list_widget.currentRowChanged.connect(self.set_current_tab) # Creating progress dialog for downloading stuffs self.progress_dialog = QProgressDialog(self) self.progress_dialog.setAutoClose(False) title = self.tr('Symbology Sharing') self.progress_dialog.setWindowTitle(title) # Populate tree repositories with registered repositories self.populate_tree_repositories() 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 """ if index == (self.menu_list_widget.count() - 1): # Switch to settings tab self.stacked_menu_widget.setCurrentIndex(1) else: # Switch to plugins tab 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.repositories.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!'), QgsMessageBar.WARNING, 5) return repo_name = dlg.line_edit_name.text() repo_url = dlg.line_edit_url.text().strip() if repo_name in self.repository_manager.repositories: repo_name += '(2)' settings = QSettings() settings.beginGroup(repo_settings_group()) settings.setValue(repo_name + '/url', repo_url) # Fetch metadata #TODO: Wrap RemoteRepository class into RepositoryManager # This dialod will only need to call RepositoryManager. # RepositoryManager will take care of the rest remote_repository = RemoteRepository(repo_url) remote_repository.fetch_metadata(self.progress_dialog) # Show metadata #TODO: Process this instead of showing it on message box :) QMessageBox.information( self, self.tr("Test"), remote_repository.metadata.data()) # Refresh tree repository self.refresh_tree_repositories() self.set_enabled_edit_delete_button(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 dlg = ManageRepositoryDialog(self) dlg.line_edit_name.setText(repo_name) dlg.line_edit_url.setText( self.repository_manager.repositories[repo_name]['url']) 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.repositories[repo_name]['url'] for repo in self.repository_manager.repositories.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!'), QgsMessageBar.WARNING, 5) return # Delete old repo and create a new entry settings = QSettings() settings.beginGroup(repo_settings_group()) settings.remove(repo_name) new_name = dlg.line_edit_name.text() if new_name in self.repository_manager.repositories and new_name != repo_name: new_name += '(2)' settings.setValue(new_name + '/url', new_url) # Refresh tree repository self.refresh_tree_repositories() self.set_enabled_edit_delete_button(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 official repository settings = QSettings() settings.beginGroup(repo_settings_group()) if settings.value(repo_name + '/url') == self.repository_manager.OFFICIAL_REPO[1]: self.message_bar.pushMessage( self.tr( 'You can not remove the official repository!'), QgsMessageBar.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 Symbology Sharing"), warning, QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return settings.remove(repo_name) # Refresh tree repository self.refresh_tree_repositories() self.set_enabled_edit_delete_button(False) def refresh_tree_repositories(self): """Refresh tree repositories using new repositories data.""" self.repository_manager.load() self.populate_tree_repositories() def populate_tree_repositories(self): """Populate 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.repositories: url = self.repository_manager.repositories[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 on_tree_repositories_itemSelectionChanged(self): """Slot for when the itemSelectionChanged signal emitted.""" # Activate edit and delete button self.set_enabled_edit_delete_button(True) def set_enabled_edit_delete_button(self, is_enabled): """Disable edit and delete button. :param is_enabled: Boolean is enabled or not. :type is_enabled: bool """ self.button_edit.setEnabled(is_enabled) self.button_delete.setEnabled(is_enabled)
class ModelerDialog(BASE, WIDGET): CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, alg=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = algList.getAlgorithm(text) if alg is not None: self._addAlgorithm(alg.getCopy(), event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) self.repaintModel() def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.commandLineName()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.fillAlgorithmTree) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) iconSize = settings.value("iconsize", 24) self.mToolbar.setIconSize(QSize(iconSize, iconSize)) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if alg is not None: self.alg = alg self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() else: self.alg = ModelerAlgorithm() self.alg.modelerdialog = self self.fillInputsTree() self.fillAlgorithmTree() self.view.centerOn(0, 0) self.alg.setModelerView(self) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save?'), self.tr('There are unsaved changes in this model, do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = QgsApplication.processingRegistry().providerById('model') alg = self.alg.getCopy() dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.alg.helpContent = dlg.descriptions self.hasChanged = True def runModel(self): if len(self.alg.algs) == 0: self.bar.pushMessage("", "Model doesn't contain any algorithm and/or parameter and can't be executed", level=QgsMessageBar.WARNING, duration=5) return if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = QgsApplication.processingRegistry().providerById('model') alg = self.alg.getCopy() dlg = AlgorithmDialog(alg) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.alg.name) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as SVG", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.alg.toPython() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", "Model was correctly exported as python script", level=QgsMessageBar.SUCCESS, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.alg.name = str(self.textName.text()) self.alg.group = str(self.textGroup.text()) if self.alg.descriptionFile is not None and not saveAs: filename = self.alg.descriptionFile else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model)')) if filename: if not filename.endswith('.model'): filename += '.model' self.alg.descriptionFile = filename if filename: text = self.alg.toJson() try: with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) except: if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format(str(sys.exc_info()[1]))) else: QMessageBox.warning(self, self.tr("Can't save model"), self.tr("This model can't be saved in its " "original location (probably you do not " "have permission to do it). Please, use " "the 'Save as...' option.")) return self.update_model.emit() self.bar.pushMessage("", "Model was correctly saved", level=QgsMessageBar.SUCCESS, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model *.MODEL)')) if filename: try: alg = ModelerAlgorithm.fromFile(filename) self.alg = alg self.alg.setModelerView(self) self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False except WrongModelException as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model {0}\n{1}').format(filename, e.msg)) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) except Exception as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model {0}\n{1}').format(filename, e.args[0])) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene() self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE, ModelerAlgorithm.CANVAS_SIZE)) self.scene.paintModel(self.alg, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.alg, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) self.alg.addParameter(ModelerParameter(dlg.param, pos)) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.inputs: maxX = max([i.pos.x() for i in list(self.alg.inputs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in ModelerParameterDefinitionDialog.paramTypes: paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = algList.getAlgorithm(item.alg.commandLineName()) self._addAlgorithm(alg.getCopy()) def _addAlgorithm(self, alg, pos=None): dlg = alg.getCustomModelerParametersDialog(self.alg) if not dlg: dlg = ModelerParametersDialog(alg, self.alg) dlg.exec_() if dlg.alg is not None: if pos is None: dlg.alg.pos = self.getPositionForAlgorithmItem() else: dlg.alg.pos = pos if isinstance(dlg.alg.pos, QPoint): dlg.alg.pos = QPointF(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(dlg.alg.outputs): dlg.alg.outputs[out].pos = dlg.alg.pos + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT) self.alg.addAlgorithm(dlg.alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.algs: maxX = max([alg.pos.x() for alg in list(self.alg.algs.values())]) maxY = max([alg.pos.y() for alg in list(self.alg.algs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillAlgorithmTree(self): self.fillAlgorithmTreeUsingProviders() self.algorithmTree.sortItems(0, Qt.AscendingOrder) text = str(self.searchBox.text()) if text != '': self.algorithmTree.expandAll() def fillAlgorithmTreeUsingProviders(self): self.algorithmTree.clear() text = str(self.searchBox.text()) search_strings = text.split(' ') allAlgs = algList.algs for provider_id in list(allAlgs.keys()): name = 'ACTIVATE_' + provider_id.upper().replace(' ', '_') if not ProcessingConfig.getSetting(name): continue groups = {} algs = list(allAlgs[provider_id].values()) # Add algorithms for alg in algs: if not alg.showInModeler: continue if alg.commandLineName() == self.alg.commandLineName(): continue item_text = [alg.name.lower()] item_text.extend(alg.tags.split(',')) show = not search_strings or all( any(part in t for t in item_text) for part in search_strings) if show: if alg.group in groups: groupItem = groups[alg.group] else: groupItem = QTreeWidgetItem() name = alg.i18n_group or alg.group groupItem.setText(0, name) groupItem.setToolTip(0, name) groups[alg.group] = groupItem algItem = TreeAlgorithmItem(alg) groupItem.addChild(algItem) if len(groups) > 0: providerItem = QTreeWidgetItem() provider = QgsApplication.processingRegistry().providerById(provider_id) providerItem.setText(0, provider.name()) providerItem.setToolTip(0, provider.name()) providerItem.setIcon(0, provider.icon()) for groupItem in list(groups.values()): providerItem.addChild(groupItem) self.algorithmTree.addTopLevelItem(providerItem) providerItem.setExpanded(text != '') for groupItem in list(groups.values()): if text != '': groupItem.setExpanded(True) self.algorithmTree.sortItems(0, Qt.AscendingOrder)
class ModelerParametersDialog(QDialog): ENTER_NAME = '[Enter name if this is a final result]' NOT_SELECTED = '[Not selected]' USE_MIN_COVERING_EXTENT = '[Use min covering extent]' def __init__(self, alg, model, algName=None): QDialog.__init__(self) self.setModal(True) # The algorithm to define in this dialog. It is an instance of GeoAlgorithm self._alg = alg # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class self.alg = None # The model this algorithm is going to be added to self.model = model # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self._algName = algName self.setupUi() self.params = None def setupUi(self): self.labels = {} self.widgets = {} self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) tooltips = self._alg.getParameterDescriptions() self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.name) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) for param in self._alg.parameters: if param.isAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr('(xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr('(x, y)') if param.optional: desc += self.tr(' [optional]') label = QLabel(desc) self.labels[param.name] = label wrapper = param.wrapper(self) self.wrappers[param.name] = wrapper widget = wrapper.widget if widget: self.valueItems[param.name] = widget if param.name in list(tooltips.keys()): tooltip = tooltips[param.name] else: tooltip = param.description label.setToolTip(tooltip) widget.setToolTip(tooltip) if param.isAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.widgets[param.name] = widget self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for output in self._alg.outputs: if output.hidden: continue if isinstance(output, (OutputRaster, OutputVector, OutputTable, OutputHTML, OutputFile, OutputDirectory)): label = QLabel(output.description + '<' + output.__class__.__name__ + '>') item = QLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[output.name] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.name) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.tabWidget = QTabWidget() self.tabWidget.setMinimumWidth(300) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.tabWidget.addTab(self.scrollArea, self.tr('Parameters')) self.txtHelp = QTextBrowser() html = None isText, algHelp = self._alg.help() if algHelp is not None: algHelp = algHelp if isText else QUrl(algHelp) try: if isText: self.txtHelp.setHtml(algHelp) else: html = self.tr('<p>Downloading algorithm help... Please wait.</p>') self.txtHelp.setHtml(html) self.tabWidget.addTab(self.txtHelp, 'Help') self.reply = QgsNetworkAccessManager.instance().get(QNetworkRequest(algHelp)) self.reply.finished.connect(self.requestFinished) except: pass self.verticalLayout2.addWidget(self.tabWidget) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def requestFinished(self): """Change the webview HTML content""" reply = self.sender() if reply.error() != QNetworkReply.NoError: html = self.tr('<h2>No help available for this algorithm</h2><p>{}</p>'.format(reply.errorString())) else: html = str(reply.readAll()) reply.deleteLater() self.txtHelp.setHtml(html) def getAvailableDependencies(self): if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) opts = [] for alg in list(self.model.algs.values()): if alg.name not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description for alg in self.getAvailableDependencies()]) def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameters: if param.isAdvanced: self.labels[param.name].setVisible(self.showAdvanced) self.widgets[param.name].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outType=None, dataType=None): # upgrade paramType to list if type(paramType) is not list: paramType = [paramType] values = [] inputs = self.model.inputs for i in list(inputs.values()): param = i.param for t in paramType: if isinstance(param, t): if dataType is not None: if param.datatype in dataType: values.append(ValueFromInput(param.name)) else: values.append(ValueFromInput(param.name)) break if outType is None: return values if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) for alg in list(self.model.algs.values()): if alg.name not in dependent: for out in alg.algorithm.outputs: if isinstance(out, outType): if dataType is not None and out.datatype in dataType: values.append(ValueFromOutput(alg.name, out.name)) else: values.append(ValueFromOutput(alg.name, out.name)) return values def resolveValueDescription(self, value): if isinstance(value, ValueFromInput): return self.model.inputs[value.name].param.description else: alg = self.model.algs[value.alg] return self.tr("'%s' from algorithm '%s'") % (alg.algorithm.getOutputFromName(value.output).description, alg.description) def setPreviousValues(self): if self._algName is not None: alg = self.model.algs[self._algName] self.descriptionBox.setText(alg.description) for param in alg.algorithm.parameters: if param.hidden: continue if param.name in alg.params: value = alg.params[param.name] else: value = param.default self.wrappers[param.name].setValue(value) for name, out in list(alg.outputs.items()): self.valueItems[name].setText(out.description) selected = [] dependencies = self.getAvailableDependencies() for idx, dependency in enumerate(dependencies): if dependency.name in alg.dependencies: selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = Algorithm(self._alg.commandLineName()) alg.setName(self.model) alg.description = self.descriptionBox.text() params = self._alg.parameters outputs = self._alg.outputs for param in params: if param.hidden: continue if not self.setParamValue(alg, param, self.wrappers[param.name]): self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description, level=QgsMessageBar.WARNING) return None for output in outputs: if not output.hidden: name = str(self.valueItems[output.name].text()) if name.strip() != '' and name != ModelerParametersDialog.ENTER_NAME: alg.outputs[output.name] = ModelerOutput(name) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() for selected in selectedOptions: alg.dependencies.append(availableDependencies[selected].name) self._alg.processBeforeAddingToModeler(alg, self.model) return alg def setParamValue(self, alg, param, wrapper): try: if wrapper.widget: value = wrapper.value() alg.params[param.name] = value return True except InvalidParameterValue: return False def okPressed(self): self.alg = self.createAlgorithm() if self.alg is not None: self.close() def cancelPressed(self): self.alg = None self.close()
class DialogImportData(QDialog, DIALOG_UI): def __init__(self, iface, db, qgis_utils): QDialog.__init__(self) self.setupUi(self) self.layout().setSizeConstraint(QLayout.SetFixedSize) QgsGui.instance().enableAutoGeometryRestore(self) self.iface = iface self.db = db self.qgis_utils = qgis_utils self.base_configuration = BaseConfiguration() self.ilicache = IliCache(self.base_configuration) self.ilicache.refresh() self._conf_db = ConfigDbSupported() self._params = None self._current_db = None self.xtf_file_browse_button.clicked.connect( make_file_selector( self.xtf_file_line_edit, title=QCoreApplication.translate( "DialogImportData", 'Open Transfer or Catalog File'), file_filter=QCoreApplication.translate( "DialogImportData", 'Transfer File (*.xtf *.itf);;Catalogue File (*.xml *.xls *.xlsx)' ))) self.validators = Validators() self.xtf_file_line_edit.setPlaceholderText( QCoreApplication.translate("DialogImportData", "[Name of the XTF to be created]")) fileValidator = FileValidator(pattern=['*.xtf', '*.itf', '*.xml']) self.xtf_file_line_edit.setValidator(fileValidator) self.xtf_file_line_edit.textChanged.connect(self.update_import_models) self.xtf_file_line_edit.textChanged.emit( self.xtf_file_line_edit.text()) # db self.connection_setting_button.clicked.connect(self.show_settings) self.connection_setting_button.setText( QCoreApplication.translate("DialogImportData", 'Connection Settings')) # LOG self.log_config.setTitle( QCoreApplication.translate("DialogImportData", "Show log")) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) self._accept_button = self.buttonBox.addButton( QCoreApplication.translate("DialogImportData", "Import data"), QDialogButtonBox.AcceptRole) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.show_help) self.update_connection_info() self.restore_configuration() def update_connection_info(self): db_description = self.db.get_description_conn_string() if db_description: self.db_connect_label.setText(db_description) self.db_connect_label.setToolTip(self.db.get_display_conn_string()) self._accept_button.setEnabled(True) else: self.db_connect_label.setText( QCoreApplication.translate("DialogImportData", "The database is not defined!")) self.db_connect_label.setToolTip('') self._accept_button.setEnabled(False) def update_import_models(self): message_error = None if not self.xtf_file_line_edit.text().strip(): color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel(self.import_models_qmodel) else: if os.path.isfile(self.xtf_file_line_edit.text().strip()): color = '#fff' # White self.import_models_qmodel = QStandardItemModel() models_name = self.find_models_xtf( self.xtf_file_line_edit.text().strip()) for model_name in models_name: if not model_name in DEFAULT_HIDDEN_MODELS: item = QStandardItem(model_name) item.setCheckable(False) item.setEditable(False) self.import_models_qmodel.appendRow(item) if self.import_models_qmodel.rowCount() > 0: self.import_models_list_view.setModel( self.import_models_qmodel) else: message_error = QCoreApplication.translate( "DialogImportData", "No models were found in the XTF. Is it a valid file?") color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel( self.import_models_qmodel) else: message_error = QCoreApplication.translate( "DialogImportData", "Please set a valid XTF file") color = '#ffd356' # Light orange self.import_models_qmodel = QStandardItemModel() self.import_models_list_view.setModel( self.import_models_qmodel) self.xtf_file_line_edit.setStyleSheet( 'QLineEdit {{ background-color: {} }}'.format(color)) if message_error: self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.import_models_list_view.setFocus() return def find_models_xtf(self, xtf_path): models_name = list() pattern = re.compile(r'<MODEL[^>]*>(?P<text>[^<]*)</MODEL>') with open(xtf_path, 'r') as f: for txt in pattern.finditer(f.read()): model_tag = str(txt.group(0)) name = re.findall('NAME="(.*?)"', model_tag, re.IGNORECASE) models_name.extend(name) return models_name def get_ili_models(self): ili_models = list() for index in range(self.import_models_qmodel.rowCount()): item = self.import_models_qmodel.item(index) ili_models.append(item.text()) return ili_models def show_settings(self): dlg = self.qgis_utils.get_settings_dialog() dlg.set_action_type(EnumDbActionType.IMPORT) dlg.tabWidget.setCurrentIndex(SETTINGS_CONNECTION_TAB_INDEX) if dlg.exec_(): self.db = dlg.get_db_connection() self.update_connection_info() def accepted(self): configuration = self.update_configuration() if not os.path.isfile(self.xtf_file_line_edit.text().strip()): message_error = 'Please set a valid XTF file before importing data. XTF file does not exist' self.txtStdout.setText( QCoreApplication.translate("DialogImportData", message_error)) self.show_message(message_error, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.xtf_file_line_edit.validator().validate( configuration.xtffile, 0)[0] == QValidator.Acceptable: message_error = 'Please set a valid XTF before importing data.' self.txtStdout.setText( QCoreApplication.translate("DialogImportData", message_error)) self.show_message(message_error, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.get_ili_models(): message_error = QCoreApplication.translate( "DialogImportData", "The selected XTF file does not have information according to the LADM-COL model to import." ) self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.import_models_list_view.setFocus() return with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.progress_bar.setValue(0) self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() dataImporter = iliimporter.Importer(dataImport=True) item_db = self._conf_db.get_db_items()[self.db.mode] dataImporter.tool_name = item_db.get_model_baker_tool_name() dataImporter.configuration = configuration self.save_configuration(configuration) dataImporter.stdout.connect(self.print_info) dataImporter.stderr.connect(self.on_stderr) dataImporter.process_started.connect(self.on_process_started) dataImporter.process_finished.connect(self.on_process_finished) self.progress_bar.setValue(25) try: if dataImporter.run() != iliimporter.Importer.SUCCESS: self.enable() self.progress_bar.hide() self.show_message( QCoreApplication.translate( "DialogImportData", "An error occurred when importing the data. For more information see the log..." ), Qgis.Warning) return except JavaNotFoundError: # Set JAVA PATH get_java_path_dlg = DialogGetJavaPath() get_java_path_dlg.setModal(True) if get_java_path_dlg.exec_(): configuration = self.update_configuration() if not get_java_path_from_qgis_model_baker(): message_error_java = QCoreApplication.translate( "DialogImportData", """Java could not be found. You can configure the JAVA_HOME environment variable, restart QGIS and try again.""" ) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(message_error_java) self.show_message(message_error_java, Qgis.Warning) self.enable() self.progress_bar.hide() return self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) self.show_message( QCoreApplication.translate( "DialogImportData", "Import of the data was successfully completed"), Qgis.Success) def save_configuration(self, configuration): settings = QSettings() settings.setValue( 'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_import', configuration.xtffile) settings.setValue('Asistente-LADM_COL/QgisModelBaker/show_log', not self.log_config.isCollapsed()) def restore_configuration(self): settings = QSettings() self.xtf_file_line_edit.setText( settings.value( 'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_import')) # Show log value_show_log = settings.value( 'Asistente-LADM_COL/QgisModelBaker/show_log', False, type=bool) self.log_config.setCollapsed(not value_show_log) # set model repository # if there is no option by default use online model repository self.use_local_models = settings.value( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', type=bool) if self.use_local_models: self.custom_model_directories = settings.value( 'Asistente-LADM_COL/models/custom_models') if settings.value( 'Asistente-LADM_COL/models/custom_models') else None def update_configuration(self): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ item_db = self._conf_db.get_db_items()[self.db.mode] configuration = ImportDataConfiguration() item_db.set_db_configuration_params(self.db.dict_conn_params, configuration) configuration.xtffile = self.xtf_file_line_edit.text().strip() configuration.delete_data = False configuration.epsg = DEFAULT_EPSG configuration.inheritance = DEFAULT_INHERITANCE configuration.create_basket_col = CREATE_BASKET_COL configuration.create_import_tid = CREATE_IMPORT_TID configuration.stroke_arcs = STROKE_ARCS java_path = get_java_path_from_qgis_model_baker() if java_path: self.base_configuration.java_path = java_path # Check custom model directories if self.use_local_models: if self.custom_model_directories is None: self.base_configuration.custom_model_directories_enabled = False else: self.base_configuration.custom_model_directories = self.custom_model_directories self.base_configuration.custom_model_directories_enabled = True configuration.base_configuration = self.base_configuration if self.get_ili_models(): configuration.ilimodels = ';'.join(self.get_ili_models()) return configuration def print_info(self, text, text_color='#000000', clear=False): self.txtStdout.setTextColor(QColor(text_color)) self.txtStdout.append(text) QCoreApplication.processEvents() def on_stderr(self, text): color_log_text(text, self.txtStdout) self.advance_progress_bar_by_text(text) def on_process_started(self, command): self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(command) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): color = '#004905' if exit_code == 0 else '#aa2222' self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append('Finished ({})'.format(exit_code)) if result == iliimporter.Importer.SUCCESS: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: self.show_message( QCoreApplication.translate("DialogImportData", "Error when importing data"), Qgis.Warning) self.enable() # Open log if self.log_config.isCollapsed(): self.log_config.setCollapsed(False) def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models...': self.progress_bar.setValue(50) QCoreApplication.processEvents() elif text.strip() == 'Info: create table structure...': self.progress_bar.setValue(55) QCoreApplication.processEvents() elif text.strip() == 'Info: first validation pass...': self.progress_bar.setValue(60) QCoreApplication.processEvents() elif text.strip() == 'Info: second validation pass...': self.progress_bar.setValue(80) QCoreApplication.processEvents() def show_help(self): self.qgis_utils.show_help("import_data") def disable(self): self.db_config.setEnabled(False) self.ili_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.db_config.setEnabled(True) self.ili_config.setEnabled(True) self.buttonBox.setEnabled(True) def show_message(self, message, level): self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)
class ShellOutputScintilla(QsciScintilla): DEFAULT_COLOR = "#4d4d4c" KEYWORD_COLOR = "#8959a8" CLASS_COLOR = "#4271ae" METHOD_COLOR = "#4271ae" DECORATION_COLOR = "#3e999f" NUMBER_COLOR = "#c82829" COMMENT_COLOR = "#8e908c" COMMENT_BLOCK_COLOR = "#8e908c" BACKGROUND_COLOR = "#ffffff" CURSOR_COLOR = "#636363" CARET_LINE_COLOR = "#efefef" SINGLE_QUOTE_COLOR = "#718c00" DOUBLE_QUOTE_COLOR = "#718c00" TRIPLE_SINGLE_QUOTE_COLOR = "#eab700" TRIPLE_DOUBLE_QUOTE_COLOR = "#eab700" MARGIN_BACKGROUND_COLOR = "#efefef" MARGIN_FOREGROUND_COLOR = "#636363" SELECTION_BACKGROUND_COLOR = "#d7d7d7" SELECTION_FOREGROUND_COLOR = "#303030" MATCHED_BRACE_BACKGROUND_COLOR = "#b7f907" MATCHED_BRACE_FOREGROUND_COLOR = "#303030" def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFontDatabase.systemFont(QFontDatabase.FixedFont) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console\n" "Use iface to access QGIS API interface or Type help(iface) for more info\n" "Security warning: typing commands from an untrusted source can lead to data loss and/or leak") # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() self.setSelectionForegroundColor(QColor(self.settings.value("pythonConsole/selectionForegroundColor", QColor(self.SELECTION_FOREGROUND_COLOR)))) self.setSelectionBackgroundColor(QColor(self.settings.value("pythonConsole/selectionBackgroundColor", QColor(self.SELECTION_BACKGROUND_COLOR)))) self.setMarginsForegroundColor(QColor(self.settings.value("pythonConsole/marginForegroundColor", QColor(self.MARGIN_FOREGROUND_COLOR)))) self.setMarginsBackgroundColor(QColor(self.settings.value("pythonConsole/marginBackgroundColor", QColor(self.MARGIN_BACKGROUND_COLOR)))) caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor(self.CARET_LINE_COLOR)) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(self.CURSOR_COLOR)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() font = QFontDatabase.systemFont(QFontDatabase.FixedFont) loadFont = self.settings.value("pythonConsole/fontfamilytext") if loadFont: font.setFamily(loadFont) fontSize = self.settings.value("pythonConsole/fontsize", type=int) if fontSize: font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(self.DEFAULT_COLOR)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(self.COMMENT_COLOR))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/numberFontColor", QColor(self.NUMBER_COLOR))), 2) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(self.KEYWORD_COLOR))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(self.CLASS_COLOR))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(self.METHOD_COLOR))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(self.DECORATION_COLOR))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(self.COMMENT_BLOCK_COLOR))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(self.SINGLE_QUOTE_COLOR))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(self.DOUBLE_QUOTE_COLOR))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(self.TRIPLE_SINGLE_QUOTE_COLOR))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(self.TRIPLE_DOUBLE_QUOTE_COLOR))), 7) self.lexer.setColor(QColor(self.settings.value("pythonConsole/defaultFontColorEditor", QColor(self.DEFAULT_COLOR))), 13) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.lexer.setFont(font, QsciLexerPython.UnclosedString) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(self.BACKGROUND_COLOR))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/mIconRunConsole.svg") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.svg") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.svg") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.svg") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
class GenerateProjectDialog(QDialog, DIALOG_UI): def __init__(self, iface, base_config, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.iface = iface self.db_simple_factory = DbSimpleFactory() QgsGui.instance().enableAutoGeometryRestore(self) self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) create_button = self.buttonBox.addButton(self.tr('Create'), QDialogButtonBox.AcceptRole) create_button.setDefault(True) self.ili_file_browse_button.clicked.connect( make_file_selector( self.ili_file_line_edit, title=self.tr('Open Interlis Model'), file_filter=self.tr('Interlis Model File (*.ili)'))) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.help_requested) self.crs = QgsCoordinateReferenceSystem() self.ili2db_options = Ili2dbOptionsDialog() self.ili2db_options_button.clicked.connect(self.ili2db_options.open) self.ili2db_options.finished.connect(self.fill_toml_file_info_label) self.multiple_models_dialog = MultipleModelsDialog(self) self.multiple_models_button.clicked.connect( self.multiple_models_dialog.open) self.multiple_models_dialog.accepted.connect( self.fill_models_line_edit) self.type_combo_box.clear() self._lst_panel = dict() for db_id in self.db_simple_factory.get_db_list(True): self.type_combo_box.addItem(displayDbIliMode[db_id], db_id) for db_id in self.db_simple_factory.get_db_list(False): db_factory = self.db_simple_factory.create_factory(db_id) item_panel = db_factory.get_config_panel(self, DbActionType.GENERATE) self._lst_panel[db_id] = item_panel self.db_layout.addWidget(item_panel) self.type_combo_box.currentIndexChanged.connect(self.type_changed) self.txtStdout.anchorClicked.connect(self.link_activated) self.crsSelector.crsChanged.connect(self.crs_changed) self.base_configuration = base_config self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.txtStdout.setLayout(QGridLayout()) self.txtStdout.layout().setContentsMargins(0, 0, 0, 0) self.txtStdout.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.validators = Validators() nonEmptyValidator = NonEmptyStringValidator() fileValidator = FileValidator(pattern='*.ili', allow_empty=True) self.restore_configuration() self.ili_models_line_edit.setValidator(nonEmptyValidator) self.ili_file_line_edit.setValidator(fileValidator) self.ili_models_line_edit.textChanged.connect( self.validators.validate_line_edits) self.ili_models_line_edit.textChanged.emit( self.ili_models_line_edit.text()) self.ili_models_line_edit.textChanged.connect(self.on_model_changed) self.ili_models_line_edit.textChanged.connect( self.complete_models_completer) self.ili_models_line_edit.punched.connect( self.complete_models_completer) self.ilicache = IliCache(self.base_configuration) self.refresh_ili_cache() self.ili_models_line_edit.setPlaceholderText( self.tr('[Search model from repository]')) self.ili_file_line_edit.textChanged.connect( self.validators.validate_line_edits) self.ili_file_line_edit.textChanged.connect(self.ili_file_changed) self.ili_file_line_edit.textChanged.emit( self.ili_file_line_edit.text()) def accepted(self): configuration = self.updated_configuration() ili_mode = self.type_combo_box.currentData() db_id = ili_mode & ~DbIliMode.ili interlis_mode = ili_mode & DbIliMode.ili if interlis_mode: if not self.ili_file_line_edit.text().strip(): if not self.ili_models_line_edit.text().strip(): self.txtStdout.setText( self. tr('Please set a valid INTERLIS model before creating the project.' )) self.ili_models_line_edit.setFocus() return if self.ili_file_line_edit.text().strip() and \ self.ili_file_line_edit.validator().validate(configuration.ilifile, 0)[0] != QValidator.Acceptable: self.txtStdout.setText( self. tr('Please set a valid INTERLIS file before creating the project.' )) self.ili_file_line_edit.setFocus() return res, message = self._lst_panel[db_id].is_valid() if not res: self.txtStdout.setText(message) return configuration.dbschema = configuration.dbschema or configuration.database self.save_configuration(configuration) # create schema with superuser db_factory = self.db_simple_factory.create_factory(db_id) res, message = db_factory.pre_generate_project(configuration) if not res: self.txtStdout.setText(message) return with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.progress_bar.setValue(0) self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() if interlis_mode: importer = iliimporter.Importer() importer.tool = self.type_combo_box.currentData() importer.configuration = configuration importer.stdout.connect(self.print_info) importer.stderr.connect(self.on_stderr) importer.process_started.connect(self.on_process_started) importer.process_finished.connect(self.on_process_finished) try: if importer.run() != iliimporter.Importer.SUCCESS: self.enable() self.progress_bar.hide() return except JavaNotFoundError as e: self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(e.error_string) self.enable() self.progress_bar.hide() return try: config_manager = db_factory.get_db_command_config_manager( configuration) uri = config_manager.get_uri() generator = Generator(configuration.tool, uri, configuration.inheritance, configuration.dbschema) self.progress_bar.setValue(50) except DBConnectorError: self.txtStdout.setText( self. tr('There was an error connecting to the database. Check connection parameters.' )) self.enable() self.progress_bar.hide() return if not interlis_mode: if not generator.db_or_schema_exists(): self.txtStdout.setText( self. tr('Source {} does not exist. Check connection parameters.' ).format(db_factory.get_specific_messages() ['db_or_schema'])) self.enable() self.progress_bar.hide() return res, message = db_factory.post_generate_project_validations( configuration) if not res: self.txtStdout.setText(message) self.enable() self.progress_bar.hide() return self.print_info( self.tr('\nObtaining available layers from the database…')) available_layers = generator.layers() if not available_layers: text = self.tr( 'The {} has no layers to load into QGIS.').format( db_factory.get_specific_messages()['layers_source']) self.txtStdout.setText(text) self.enable() self.progress_bar.hide() return self.progress_bar.setValue(70) self.print_info(self.tr('Obtaining relations from the database…')) relations, bags_of_enum = generator.relations(available_layers) self.progress_bar.setValue(75) self.print_info(self.tr('Arranging layers into groups…')) legend = generator.legend(available_layers) self.progress_bar.setValue(85) project = Project() project.layers = available_layers project.relations = relations project.bags_of_enum = bags_of_enum project.legend = legend self.print_info(self.tr('Configuring forms and widgets…')) project.post_generate() self.progress_bar.setValue(90) qgis_project = QgsProject.instance() self.print_info(self.tr('Generating QGIS project…')) project.create(None, qgis_project) # Set the extent of the mapCanvas from the first layer extent found for layer in project.layers: if layer.extent is not None: self.iface.mapCanvas().setExtent(layer.extent) self.iface.mapCanvas().refresh() break self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) self.print_info(self.tr('\nDone!'), '#004905') def print_info(self, text, text_color='#000000'): 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) QCoreApplication.processEvents() def on_process_started(self, command): self.txtStdout.setText(command) self.progress_bar.setValue(10) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): if exit_code == 0: color = '#004905' message = self.tr( 'Interlis model(s) successfully imported into the database!') else: color = '#aa2222' message = self.tr('Finished with errors!') self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append(message) self.progress_bar.setValue(50) def updated_configuration(self): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ configuration = SchemaImportConfiguration() mode = self.type_combo_box.currentData() db_id = mode & ~DbIliMode.ili self._lst_panel[db_id].get_fields(configuration) configuration.tool = mode configuration.epsg = self.epsg configuration.inheritance = self.ili2db_options.inheritance_type() configuration.tomlfile = self.ili2db_options.toml_file() configuration.create_basket_col = self.ili2db_options.create_basket_col( ) configuration.create_import_tid = self.ili2db_options.create_import_tid( ) configuration.stroke_arcs = self.ili2db_options.stroke_arcs() configuration.base_configuration = self.base_configuration if self.ili_file_line_edit.text().strip(): configuration.ilifile = self.ili_file_line_edit.text().strip() if self.ili_models_line_edit.text().strip(): configuration.ilimodels = self.ili_models_line_edit.text().strip() return configuration def save_configuration(self, configuration): settings = QSettings() settings.setValue('QgisModelBaker/ili2db/ilifile', configuration.ilifile) settings.setValue('QgisModelBaker/ili2db/epsg', self.epsg) settings.setValue('QgisModelBaker/importtype', self.type_combo_box.currentData().name) mode = self.type_combo_box.currentData() db_factory = self.db_simple_factory.create_factory(mode) config_manager = db_factory.get_db_command_config_manager( configuration) config_manager.save_config_in_qsettings() def restore_configuration(self): settings = QSettings() self.ili_file_line_edit.setText( settings.value('QgisModelBaker/ili2db/ilifile')) self.crs = QgsCoordinateReferenceSystem( settings.value('QgisModelBaker/ili2db/epsg', 21781, int)) self.fill_toml_file_info_label() self.update_crs_info() for db_id in self.db_simple_factory.get_db_list(False): configuration = SchemaImportConfiguration() db_factory = self.db_simple_factory.create_factory(db_id) config_manager = db_factory.get_db_command_config_manager( configuration) config_manager.load_config_from_qsettings() self._lst_panel[db_id].set_fields(configuration) mode = settings.value('QgisModelBaker/importtype') mode = DbIliMode[ mode] if mode else self.db_simple_factory.default_database self.type_combo_box.setCurrentIndex(self.type_combo_box.findData(mode)) self.type_changed() self.crs_changed() def disable(self): self.type_combo_box.setEnabled(False) for key, value in self._lst_panel.items(): value.setEnabled(False) self.ili_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.type_combo_box.setEnabled(True) for key, value in self._lst_panel.items(): value.setEnabled(True) self.ili_config.setEnabled(True) self.buttonBox.setEnabled(True) def type_changed(self): self.txtStdout.clear() self.progress_bar.hide() ili_mode = self.type_combo_box.currentData() db_id = ili_mode & ~DbIliMode.ili interlis_mode = bool(ili_mode & DbIliMode.ili) self.ili_config.setVisible(interlis_mode) self.db_wrapper_group_box.setTitle(displayDbIliMode[db_id]) # Refresh panels for key, value in self._lst_panel.items(): value.interlis_mode = interlis_mode is_current_panel_selected = db_id == key value.setVisible(is_current_panel_selected) if is_current_panel_selected: value._show_panel() def on_model_changed(self, text): if not text: return for pattern, crs in CRS_PATTERNS.items(): if re.search(pattern, text): self.crs = QgsCoordinateReferenceSystem(crs) self.update_crs_info() break self.ili2db_options.set_toml_file_key(text) self.fill_toml_file_info_label() def link_activated(self, link): if link.url() == '#configure': cfg = OptionsDialog(self.base_configuration) if cfg.exec_(): settings = QSettings() settings.beginGroup('QgisModelBaker/ili2db') self.base_configuration.save(settings) else: QDesktopServices.openUrl(link) def update_crs_info(self): self.crsSelector.setCrs(self.crs) def crs_changed(self): if self.crsSelector.crs().authid()[:5] != 'EPSG:': self.crs_label.setStyleSheet('color: orange') self.crs_label.setToolTip( self.tr('Please select an EPSG Coordinate Reference System')) self.epsg = 21781 else: self.crs_label.setStyleSheet('') self.crs_label.setToolTip(self.tr('Coordinate Reference System')) authid = self.crsSelector.crs().authid() self.epsg = int(authid[5:]) def ili_file_changed(self): # If ili file is valid, models is optional if self.ili_file_line_edit.text().strip() and \ self.ili_file_line_edit.validator().validate(self.ili_file_line_edit.text().strip(), 0)[0] == QValidator.Acceptable: self.ili_models_line_edit.setValidator(None) self.ili_models_line_edit.textChanged.emit( self.ili_models_line_edit.text()) # Update completer to add models from given ili file self.ilicache = IliCache(None, self.ili_file_line_edit.text().strip()) self.refresh_ili_cache() models = self.ilicache.process_ili_file( self.ili_file_line_edit.text().strip()) self.ili_models_line_edit.setText(models[-1]['name']) self.ili_models_line_edit.setPlaceholderText(models[-1]['name']) else: nonEmptyValidator = NonEmptyStringValidator() self.ili_models_line_edit.setValidator(nonEmptyValidator) self.ili_models_line_edit.textChanged.emit( self.ili_models_line_edit.text()) # Update completer to add models from given ili file self.ilicache = IliCache(self.base_configuration) self.refresh_ili_cache() self.ili_models_line_edit.setPlaceholderText( self.tr('[Search model from repository]')) def refresh_ili_cache(self): self.ilicache.new_message.connect(self.show_message) self.ilicache.refresh() self.update_models_completer() def complete_models_completer(self): if not self.ili_models_line_edit.text(): self.ili_models_line_edit.completer().setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.ili_models_line_edit.completer().complete() else: self.ili_models_line_edit.completer().setCompletionMode( QCompleter.PopupCompletion) def update_models_completer(self): completer = QCompleter(self.ilicache.model, self.ili_models_line_edit) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setFilterMode(Qt.MatchContains) self.delegate = ModelCompleterDelegate() completer.popup().setItemDelegate(self.delegate) self.ili_models_line_edit.setCompleter(completer) self.multiple_models_dialog.models_line_edit.setCompleter(completer) def show_message(self, level, message): if level == Qgis.Warning: self.bar.pushMessage(message, Qgis.Info, 10) elif level == Qgis.Critical: self.bar.pushMessage(message, Qgis.Warning, 10) def fill_models_line_edit(self): self.ili_models_line_edit.setText( self.multiple_models_dialog.get_models_string()) def fill_toml_file_info_label(self): text = None if self.ili2db_options.toml_file(): text = self.tr('Extra Model Information File: {}').format(( '…' + self.ili2db_options.toml_file( )[len(self.ili2db_options.toml_file()) - 40:]) if len(self.ili2db_options.toml_file() ) > 40 else self.ili2db_options.toml_file()) self.toml_file_info_label.setText(text) self.toml_file_info_label.setToolTip(self.ili2db_options.toml_file()) def help_requested(self): os_language = QLocale( QSettings().value('locale/userLocale')).name()[:2] if os_language in ['es', 'de']: webbrowser.open( "https://opengisch.github.io/QgisModelBaker/docs/{}/user-guide.html#generate-project" .format(os_language)) else: webbrowser.open( "https://opengisch.github.io/QgisModelBaker/docs/user-guide.html#generate-project" ) def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models…': self.progress_bar.setValue(20) elif text.strip() == 'Info: create table structure…': self.progress_bar.setValue(30)
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info") ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Settings"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super().__init__(None) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass self.mToolbar.setIconSize(iface.iconSize()) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) #self.mActionExportPython.setIcon( # QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState( settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry( settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): from processing.core.Processing import Processing if event.mimeData().hasText(): itemId = event.mimeData().text() if itemId in Processing.registeredParameters(): self.addInputOfType(itemId, event.pos()) else: alg = QgsApplication.processingRegistry( ).createAlgorithmById(itemId) if alg is not None: self._addAlgorithm(alg, event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue( self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue( self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].data(0, Qt.UserRole) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] mimeData = None if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.id()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText( QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.textChanged) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider( QgsApplication.processingRegistry().providerById('model')) self.fillInputsTree() self.fillTreeUsingProviders() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self. tr('There are unsaved changes in this model. Do you want to keep those?' ), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage( "", "Model doesn't contain any algorithm and/or parameter and can't be executed", level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as SVG", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", "Model was correctly exported as python script", level=Qgis.Success, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving')) return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning( self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format( str(sys.exc_info()[1]))) else: QMessageBox.warning( self, self.tr("Can't save model"), QCoreApplication. translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option." ))) return self.update_model.emit() self.bar.pushMessage("", "Model was correctly saved", level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName( self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider( QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage( self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical( self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() param = item.data(0, Qt.UserRole) self.addInputOfType(param) def addInputOfType(self, paramType, pos=None): dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([ i.position().x() for i in list(self.model.parameterComponents().values()) ]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def textChanged(self): text = self.searchBox.text().strip(' ').lower() for item in list(self.disabledProviderItems.values()): item.setHidden(True) self._filterItem(self.algorithmTree.invisibleRootItem(), [t for t in text.split(' ') if t]) if text: self.algorithmTree.expandAll() self.disabledWithMatchingAlgs = [] for provider in QgsApplication.processingRegistry().providers(): if not provider.isActive(): for alg in provider.algorithms(): if text in alg.name(): self.disabledWithMatchingAlgs.append(provider.id()) break else: self.algorithmTree.collapseAll() def _filterItem(self, item, text): if (item.childCount() > 0): show = False for i in range(item.childCount()): child = item.child(i) showChild = self._filterItem(child, text) show = (showChild or show) and item not in list( self.disabledProviderItems.values()) item.setHidden(not show) return show elif isinstance(item, (TreeAlgorithmItem, TreeActionItem)): # hide if every part of text is not contained somewhere in either the item text or item user role item_text = [ item.text(0).lower(), item.data(0, ModelerDialog.NAME_ROLE).lower() ] if isinstance(item, TreeAlgorithmItem): item_text.append(item.alg.id()) item_text.extend(item.data(0, ModelerDialog.TAG_ROLE)) hide = bool(text) and not all( any(part in t for t in item_text) for part in text) item.setHidden(hide) return not hide else: item.setHidden(True) return False def fillInputsTree(self): from processing.core.Processing import Processing icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) sortedParams = sorted(Processing.registeredParameters().items()) for param in sortedParams: if param[1]['exposeToModeller']: paramItem = QTreeWidgetItem() paramItem.setText(0, param[1]['name']) paramItem.setData(0, Qt.UserRole, param[0]) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, param[1]['description']) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = QgsApplication.processingRegistry().createAlgorithmById( item.alg.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = None try: dlg = alg.getCustomModelerParametersDialog(self.model) except: pass if not dlg: dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition( alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([ alg.position().x() for alg in list(self.model.childAlgorithms().values()) ]) maxY = max([ alg.position().y() for alg in list(self.model.childAlgorithms().values()) ]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillTreeUsingProviders(self): self.algorithmTree.clear() self.disabledProviderItems = {} # TODO - replace with proper model for toolbox! # first add qgis/native providers, since they create top level groups for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): self.addAlgorithmsFromProvider( provider, self.algorithmTree.invisibleRootItem()) else: continue self.algorithmTree.sortItems(0, Qt.AscendingOrder) for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): # already added continue else: providerItem = TreeProviderItem(provider, self.algorithmTree, self) if not provider.isActive(): providerItem.setHidden(True) self.disabledProviderItems[provider.id()] = providerItem # insert non-native providers at end of tree, alphabetically for i in range( self.algorithmTree.invisibleRootItem().childCount()): child = self.algorithmTree.invisibleRootItem().child(i) if isinstance(child, TreeProviderItem): if child.text(0) > providerItem.text(0): break self.algorithmTree.insertTopLevelItem(i + 1, providerItem) def addAlgorithmsFromProvider(self, provider, parent): groups = {} count = 0 algs = provider.algorithms() active = provider.isActive() # Add algorithms for alg in algs: if alg.flags() & QgsProcessingAlgorithm.FlagHideFromModeler: continue groupItem = None if alg.group() in groups: groupItem = groups[alg.group()] else: # check if group already exists for i in range(parent.childCount()): if parent.child(i).text(0) == alg.group(): groupItem = parent.child(i) groups[alg.group()] = groupItem break if not groupItem: groupItem = TreeGroupItem(alg.group()) if not active: groupItem.setInactive() if provider.id() in ('qgis', 'native', '3d'): groupItem.setIcon(0, provider.icon()) groups[alg.group()] = groupItem algItem = TreeAlgorithmItem(alg) if not active: algItem.setForeground(0, Qt.darkGray) groupItem.addChild(algItem) count += 1 text = provider.name() if not provider.id() in ('qgis', 'native', '3d'): if not active: def activateProvider(): self.activateProvider(provider.id()) label = QLabel( text + " <a href='%s'>Activate</a>") label.setStyleSheet( "QLabel {background-color: white; color: grey;}") label.linkActivated.connect(activateProvider) self.algorithmTree.setItemWidget(parent, 0, label) else: parent.setText(0, text) for group, groupItem in sorted(groups.items(), key=operator.itemgetter(1)): parent.addChild(groupItem) if not provider.id() in ('qgis', 'native', '3d'): parent.setHidden(parent.childCount() == 0)
class BatchAlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setWindowTitle(self.tr('Batch Processing - %s') % self.alg.name) self.setMainWidget(BatchPanel(self, self.alg)) self.textShortHelp.setVisible(False) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) def accept(self): self.algs = [] self.load = [] self.canceled = False for row in range(self.mainWidget.tblParameters.rowCount()): alg = self.alg.getCopy() col = 0 for param in alg.parameters: if param.hidden: continue wrapper = self.mainWidget.wrappers[row][col] if not self.mainWidget.setParamValue(param, wrapper, alg): self.bar.pushMessage("", self.tr('Wrong or missing parameter value: %s (row %d)') % (param.description, row + 1), level=QgsMessageBar.WARNING, duration=5) self.algs = None return col += 1 for out in alg.outputs: if out.hidden: continue widget = self.mainWidget.tblParameters.cellWidget(row, col) text = widget.getValue() if text.strip() != '': out.value = text col += 1 else: self.bar.pushMessage("", self.tr('Wrong or missing output value: %s (row %d)') % (out.description, row + 1), level=QgsMessageBar.WARNING, duration=5) self.algs = None return self.algs.append(alg) if self.alg.getVisibleOutputsCount(): widget = self.mainWidget.tblParameters.cellWidget(row, col) self.load.append(widget.currentIndex() == 0) else: self.load.append(False) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.mainWidget.setEnabled(False) self.progressBar.setMaximum(len(self.algs)) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass for count, alg in enumerate(self.algs): self.setText(self.tr('\nProcessing algorithm %d/%d...') % (count + 1, len(self.algs))) self.setInfo(self.tr('<b>Algorithm %s starting...</b>' % alg.name)) if runalg(alg, self) and not self.canceled: if self.load[count]: handleAlgorithmResults(alg, self, False) self.setInfo(self.tr('Algorithm %s correctly executed...') % alg.name) else: QApplication.restoreOverrideCursor() return self.finish() def finish(self): for count, alg in enumerate(self.algs): self.loadHTMLResults(alg, count) self.createSummaryTable() QApplication.restoreOverrideCursor() self.mainWidget.setEnabled(True) QMessageBox.information(self, self.tr('Batch processing'), self.tr('Batch processing completed')) def loadHTMLResults(self, alg, num): for out in alg.outputs: if out.hidden or not out.open: continue if isinstance(out, OutputHTML): ProcessingResults.addResult( '{} [{}]'.format(out.description, num), out.value) def createSummaryTable(self): createTable = False for out in self.algs[0].outputs: if isinstance(out, (OutputNumber, OutputString)): createTable = True break if not createTable: return outputFile = getTempFilename('html') with codecs.open(outputFile, 'w', encoding='utf-8') as f: for alg in self.algs: f.write('<hr>\n') for out in alg.outputs: if isinstance(out, (OutputNumber, OutputString)): f.write('<p>{}: {}</p>\n'.format(out.description, out.value)) f.write('<hr>\n') ProcessingResults.addResult( '{} [summary]'.format(self.algs[0].name), outputFile)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super().__init__(None) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi(self) self._variables_scope = None # LOTS of bug reports when we include the dock creation in the UI file # see e.g. #16428, #19068 # So just roll it all by hand......! self.propertiesDock = QgsDockWidget(self) self.propertiesDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.propertiesDock.setObjectName("propertiesDock") propertiesDockContents = QWidget() self.verticalDockLayout_1 = QVBoxLayout(propertiesDockContents) self.verticalDockLayout_1.setContentsMargins(0, 0, 0, 0) self.verticalDockLayout_1.setSpacing(0) self.scrollArea_1 = QgsScrollArea(propertiesDockContents) sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.scrollArea_1.sizePolicy().hasHeightForWidth()) self.scrollArea_1.setSizePolicy(sizePolicy) self.scrollArea_1.setFocusPolicy(Qt.WheelFocus) self.scrollArea_1.setFrameShape(QFrame.NoFrame) self.scrollArea_1.setFrameShadow(QFrame.Plain) self.scrollArea_1.setWidgetResizable(True) self.scrollAreaWidgetContents_1 = QWidget() self.gridLayout = QGridLayout(self.scrollAreaWidgetContents_1) self.gridLayout.setContentsMargins(6, 6, 6, 6) self.gridLayout.setSpacing(4) self.label_1 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1) self.textName = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textName, 0, 1, 1, 1) self.label_2 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) self.textGroup = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textGroup, 1, 1, 1, 1) self.label_1.setText(self.tr("Name")) self.textName.setToolTip(self.tr("Enter model name here")) self.label_2.setText(self.tr("Group")) self.textGroup.setToolTip(self.tr("Enter group name here")) self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1) self.verticalDockLayout_1.addWidget(self.scrollArea_1) self.propertiesDock.setWidget(propertiesDockContents) self.propertiesDock.setWindowTitle(self.tr("Model Properties")) self.inputsDock = QgsDockWidget(self) self.inputsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.inputsDock.setObjectName("inputsDock") self.inputsDockContents = QWidget() self.verticalLayout_3 = QVBoxLayout(self.inputsDockContents) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.scrollArea_2 = QgsScrollArea(self.inputsDockContents) sizePolicy.setHeightForWidth( self.scrollArea_2.sizePolicy().hasHeightForWidth()) self.scrollArea_2.setSizePolicy(sizePolicy) self.scrollArea_2.setFocusPolicy(Qt.WheelFocus) self.scrollArea_2.setFrameShape(QFrame.NoFrame) self.scrollArea_2.setFrameShadow(QFrame.Plain) self.scrollArea_2.setWidgetResizable(True) self.scrollAreaWidgetContents_2 = QWidget() self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents_2) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setSpacing(0) self.inputsTree = QTreeWidget(self.scrollAreaWidgetContents_2) self.inputsTree.setAlternatingRowColors(True) self.inputsTree.header().setVisible(False) self.verticalLayout.addWidget(self.inputsTree) self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) self.verticalLayout_3.addWidget(self.scrollArea_2) self.inputsDock.setWidget(self.inputsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.inputsDock) self.inputsDock.setWindowTitle(self.tr("Inputs")) self.algorithmsDock = QgsDockWidget(self) self.algorithmsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.algorithmsDock.setObjectName("algorithmsDock") self.algorithmsDockContents = QWidget() self.verticalLayout_4 = QVBoxLayout(self.algorithmsDockContents) self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.scrollArea_3 = QgsScrollArea(self.algorithmsDockContents) sizePolicy.setHeightForWidth( self.scrollArea_3.sizePolicy().hasHeightForWidth()) self.scrollArea_3.setSizePolicy(sizePolicy) self.scrollArea_3.setFocusPolicy(Qt.WheelFocus) self.scrollArea_3.setFrameShape(QFrame.NoFrame) self.scrollArea_3.setFrameShadow(QFrame.Plain) self.scrollArea_3.setWidgetResizable(True) self.scrollAreaWidgetContents_3 = QWidget() self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents_3) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setSpacing(4) self.searchBox = QgsFilterLineEdit(self.scrollAreaWidgetContents_3) self.verticalLayout_2.addWidget(self.searchBox) self.algorithmTree = QgsProcessingToolboxTreeView( None, QgsApplication.processingRegistry()) self.algorithmTree.setAlternatingRowColors(True) self.algorithmTree.header().setVisible(False) self.verticalLayout_2.addWidget(self.algorithmTree) self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3) self.verticalLayout_4.addWidget(self.scrollArea_3) self.algorithmsDock.setWidget(self.algorithmsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.algorithmsDock) self.algorithmsDock.setWindowTitle(self.tr("Algorithms")) self.searchBox.setToolTip( self.tr("Enter algorithm name to filter list")) self.searchBox.setShowSearchIcon(True) self.variables_dock = QgsDockWidget(self) self.variables_dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.variables_dock.setObjectName("variablesDock") self.variables_dock_contents = QWidget() vl_v = QVBoxLayout() vl_v.setContentsMargins(0, 0, 0, 0) self.variables_editor = QgsVariableEditorWidget() vl_v.addWidget(self.variables_editor) self.variables_dock_contents.setLayout(vl_v) self.variables_dock.setWidget(self.variables_dock_contents) self.addDockWidget(Qt.DockWidgetArea(1), self.variables_dock) self.variables_dock.setWindowTitle(self.tr("Variables")) self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock) self.tabifyDockWidget(self.propertiesDock, self.variables_dock) self.variables_editor.scopeChanged.connect(self.variables_changed) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass if iface is not None: self.mToolbar.setIconSize(iface.iconSize()) self.setStyleSheet(iface.mainWindow().styleSheet()) self.toolbutton_export_to_script = QToolButton() self.toolbutton_export_to_script.setPopupMode(QToolButton.InstantPopup) self.export_to_script_algorithm_action = QAction( QCoreApplication.translate('ModelerDialog', 'Export as Script Algorithm…')) self.toolbutton_export_to_script.addActions( [self.export_to_script_algorithm_action]) self.mToolbar.insertWidget(self.mActionExportImage, self.toolbutton_export_to_script) self.export_to_script_algorithm_action.triggered.connect( self.export_as_script_algorithm) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionSaveInProject.setIcon( QgsApplication.getThemeIcon('/mAddToProject.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) self.toolbutton_export_to_script.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState( settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry( settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) def _dragEnterEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): def alg_dropped(algorithm_id, pos): alg = QgsApplication.processingRegistry().createAlgorithmById( algorithm_id) if alg is not None: self._addAlgorithm(alg, pos) else: assert False, algorithm_id def input_dropped(id, pos): if id in [ param.id() for param in QgsApplication.instance(). processingRegistry().parameterTypes() ]: self.addInputOfType(itemId, pos) if event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): data = event.mimeData().data( 'application/x-vnd.qgis.qgis.algorithmid') stream = QDataStream(data, QIODevice.ReadOnly) algorithm_id = stream.readQString() QTimer.singleShot( 0, lambda id=algorithm_id, pos=self.view.mapToScene(event.pos( )): alg_dropped(id, pos)) event.accept() elif event.mimeData().hasText(): itemId = event.mimeData().text() QTimer.singleShot(0, lambda id=itemId, pos=self.view.mapToScene( event.pos()): input_dropped(id, pos)) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue( self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue( self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].data(0, Qt.UserRole) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) self.algorithms_model = ModelerToolboxModel( self, QgsApplication.processingRegistry()) self.algorithmTree.setToolboxProxyModel(self.algorithms_model) self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) filters = QgsProcessingToolboxProxyModel.Filters( QgsProcessingToolboxProxyModel.FilterModeler) if ProcessingConfig.getSetting( ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES): filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues self.algorithmTree.setFilters(filters) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText( QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.algorithmTree.setFilterString) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionSaveInProject.triggered.connect(self.saveInProject) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider( QgsApplication.processingRegistry().providerById('model')) self.update_variables_gui() self.fillInputsTree() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self. tr('There are unsaved changes in this model. Do you want to keep those?' ), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def update_variables_gui(self): variables_scope = QgsExpressionContextScope(self.tr('Model Variables')) for k, v in self.model.variables().items(): variables_scope.setVariable(k, v) variables_context = QgsExpressionContext() variables_context.appendScope(variables_scope) self.variables_editor.setContext(variables_context) self.variables_editor.setEditableScopeIndex(0) def variables_changed(self): self.model.setVariables(self.variables_editor.variablesInActiveScope()) def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage( "", self. tr("Model doesn't contain any algorithm and/or parameter and can't be executed" ), level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model.create(), parent=iface.mainWindow()) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def saveInProject(self): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) self.model.setSourceFilePath(None) project_provider = QgsApplication.processingRegistry().providerById( PROJECT_PROVIDER_ID) project_provider.add_model(self.model) self.update_model.emit() self.bar.pushMessage("", self.tr("Model was saved inside current project"), level=Qgis.Success, duration=5) self.hasChanged = False QgsProject.instance().setDirty(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage( "", self.tr( "Successfully exported model as image to <a href=\"{}\">{}</a>" ).format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage( "", self.tr( "Successfully exported model as PDF to <a href=\"{}\">{}</a>"). format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage( "", self.tr( "Successfully exported model as SVG to <a href=\"{}\">{}</a>"). format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Python Script'), '', self.tr('Processing scripts (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage( "", self. tr("Successfully exported model as python script to <a href=\"{}\">{}</a>" ).format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) def can_save(self): """ Tests whether a model can be saved, or if it is not yet valid :return: bool """ if str(self.textName.text()).strip() == '': self.bar.pushWarning( "", self.tr('Please a enter model name before saving')) return False return True def saveModel(self, saveAs): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning( self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format( str(sys.exc_info()[1]))) else: QMessageBox.warning( self, self.tr("Can't save model"), QCoreApplication. translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option." ))) return self.update_model.emit() if saveAs: self.bar.pushMessage( "", self.tr( "Model was correctly saved to <a href=\"{}\">{}</a>"). format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) else: self.bar.pushMessage("", self.tr("Model was correctly saved"), level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName( self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider( QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.update_variables_gui() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage( self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical( self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() param = item.data(0, Qt.UserRole) self.addInputOfType(param) def addInputOfType(self, paramType, pos=None): dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([ i.position().x() for i in list(self.model.parameterComponents().values()) ]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) sortedParams = sorted( QgsApplication.instance().processingRegistry().parameterTypes(), key=lambda pt: pt.name()) for param in sortedParams: if param.flags() & QgsProcessingParameterType.ExposeToModeler: paramItem = QTreeWidgetItem() paramItem.setText(0, param.name()) paramItem.setData(0, Qt.UserRole, param.id()) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, param.description()) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): algorithm = self.algorithmTree.selectedAlgorithm() if algorithm is not None: alg = QgsApplication.processingRegistry().createAlgorithmById( algorithm.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition( alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([ alg.position().x() for alg in list(self.model.childAlgorithms().values()) ]) maxY = max([ alg.position().y() for alg in list(self.model.childAlgorithms().values()) ]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def export_as_script_algorithm(self): dlg = ScriptEditorDialog(None) dlg.editor.setText('\n'.join( self.model.asPythonCode( QgsProcessing.PythonQgsProcessingAlgorithmSubclass, 4))) dlg.show()
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 SettingsDialog(QDialog, DIALOG_UI): db_connection_changed = pyqtSignal(DBConnector, bool) # dbconn, ladm_col_db fetcher_task = None 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._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) # TODO remove? else: return # Do not close the dialog else: QDialog.accept(self) # TODO remove? 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()) 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.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 ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla,self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.setLexers() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python %1 on %2\n" "## Type help(iface) for more info and list of methods.\n").arg(sys.version, socket.gethostname()) initText = self.setText(txtInit) def refreshLexerProperties(self): self.setLexers() def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = self.settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") hideToolBar = menu.addAction(iconHideTool, "Hide/Show Toolbar", self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction("Show Editor", self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, "Enter Selected", self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, "Clear console", self.clearConsole) menu.addSeparator() copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction("Select All", self.selectAll, QKeySequence.SelectAll) menu.addSeparator() settingsDialog = menu.addAction(iconSettings, "Settings", self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) action = menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if txt.length() and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class ModelerParametersDialog(QDialog): ENTER_NAME = '[Enter name if this is a final result]' NOT_SELECTED = '[Not selected]' USE_MIN_COVERING_EXTENT = '[Use min covering extent]' def __init__(self, alg, model, algName=None): QDialog.__init__(self) self.setModal(True) # The algorithm to define in this dialog. It is an instance of GeoAlgorithm self._alg = alg # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class self.alg = None # The model this algorithm is going to be added to self.model = model # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self._algName = algName self.setupUi() self.params = None def setupUi(self): self.labels = {} self.widgets = {} self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) tooltips = self._alg.getParameterDescriptions() self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.name) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) for param in self._alg.parameters: if param.isAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText( self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr('(xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr('(x, y)') if param.optional: desc += self.tr(' [optional]') label = QLabel(desc) self.labels[param.name] = label wrapper = param.wrapper(self) self.wrappers[param.name] = wrapper widget = wrapper.widget if widget is not None: self.valueItems[param.name] = widget if param.name in list(tooltips.keys()): tooltip = tooltips[param.name] else: tooltip = param.description label.setToolTip(tooltip) widget.setToolTip(tooltip) if param.isAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.widgets[param.name] = widget self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for output in self._alg.outputs: if output.hidden: continue if isinstance(output, (OutputRaster, OutputVector, OutputTable, OutputHTML, OutputFile, OutputDirectory)): label = QLabel(output.description + '<' + output.__class__.__name__ + '>') item = QLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[output.name] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.name) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.tabWidget = QTabWidget() self.tabWidget.setMinimumWidth(300) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.tabWidget.addTab(self.scrollArea, self.tr('Parameters')) self.txtHelp = QTextBrowser() html = None isText, algHelp = self._alg.help() if algHelp is not None: algHelp = algHelp if isText else QUrl(algHelp) try: if isText: self.txtHelp.setHtml(algHelp) else: html = self.tr( '<p>Downloading algorithm help... Please wait.</p>') self.txtHelp.setHtml(html) self.tabWidget.addTab(self.txtHelp, 'Help') self.reply = QgsNetworkAccessManager.instance().get( QNetworkRequest(algHelp)) self.reply.finished.connect(self.requestFinished) except: pass self.verticalLayout2.addWidget(self.tabWidget) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def requestFinished(self): """Change the webview HTML content""" reply = self.sender() if reply.error() != QNetworkReply.NoError: html = self.tr( '<h2>No help available for this algorithm</h2><p>{}</p>'. format(reply.errorString())) else: html = str(reply.readAll()) reply.deleteLater() self.txtHelp.setHtml(html) def getAvailableDependencies(self): # spellok if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) opts = [] for alg in list(self.model.algs.values()): if alg.name not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([ alg.description for alg in self.getAvailableDependencies() ]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameters: if param.isAdvanced: self.labels[param.name].setVisible(self.showAdvanced) self.widgets[param.name].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outType=None, dataType=None): # upgrade paramType to list if type(paramType) is not list: paramType = [paramType] values = [] inputs = self.model.inputs for i in list(inputs.values()): param = i.param for t in paramType: if isinstance(param, t): if dataType is not None: if param.datatype in dataType: values.append(ValueFromInput(param.name)) else: values.append(ValueFromInput(param.name)) break if outType is None: return values if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) for alg in list(self.model.algs.values()): if alg.name not in dependent: for out in alg.algorithm.outputs: if isinstance(out, outType): if dataType is not None and out.datatype in dataType: values.append(ValueFromOutput(alg.name, out.name)) else: values.append(ValueFromOutput(alg.name, out.name)) return values def resolveValueDescription(self, value): if isinstance(value, ValueFromInput): return self.model.inputs[value.name].param.description else: alg = self.model.algs[value.alg] return self.tr("'{0}' from algorithm '{1}'").format( alg.algorithm.getOutputFromName(value.output).description, alg.description) def setPreviousValues(self): if self._algName is not None: alg = self.model.algs[self._algName] self.descriptionBox.setText(alg.description) for param in alg.algorithm.parameters: if param.hidden: continue if param.name in alg.params: value = alg.params[param.name] else: value = param.default self.wrappers[param.name].setValue(value) for name, out in list(alg.outputs.items()): self.valueItems[name].setText(out.description) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.name in alg.dependencies: selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = Algorithm(self._alg.commandLineName()) alg.setName(self.model) alg.description = self.descriptionBox.text() params = self._alg.parameters outputs = self._alg.outputs for param in params: if param.hidden: continue if not self.setParamValue(alg, param, self.wrappers[param.name]): self.bar.pushMessage( "Error", "Wrong or missing value for parameter '%s'" % param.description, level=QgsMessageBar.WARNING) return None for output in outputs: if not output.hidden: name = str(self.valueItems[output.name].text()) if name.strip( ) != '' and name != ModelerParametersDialog.ENTER_NAME: alg.outputs[output.name] = ModelerOutput(name) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok for selected in selectedOptions: alg.dependencies.append( availableDependencies[selected].name) # spellok self._alg.processBeforeAddingToModeler(alg, self.model) return alg def setParamValue(self, alg, param, wrapper): try: if wrapper.widget: value = wrapper.value() alg.params[param.name] = value return True except InvalidParameterValue: return False def okPressed(self): self.alg = self.createAlgorithm() if self.alg is not None: self.close() def cancelPressed(self): self.alg = None self.close()
class ModelerParametersDialog(QDialog): def __init__(self, alg, model, algName=None, configuration=None): QDialog.__init__(self) self.setModal(True) self._alg = alg # The algorithm to define in this dialog. It is an instance of QgsProcessingAlgorithm self.model = model # The model this algorithm is going to be added to. It is an instance of QgsProcessingModelAlgorithm self.childId = algName # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self.configuration = configuration self.context = createContext() self.widget_labels = {} class ContextGenerator(QgsProcessingContextGenerator): def __init__(self, context): super().__init__() self.processing_context = context def processingContext(self): return self.processing_context self.context_generator = ContextGenerator(self.context) self.setupUi() self.params = None settings = QgsSettings() self.restoreGeometry(settings.value("/Processing/modelParametersDialogGeometry", QByteArray())) def closeEvent(self, event): settings = QgsSettings() settings.setValue("/Processing/modelParametersDialogGeometry", self.saveGeometry()) super(ModelerParametersDialog, self).closeEvent(event) def setupUi(self): self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.algorithmItem = None self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.displayName()) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) widget_context = QgsProcessingParameterWidgetContext() widget_context.setProject(QgsProject.instance()) if iface is not None: widget_context.setMapCanvas(iface.mapCanvas()) widget_context.setModel(self.model) widget_context.setModelChildAlgorithmId(self.childId) self.algorithmItem = QgsGui.instance().processingGuiRegistry().algorithmConfigurationWidget(self._alg) if self.algorithmItem: self.algorithmItem.setWidgetContext(widget_context) self.algorithmItem.registerProcessingContextGenerator(self.context_generator) if self.configuration: self.algorithmItem.setConfiguration(self.configuration) self.verticalLayout.addWidget(self.algorithmItem) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue wrapper = WidgetWrapperFactory.create_wrapper(param, self) self.wrappers[param.name()] = wrapper wrapper.setWidgetContext(widget_context) wrapper.registerProcessingContextGenerator(self.context_generator) if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): widget = wrapper else: widget = wrapper.widget if widget is not None: self.valueItems[param.name()] = widget if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): label = wrapper.createLabel() else: tooltip = param.description() widget.setToolTip(tooltip) label = wrapper.label self.widget_labels[param.name()] = label if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for dest in self._alg.destinationParameterDefinitions(): if dest.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if isinstance(dest, (QgsProcessingParameterRasterDestination, QgsProcessingParameterVectorDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination)): label = QLabel(dest.description()) item = QgsFilterLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(self.tr('[Enter name if this is a final result]')) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[dest.name()] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.displayName()) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.verticalLayout2.addWidget(self.scrollArea) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.helpRequested.connect(self.openHelp) QMetaObject.connectSlotsByName(self) def getAvailableDependencies(self): # spellok if self.childId is None: dependent = [] else: dependent = list(self.model.dependentChildAlgorithms(self.childId)) dependent.append(self.childId) opts = [] for alg in list(self.model.childAlgorithms().values()): if alg.childId() not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description() for alg in self.getAvailableDependencies()]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: wrapper = self.wrappers[param.name()] if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): wrapper.setVisible(self.showAdvanced) else: wrapper.widget.setVisible(self.showAdvanced) self.widget_labels[param.name()].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]): # upgrade paramType to list if paramType is None: paramType = [] elif not isinstance(paramType, (tuple, list)): paramType = [paramType] if outTypes is None: outTypes = [] elif not isinstance(outTypes, (tuple, list)): outTypes = [outTypes] return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if issubclass(p, QgsProcessingParameterDefinition)], [o.typeName() for o in outTypes if issubclass(o, QgsProcessingOutputDefinition)], dataTypes) def resolveValueDescription(self, value): if isinstance(value, QgsProcessingModelChildParameterSource): if value.source() == QgsProcessingModelChildParameterSource.StaticValue: return value.staticValue() elif value.source() == QgsProcessingModelChildParameterSource.ModelParameter: return self.model.parameterDefinition(value.parameterName()).description() elif value.source() == QgsProcessingModelChildParameterSource.ChildOutput: alg = self.model.childAlgorithm(value.outputChildId()) return self.tr("'{0}' from algorithm '{1}'").format( alg.algorithm().outputDefinition(value.outputName()).description(), alg.description()) return value def setPreviousValues(self): if self.childId is not None: alg = self.model.childAlgorithm(self.childId) self.descriptionBox.setText(alg.description()) for param in alg.algorithm().parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue value = None if param.name() in alg.parameterSources(): value = alg.parameterSources()[param.name()] if isinstance(value, list) and len(value) == 1: value = value[0] elif isinstance(value, list) and len(value) == 0: value = None wrapper = self.wrappers[param.name()] if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): if value is None: value = QgsProcessingModelChildParameterSource.fromStaticValue(param.defaultValue()) wrapper.setWidgetValue(value) else: if value is None: value = param.defaultValue() if isinstance(value, QgsProcessingModelChildParameterSource) and value.source() == QgsProcessingModelChildParameterSource.StaticValue: value = value.staticValue() wrapper.setValue(value) for name, out in alg.modelOutputs().items(): if out.childOutputName() in self.valueItems: self.valueItems[out.childOutputName()].setText(out.name()) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.childId() in alg.dependencies(): selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = QgsProcessingModelChildAlgorithm(self._alg.id()) if not self.childId: alg.generateChildId(self.model) else: alg.setChildId(self.childId) alg.setDescription(self.descriptionBox.text()) if self.algorithmItem: alg.setConfiguration(self.algorithmItem.configuration()) self._alg = alg.algorithm().create(self.algorithmItem.configuration()) for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue try: wrapper = self.wrappers[param.name()] if issubclass(wrapper.__class__, WidgetWrapper): val = wrapper.value() elif issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): val = wrapper.value() else: val = wrapper.parameterValue() except InvalidParameterValue: self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(param.description()), level=Qgis.Warning) return None if isinstance(val, QgsProcessingModelChildParameterSource): val = [val] elif not (isinstance(val, list) and all( [isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])): val = [QgsProcessingModelChildParameterSource.fromStaticValue(val)] for subval in val: if (isinstance(subval, QgsProcessingModelChildParameterSource) and subval.source() == QgsProcessingModelChildParameterSource.StaticValue and not param.checkValueIsAcceptable(subval.staticValue())) \ or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional): self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format( param.description()), level=Qgis.Warning) return None alg.addParameterSources(param.name(), val) outputs = {} for dest in self._alg.destinationParameterDefinitions(): if not dest.flags() & QgsProcessingParameterDefinition.FlagHidden: name = self.valueItems[dest.name()].text() if name.strip() != '': output = QgsProcessingModelOutput(name, name) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[name] = output if dest.flags() & QgsProcessingParameterDefinition.FlagIsModelOutput: if dest.name() not in outputs: output = QgsProcessingModelOutput(dest.name(), dest.name()) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[dest.name()] = output alg.setModelOutputs(outputs) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok dep_ids = [] for selected in selectedOptions: dep_ids.append(availableDependencies[selected].childId()) # spellok alg.setDependencies(dep_ids) #try: # self._alg.processBeforeAddingToModeler(alg, self.model) #except: # pass return alg def okPressed(self): alg = self.createAlgorithm() if alg is not None: self.accept() def cancelPressed(self): self.reject() def openHelp(self): algHelp = self._alg.helpUrl() if not algHelp: algHelp = QgsHelp.helpUrl("processing_algs/{}/{}.html#{}".format( self._alg.provider().helpId(), self._alg.groupId(), "{}{}".format(self._alg.provider().helpId(), self._alg.name()))).toString() if algHelp not in [None, ""]: webbrowser.open(algHelp)
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 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 None: ret = QMessageBox.question( self, self.tr("Metadata file"), self. tr("Could not find a suitable metadata file.\nDo you want to select it manually?" )) if ret == QMessageBox.Yes: metadataFile, _ = QFileDialog.getOpenFileName( self, self.tr("Metadata file"), os.path.dirname(self.currentLayer.source()), '*.xml') print(metadataFile) if metadataFile: try: loadMetadataFromXml(self.currentLayer, metadataFile) except: self.bar.pushMessage( self.tr("Error importing metadata"), self. tr("Cannot convert the metadata file. Maybe not ISO19139 or ESRI-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) 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): toPublish = self._toPublish() if self.validateBeforePublication(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, toPublish): 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.validateGeodataBeforePublication(errors, toPublish) if self.comboMetadataServer.currentIndex() != 0: metadataServer = metadataServers()[ self.comboMetadataServer.currentText()] metadataServer.validateMetadataBeforePublication(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