def execute(self): filename = QFileDialog.getOpenFileName( self.toolbox, self.tr("Open model", "AddModelFromFileAction"), None, self.tr("Processing model files (*.model *.MODEL)", "AddModelFromFileAction"), ) if filename: try: ModelerAlgorithm.fromFile(filename) except WrongModelException: QMessageBox.warning( self.toolbox, self.tr("Error reading model", "AddModelFromFileAction"), self.tr("The selected file does not contain a valid model", "AddModelFromFileAction"), ) return except: QMessageBox.warning( self.toolbox, self.tr("Error reading model", "AddModelFromFileAction"), self.tr("Cannot read file", "AddModelFromFileAction"), ) destFilename = os.path.join(ModelerUtils.modelsFolder(), os.path.basename(filename)) shutil.copyfile(filename, destFilename) self.toolbox.updateProvider("model")
def __init__(self, resourceType): super(GetScriptsAndModelsDialog, self).__init__(iface.mainWindow()) self.setupUi(self) self.manager = QgsNetworkAccessManager.instance() repoUrl = ProcessingConfig.getSetting(ProcessingConfig.MODELS_SCRIPTS_REPO) self.resourceType = resourceType if self.resourceType == self.MODELS: self.folder = ModelerUtils.modelsFolders()[0] self.urlBase = '{}/models/'.format(repoUrl) self.icon = QIcon(os.path.join(pluginPath, 'images', 'model.png')) elif self.resourceType == self.SCRIPTS: self.folder = ScriptUtils.scriptsFolders()[0] self.urlBase = '{}/scripts/'.format(repoUrl) self.icon = QIcon(os.path.join(pluginPath, 'images', 'script.png')) else: self.folder = RUtils.RScriptsFolders()[0] self.urlBase = '{}/rscripts/'.format(repoUrl) self.icon = QIcon(os.path.join(pluginPath, 'images', 'r.svg')) self.lastSelectedItem = None self.updateProvider = False self.populateTree() self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.tree.currentItemChanged.connect(self.currentItemChanged)
def __init__(self, resourceType): super(GetScriptsAndModelsDialog, self).__init__(iface.mainWindow()) self.setupUi(self) if hasattr(self.leFilter, 'setPlaceholderText'): self.leFilter.setPlaceholderText(self.tr('Search...')) self.manager = QgsNetworkAccessManager.instance() repoUrl = ProcessingConfig.getSetting(ProcessingConfig.MODELS_SCRIPTS_REPO) self.resourceType = resourceType if self.resourceType == self.MODELS: self.folder = ModelerUtils.modelsFolders()[0] self.urlBase = '{}/models/'.format(repoUrl) self.icon = QgsApplication.getThemeIcon("/processingModel.svg") elif self.resourceType == self.SCRIPTS: self.folder = ScriptUtils.scriptsFolders()[0] self.urlBase = '{}/scripts/'.format(repoUrl) self.icon = QgsApplication.getThemeIcon("/processingScript.svg") self.lastSelectedItem = None self.updateProvider = False self.data = None self.populateTree() self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.tree.currentItemChanged.connect(self.currentItemChanged) self.leFilter.textChanged.connect(self.fillTree)
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 __init__(self, resourceType): super(GetScriptsAndModelsDialog, self).__init__(iface.mainWindow()) self.setupUi(self) self.manager = QgsNetworkAccessManager.instance() self.resourceType = resourceType if self.resourceType == self.MODELS: self.folder = ModelerUtils.modelsFolder() self.urlBase = 'https://raw.githubusercontent.com/qgis/QGIS-Processing/master/models/' self.icon = QIcon(os.path.join(pluginPath, 'images', 'model.png')) elif self.resourceType == self.SCRIPTS: self.folder = ScriptUtils.scriptsFolder() self.urlBase = 'https://raw.githubusercontent.com/qgis/QGIS-Processing/master/scripts/' self.icon = QIcon(os.path.join(pluginPath, 'images', 'script.png')) else: self.folder = RUtils.RScriptsFolder() self.urlBase = 'https://raw.githubusercontent.com/qgis/QGIS-Processing/master/rscripts/' self.icon = QIcon(os.path.join(pluginPath, 'images', 'r.png')) self.lastSelectedItem = None self.updateToolbox = False self.populateTree() self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.tree.currentItemChanged.connect(self.currentItemChanged)
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 loadAlgorithms(self): self.algs = [] folders = ModelerUtils.modelsFolders() for f in folders: self.loadFromFolder(f) for a in self.algs: self.addAlgorithm(a)
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 openModel(self): filename = unicode(QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolder(), 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 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: QgsMessageLog.logMessage(self.tr('Could not load model {0}\n{1}').format(filename, e.msg), self.tr('Processing'), QgsMessageLog.CRITICAL) 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: QgsMessageLog.logMessage(self.tr('Could not load model {0}\n{1}').format(filename, e.args[0]), self.tr('Processing'), QgsMessageLog.CRITICAL) 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 __init__(self, resourceType): super(GetScriptsAndModelsDialog, self).__init__(iface.mainWindow()) self.setupUi(self) if hasattr(self.leFilter, "setPlaceholderText"): self.leFilter.setPlaceholderText(self.tr("Search...")) self.manager = QgsNetworkAccessManager.instance() self.resourceType = resourceType if self.resourceType == self.MODELS: self.folder = ModelerUtils.modelsFolders()[0] self.urlBase = "https://raw.githubusercontent.com/qgis/QGIS-Processing/master/models/" self.icon = QIcon(os.path.join(pluginPath, "images", "model.png")) elif self.resourceType == self.SCRIPTS: self.folder = ScriptUtils.scriptsFolders()[0] self.urlBase = "https://raw.githubusercontent.com/qgis/QGIS-Processing/master/scripts/" self.icon = QIcon(os.path.join(pluginPath, "images", "script.png")) else: self.folder = RUtils.RScriptsFolders()[0] self.urlBase = "https://raw.githubusercontent.com/qgis/QGIS-Processing/master/rscripts/" self.icon = QIcon(os.path.join(pluginPath, "images", "r.svg")) self.lastSelectedItem = None self.updateProvider = False self.data = None self.populateTree() self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.tree.currentItemChanged.connect(self.currentItemChanged) self.leFilter.textChanged.connect(self.fillTree)
def execute(self): settings = QSettings() lastDir = settings.value('Processing/lastModelsDir', '') filename = QFileDialog.getOpenFileName(self.toolbox, self.tr('Open model', 'AddModelFromFileAction'), lastDir, self.tr('Processing model files (*.model *.MODEL)', 'AddModelFromFileAction')) if filename: try: settings.setValue('Processing/lastModelsDir', QFileInfo(filename).absoluteDir().absolutePath()) ModelerAlgorithm.fromFile(filename) except WrongModelException: QMessageBox.warning( self.toolbox, self.tr('Error reading model', 'AddModelFromFileAction'), self.tr('The selected file does not contain a valid model', 'AddModelFromFileAction')) return except: QMessageBox.warning(self.toolbox, self.tr('Error reading model', 'AddModelFromFileAction'), self.tr('Cannot read file', 'AddModelFromFileAction')) return destFilename = os.path.join(ModelerUtils.modelsFolder(), os.path.basename(filename)) shutil.copyfile(filename, destFilename) self.toolbox.updateProvider('model')
def execute(self, progress): self.alg = ModelerUtils.getAlgorithm(self.description["algname"]).getCopy() for name, value in self.description["parameters"].iteritems(): self.alg.setParameterValue(name, value) for name, value in self.description["outputs"].iteritems(): self.alg.setOutputValue(name, value) self.alg.execute(progress) self.outputs = self.alg.outputs
def checkBeforeOpeningParametersDialog(self): for alg in self.algs.values(): algInstance = ModelerUtils.getAlgorithm(alg.consoleName) if algInstance is None: return ( "The model you are trying to run contains an algorithm that is not available: <i>%s</i>" % alg.consoleName )
def load(self): ProcessingConfig.settingIcons[self.name()] = self.icon() ProcessingConfig.addSetting(Setting(self.name(), ModelerUtils.MODELS_FOLDER, self.tr('Models folder', 'ModelerAlgorithmProvider'), ModelerUtils.defaultModelsFolder(), valuetype=Setting.MULTIPLE_FOLDERS)) ProviderActions.registerProviderActions(self, self.actions) ProviderContextMenuActions.registerProviderContextMenuActions(self.contextMenuActions) ProcessingConfig.readSettings() self.refreshAlgorithms() return True
def loadAlgorithms(self): if self.isLoading: return self.isLoading = True self.algs = [] folders = ModelerUtils.modelsFolders() for f in folders: self.loadFromFolder(f) for a in self.algs: self.addAlgorithm(a) self.isLoading = False
def initializeSettings(self): AlgorithmProvider.initializeSettings(self) ProcessingConfig.addSetting( Setting( self.getDescription(), ModelerUtils.MODELS_FOLDER, self.tr("Models folder", "ModelerAlgorithmProvider"), ModelerUtils.defaultModelsFolder(), valuetype=Setting.MULTIPLE_FOLDERS, ) )
def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = ModelerUtils.getAlgorithm(item.alg.commandLineName()) alg = alg.getCopy()#copy.deepcopy(alg) # create a tab for this algorithm stepDialog = StepDialog(alg, self) self.canvasTabWidget.addTab(stepDialog, alg.name) # add this step to the workflow self.workflow.addStep(alg, stepDialog.getMode(), stepDialog.getInstructions())
def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = ModelerUtils.getAlgorithm(text) if alg is not None: self._addAlgorithm(alg.getCopy(), event.pos()) event.accept() else: event.ignore()
def execute(self): filename = QtGui.QFileDialog.getOpenFileName(self.toolbox, 'model files', None, '*.model') if filename: try: model = ModelerAlgorithm() model.openModel(filename) except WrongModelException: QtGui.QMessageBox.warning(self.toolbox, "Error reading model", "The selected file does not contain a valid model") return destFilename = os.path.join(ModelerUtils.modelsFolder(), os.path.basename(filename)) shutil.copyfile(filename,destFilename) self.toolbox.updateProvider('script')
def saveModel(self, saveAs): if unicode(self.textGroup.text()).strip() == "" or unicode(self.textName.text()).strip() == "": QMessageBox.warning(self, self.tr("Warning"), self.tr("Please enter group and model names before saving")) return self.alg.name = unicode(self.textName.text()) self.alg.group = unicode(self.textGroup.text()) if self.alg.descriptionFile is not None and not saveAs: filename = self.alg.descriptionFile else: filename = unicode( QFileDialog.getSaveFileName( self, self.tr("Save Model"), ModelerUtils.defaultModelsFolder(), 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") % unicode(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 QMessageBox.information(self, self.tr("Model saved"), self.tr("Model was correctly saved.")) self.hasChanged = False
def __init__(self, resourceType): QDialog.__init__(self, iface.mainWindow()) self.resourceType = resourceType if self.resourceType == self.MODELS: self.folder = ModelerUtils.modelsFolder() self.urlBase = "https://raw.githubusercontent.com/qgis/QGIS-Processing/master/models/" self.icon = QtGui.QIcon(os.path.dirname(__file__) + '/../images/model.png') else: self.folder = ScriptUtils.scriptsFolder() self.urlBase = "https://raw.githubusercontent.com/qgis/QGIS-Processing/master/scripts/" self.icon = QtGui.QIcon(os.path.dirname(__file__) + '/../images/script.png') self.lastSelectedItem = None self.setupUi(self) self.populateTree() self.updateToolbox = False self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.tree.currentItemChanged .connect(self.currentItemChanged)
def __init__(self): self.provider = PermaclimAlgorithmProvider() return settings = QSettings() version_settings = settings.value( "/version", '') current_version = version() if version_settings != current_version: settings.setValue("/version", current_version) models_src_path = os.path.join(cmd_folder, 'models') models_dst_path = ModelerUtils.modelsFolder() for name in os.listdir(models_src_path): file_src_path = os.path.join(models_src_path, name) file_dst_path = os.path.join(models_dst_path, name) if os.path.exists(file_dst_path): shutil.move(file_dst_path, file_dst_path+'.old') shutil.copy(file_src_path, file_dst_path)
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 QMessageBox.information(self, self.tr('Model saved'), self.tr('Model was correctly saved.')) self.hasChanged = False
def execute(self): settings = QgsSettings() lastDir = settings.value('Processing/lastModelsDir', '') filename, selected_filter = QFileDialog.getOpenFileName(self.toolbox, self.tr('Open Model', 'AddModelFromFileAction'), lastDir, self.tr('Processing models (*.model3 *.MODEL3)', 'AddModelFromFileAction')) if filename: settings.setValue('Processing/lastModelsDir', QFileInfo(filename).absoluteDir().absolutePath()) alg = QgsProcessingModelAlgorithm() if not alg.fromFile(filename): QMessageBox.warning( self.toolbox, self.tr('Open Model', 'AddModelFromFileAction'), self.tr('The selected file does not contain a valid model', 'AddModelFromFileAction')) return destFilename = os.path.join(ModelerUtils.modelsFolders()[0], os.path.basename(filename)) shutil.copyfile(filename, destFilename) QgsApplication.processingRegistry().providerById('model').refreshAlgorithms()
def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if 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'), QgsMessageLog.CRITICAL) 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 algorithm(self): if self._algInstance is None: self._algInstance = ModelerUtils.getAlgorithm(self.consoleName).getCopy(); return self._algInstance
class WorkflowSetupDialog(QDialog, FORM_CLASS): __qgisModelPath__ = ModelerUtils.modelsFolders()[0] ON_FLAGS_HALT, ON_FLAGS_WARN, ON_FLAGS_IGNORE = range(3) onFlagsDisplayNameMap = { ON_FLAGS_HALT: QCoreApplication.translate('WorkflowSetupDialog', "Halt"), ON_FLAGS_WARN: QCoreApplication.translate('WorkflowSetupDialog', "Warn"), ON_FLAGS_IGNORE: QCoreApplication.translate('WorkflowSetupDialog', "Ignore") } onFlagsValueMap = { ON_FLAGS_HALT: "halt", ON_FLAGS_WARN: "warn", ON_FLAGS_IGNORE: "ignore" } MODEL_NAME_HEADER, MODEL_SOURCE_HEADER, ON_FLAGS_HEADER, LOAD_OUT_HEADER = range( 4) def __init__(self, parent=None): """ Class constructor. :param headerMap: (dict) a map from each header to be shown and type of cell content (e.g. widget or item). :param parent: (QtWidgets.*) any widget parent to current instance. """ super(WorkflowSetupDialog, self).__init__(parent) self.parent = parent self.setupUi(self) self.messageBar = QgsMessageBar(self) self.orderedTableWidget.setHeaders({ self.MODEL_NAME_HEADER: { "header": self.tr("Model name"), "type": "widget", "widget": self.modelNameWidget, "setter": "setText", "getter": "text" }, self.MODEL_SOURCE_HEADER: { "header": self.tr("Model source"), "type": "widget", "widget": self.modelWidget, "setter": "setText", "getter": "text" }, self.ON_FLAGS_HEADER: { "header": self.tr("On flags"), "type": "widget", "widget": self.onFlagsWidget, "setter": "setCurrentIndex", "getter": "currentIndex" }, self.LOAD_OUT_HEADER: { "header": self.tr("Load output"), "type": "widget", "widget": self.loadOutputWidget, "setter": "setChecked", "getter": "isChecked" } }) self.orderedTableWidget.setHeaderDoubleClickBehaviour("replicate") def resizeTable(self): """ Adjusts table columns sizes. """ dSize = self.orderedTableWidget.geometry().width() - \ self.orderedTableWidget.horizontalHeader().geometry().width() onFlagsColSize = self.orderedTableWidget.sectionSize(2) loadOutColSize = self.orderedTableWidget.sectionSize(3) missingBarSize = self.geometry().size().width() - dSize\ - onFlagsColSize - loadOutColSize # the "-11" is empiric: it makes it fit header to table self.orderedTableWidget.tableWidget.horizontalHeader().resizeSection( 0, int(0.4 * missingBarSize) - 11) self.orderedTableWidget.tableWidget.horizontalHeader().resizeSection( 1, missingBarSize - int(0.4 * missingBarSize) - 11) def resizeEvent(self, e): """ Reimplementation in order to use this window's resize event. On this object, this method makes sure that message bar is always the same size as the window. :param e: (QResizeEvent) resize event. """ self.messageBar.resize( QSize( self.geometry().size().width(), 40 # this felt nicer than the original height (30) )) self.resizeTable() def confirmAction(self, msg, showCancel=True): """ Raises a message box for confirmation before executing an action. :param msg: (str) message to be exposed. :param showCancel: (bool) whether Cancel button should be exposed. :return: (bool) whether action was confirmed. """ if showCancel: return QMessageBox.question(self, self.tr('Confirm Action'), msg, QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Ok else: return QMessageBox.question(self, self.tr('Confirm Action'), msg, QMessageBox.Ok) == QMessageBox.Ok def clear(self): """ Clears all input data from GUI. """ self.authorLineEdit.setText("") self.nameLineEdit.setText("") self.versionLineEdit.setText("") self.orderedTableWidget.clear() def modelNameWidget(self, name=None): """ Gets a new instance of model name's setter widget. :param name: (str) model name to be filled. :return: (QLineEdit) widget for model's name setting. """ # no need to pass parenthood as it will be set to the table when added # to a cell le = QLineEdit() # setPlace"h"older, with a lower case "h"... le.setPlaceholderText(self.tr("Set a name for the model...")) if name is not None: le.setText(name) le.setFrame(False) return le def modelWidget(self, filepath=None): """ Gets a new instance of model settter's widget. :parma filepath: (str) path to a model. :return: (SelectFileWidget) DSGTools custom file selection widget. """ widget = SelectFileWidget() widget.label.hide() widget.selectFilePushButton.setText("...") widget.selectFilePushButton.setMaximumWidth(32) widget.lineEdit.setPlaceholderText(self.tr("Select a model...")) widget.lineEdit.setFrame(False) widget.setCaption(self.tr("Select a QGIS Processing model file")) widget.setFilter( self.tr("Select a QGIS Processing model (*.model *.model3)")) # defining setter and getter methods for composed widgets into OTW widget.setText = widget.lineEdit.setText widget.text = widget.lineEdit.text if filepath is not None: widget.setText(filepath) return widget def onFlagsWidget(self, option=None): """ Gets a new instance for the widget that sets model's behaviour when flags are raised. :param option: (str) on flags raised behaviour (non translatable text). :return: (QComboBox) model's behaviour selection widget. """ combo = QComboBox() combo.addItems([ self.onFlagsDisplayNameMap[self.ON_FLAGS_HALT], self.onFlagsDisplayNameMap[self.ON_FLAGS_WARN], self.onFlagsDisplayNameMap[self.ON_FLAGS_IGNORE] ]) if option is not None: optIdx = None for idx, txt in self.onFlagsValueMap.items(): if option == txt: optIdx = idx break optIdx = optIdx if optIdx is not None else 0 combo.setCurrentIndex(optIdx) return combo def loadOutputWidget(self, option=None): """ Gets a new instance for the widget that sets output layer loading definitions. :param option: (bool) if output should be loaded. :return: (QWidget) widget for output layer loading behaviour definition. """ cb = QCheckBox() cb.setStyleSheet("margin:auto;") if option is not None: cb.setChecked(option) return cb def now(self): """ Gets time and date from the system. Format: "dd/mm/yyyy HH:MM:SS". :return: (str) current's date and time """ paddle = lambda n: str(n) if n > 9 else "0{0}".format(n) now = datetime.now() return "{day}/{month}/{year} {hour}:{minute}:{second}".format( year=now.year, month=paddle(now.month), day=paddle(now.day), hour=paddle(now.hour), minute=paddle(now.minute), second=paddle(now.second)) def workflowName(self): """ Reads filled workflow name from GUI. :return: (str) workflow's name. """ return self.nameLineEdit.text().strip() def setWorkflowName(self, name): """ Sets workflow name to GUI. :param name: (str) workflow's name. """ self.nameLineEdit.setText(name) def author(self): """ Reads filled workflow name from GUI. :return: (str) workflow's author. """ return self.authorLineEdit.text().strip() def setWorkflowAuthor(self, author): """ Sets workflow author name to GUI. :param author: (str) workflow's author name. """ self.authorLineEdit.setText(author) def version(self): """ Reads filled workflow name from GUI. :return: (str) workflow's version. """ return self.versionLineEdit.text().strip() def setWorkflowVersion(self, version): """ Sets workflow version to GUI. :param version: (str) workflow's version. """ self.versionLineEdit.setText(version) def modelCount(self): """ Reads the amount of models (rows added) the user intend to use. :return: (int) model count. """ return self.orderedTableWidget.rowCount() def readRow(self, row): """ Reads a row's contents and set it as a DsgToolsProcessingModel set of parameters. :return: (dict) parameters map. """ contents = self.orderedTableWidget.row(row) filepath = contents[self.MODEL_SOURCE_HEADER].strip() onFlagsIdx = contents[self.ON_FLAGS_HEADER] name = contents[self.MODEL_NAME_HEADER].strip() loadOutput = contents[self.LOAD_OUT_HEADER] if not os.path.exists(filepath): xml = "" else: with open(filepath, "r", encoding="utf-8") as f: xml = f.read() return { "displayName": name, "flags": { "onFlagsRaised": self.onFlagsValueMap[onFlagsIdx], "loadOutput": loadOutput }, "source": { "type": "xml", "data": xml }, "metadata": { "originalName": os.path.basename(filepath) } } def setModelToRow(self, row, model): """ Reads model's parameters from model parameters default map. :param row: (int) row to have its widgets filled with model's parameters. :param model: (DsgToolsProcessingModel) model object. """ # all model files handled by this tool are read/written on QGIS model dir data = model.data() if model.source() == "file" and os.path.exists(data): with open(data, "r", encoding="utf-8") as f: xml = f.read() originalName = os.path.basename(data) elif model.source() == "xml": xml = data meta = model.metadata() originalName = model.originalName() if model.originalName() \ else "temp_{0}.model3".format(hash(time())) else: return False path = os.path.join(self.__qgisModelPath__, originalName) msg = self.tr( "Model '{0}' is already imported would you like to overwrite it?" ).format(path) if os.path.exists(path) and self.confirmAction(msg): os.remove(path) if not os.path.exists(path): with open(path, "w") as f: f.write(xml) self.orderedTableWidget.addRow( contents={ self.MODEL_NAME_HEADER: model.displayName(), self.MODEL_SOURCE_HEADER: path, self.ON_FLAGS_HEADER: { "halt": self.ON_FLAGS_HALT, "warn": self.ON_FLAGS_WARN, "ignore": self.ON_FLAGS_IGNORE }[model.onFlagsRaised()], self.LOAD_OUT_HEADER: model.loadOutput() }) return True def validateRowContents(self, contents): """ Checks if all attributes read from a row are valid. :param contents: (dict) map to (already read) row contents. :return: (str) invalidation reason """ if contents["displayName"] == "": return self.tr("Missing model's name.") if contents["source"]["data"] == "": return self.tr("Model is empty or file was not found.") return "" def models(self): """ Reads all table contents and sets it as a DsgToolsProcessingAlgorithm's set of parameters. :return: (dict) map to each model's set of parameters. """ models = dict() for row in range(self.modelCount()): contents = self.readRow(row) models[contents["displayName"]] = contents return models def validateModels(self): """ Check if each row on table has a valid input. :return: (str) invalidation reason. """ for row in range(self.modelCount()): msg = self.validateRowContents(self.readRow(row)) if msg: return "Row {row}: '{error}'".format(row=row + 1, error=msg) if len(self.models()) != self.modelCount(): return self.tr("Check if no model name is repeated.") return "" def workflowParameterMap(self): """ Generates a Workflow map from input data. """ return { "displayName": self.workflowName(), "models": self.models(), "metadata": { "author": self.author(), "version": self.version(), "lastModified": self.now() } } def currentWorkflow(self): """ Returns current workflow object as read from GUI. :return: (QualityAssuranceWorkflow) current workflow object. """ try: return QualityAssuranceWorkflow(self.workflowParameterMap()) except: return None def validate(self): """ Checks if all filled data generates a valid Workflow object. :return: (bool) validation status. """ if self.workflowName() == "": return self.tr("Workflow's name needs to be filled.") if self.author() == "": return self.tr("Workflow's author needs to be filled.") if self.version() == "": return self.tr("Workflow's version needs to be filled.") msg = self.validateModels() if msg != "": return msg return "" def exportWorkflow(self, filepath): """ Exports current data to a JSON file. :param filepath: (str) output file directory. """ QualityAssuranceWorkflow(self.workflowParameterMap()).export(filepath) @pyqtSlot(bool, name="on_exportPushButton_clicked") def export(self): """ Exports current input data as a workflow JSON, IF input is valid. :return: (bool) operation success. """ msg = self.validate() if msg != "": self.messageBar.pushMessage(self.tr('Invalid workflow'), msg, level=Qgis.Warning, duration=5) return False fd = QFileDialog() filename = fd.getSaveFileName( caption=self.tr("Export DSGTools Workflow"), filter=self.tr("DSGTools Workflow (*.workflow)")) filename = filename[0] if isinstance(filename, tuple) else "" if filename == "": return False filename = filename if filename.lower().endswith(".workflow") \ else "{0}.workflow".format(filename) try: self.exportWorkflow(filename) except Exception as e: self.messageBar.pushMessage( self.tr('Invalid workflow'), self.tr( "Unable to export workflow to '{fp}' ({error}).").format( fp=filename, error=str(e)), level=Qgis.Warning, duration=5) return False result = os.path.exists(filename) msg = (self.tr("Workflow exported to {fp}") if result else \ self.tr("Unable to export workflow to '{fp}'")).format(fp=filename) lvl = Qgis.Success if result else Qgis.Warning self.messageBar.pushMessage(self.tr('Workflow exportation'), msg, level=lvl, duration=5) return result def importWorkflow(self, filepath): """ Sets workflow contents from an imported DSGTools Workflow dump file. :param filepath: (str) workflow file to be imported. """ with open(filepath, "r", encoding="utf-8") as f: xml = json.load(f) workflow = QualityAssuranceWorkflow(xml) self.clear() self.setWorkflowAuthor(workflow.author()) self.setWorkflowVersion(workflow.version()) self.setWorkflowName(workflow.displayName()) for row, modelParam in enumerate(xml["models"].values()): self.setModelToRow(row, DsgToolsProcessingModel(modelParam, "")) @pyqtSlot(bool, name="on_importPushButton_clicked") def import_(self): """ Request a file for Workflow importation and sets it to GUI. :return: (bool) operation status. """ fd = QFileDialog() filename = fd.getOpenFileName( caption=self.tr('Select a Workflow file'), filter=self.tr('DSGTools Workflow (*.workflow *.json)')) filename = filename[0] if isinstance(filename, tuple) else "" if not filename: return False try: self.importWorkflow(filename) except Exception as e: self.messageBar.pushMessage( self.tr('Invalid workflow'), self.tr( "Unable to export workflow to '{fp}' ({error}).").format( fp=filename, error=str(e)), level=Qgis.Critical, duration=5) return False self.messageBar.pushMessage( self.tr('Success'), self.tr("Workflow '{fp}' imported!").format(fp=filename), level=Qgis.Info, duration=5) return True @pyqtSlot(bool, name="on_okPushButton_clicked") def ok(self): """ Closes dialog and checks if current workflow is valid. """ msg = self.validate() if msg == "" and self.currentWorkflow(): self.done(1) else: self.messageBar.pushMessage(self.tr('Invalid workflow'), self.validate(), level=Qgis.Warning, duration=5) @pyqtSlot(bool, name="on_cancelPushButton_clicked") def cancel(self): """ Restores GUI to last state and closes it. """ self.done(0)
def modelsFolder(self): return ModelerUtils.modelsFolders()[0]
def _loadAlgorithms(self): folder = ModelerUtils.modelsFolder() self.loadFromFolder(folder)
def initializeSettings(self): AlgorithmProvider.initializeSettings(self) ProcessingConfig.addSetting( Setting(self.getDescription(), ModelerUtils.MODELS_FOLDER, 'Models folder', ModelerUtils.modelsFolder()))
class DataValidationTool(QWidget, FORM_CLASS): """ Toolbar for fast usage of processing methods. It is assumed that the models have all of its child algorithm's variable's well defined and the only variables expected on input are the output layers, whenever needed and as many as it may be. """ __dsgToolsModelPath__ = os.path.join( os.path.dirname(__file__), "..", "..", "..", "..", "core", "Misc", "QGIS_Models" ) __qgisModelPath__ = ModelerUtils.modelsFolders()[0] modelAdded = pyqtSignal(str) modelRemoved = pyqtSignal(str) def __init__(self, iface, parent=None): """ Class constructor. :param iface: A QGIS interface instance. :param parent: (QtWidgets) widget parent to new instance of DataValidationTool. """ super(DataValidationTool, self).__init__(parent) self.parent = parent self.setupUi(self) self.iface = iface self.dsgToolsOptions = Options(self) # self.dsgToolsOptions.modelPathChanged.connect(self.resetModelList) self.activateTool() self.addShortcut() self.resetModelList() self._feedback = QgsProcessingFeedback() self._context = QgsProcessingContext() self._newModels = [] def _widgets(self): """ Gets a list of all [important] widgets. :return: (list-of-QtWidgets) all widgets this object parents. """ return [ self.validationPushButton, self.modelComboBox, self.addModelPushButton, self.removeModelPushButton, self.runModelPushButton, self.splitter ] def options(self): """ Reads tool parameters. :return: (dict) map of parameters for Validation Toolbar. """ return self.dsgToolsOptions.validationToolbarConfig() def model(self, idx=None): """ Gets current model name or the model from an index. :param idx: (int) order in model combo box. :return: (str) model name. """ if idx is not None and (self.modelComboBox.count() < idx or idx < 1): return "" return self.modelComboBox.currentText() def modelPath(self, idx=None): """ Gets a model's path from its index. :param idx: (int) order in model combo box. :return: (str) model path. """ model = self.model(idx) return os.path.join(self.defaultModelPath(), model) if model else "" def defaultModelPath(self): """ Gets the directory used to read and save the models shown on toolbar. :return: (str) default models path. """ return self.options()["defaultModelPath"] def resetModelList(self): """ Clear models listed and look refill it with current models. """ self.modelComboBox.clear() self.modelComboBox.addItem(self.tr("Select a model...")) models = [] for file_ in os.listdir(self.defaultModelPath()): if file_.endswith('.model') or file_.endswith('.model3'): models.append(file_) if models: self.modelComboBox.addItems(models) @pyqtSlot(bool, name='on_validationPushButton_toggled') def activateTool(self, toggled=None): """ Shows/hides the toolbar. :param toggled: (bool) toolbar status. """ if toggled is None: toggled = self.validationPushButton.isChecked() if toggled: self.splitter.show() else: self.splitter.hide() def confirmAction(self, msg, showCancel=True): """ Raises a message box for confirmation before executing an action. :param msg: (str) message to be exposed. :param showCancel: (bool) whether Cancel button should be exposed. :return: (bool) whether action was confirmed. """ if showCancel: return QMessageBox.question( self, self.tr('Confirm Action'), msg, QMessageBox.Ok|QMessageBox.Cancel ) == QMessageBox.Ok else: return QMessageBox.question( self, self.tr('Confirm Action'), msg, QMessageBox.Ok ) == QMessageBox.Ok def modelExists(self, modelName): """ Checks if model from modelPath exists (by name). :param modelName: (str) model name to be checked if exists. :return: (str) whether model exists into default directory. """ return os.path.exists( os.path.join(self.defaultModelPath(), os.path.basename(modelName)) ) def setActiveModel(self, modelName): """ Sets a model as current selected, if found on default directory. :param modelName: (str) model name to be set as active. :return: (bool) whether model was set. """ idx = self.modelComboBox.findText(modelName) if idx >= 0: self.modelComboBox.setCurrentIndex(idx) return True return False @pyqtSlot(int, name='on_modelComboBox_currentIndexChanged') def modelIsValid(self, idx): """ Checks if a model is valid and sets GUI buttons enabled if so. :param idx: (int) index for the model to be checked. """ enabled = idx > 0 self.removeModelPushButton.setEnabled(enabled) self.runModelPushButton.setEnabled(enabled) return enabled def addLayerToGroup(self, layer, groupname, subgroupname=None): """ Adds a layer to a group into layer panel. :param layer: (QgsMapLayer) layer to be added to canvas. :param groupname: (str) name for group to nest the layer. :param subgroupname: (str) name for the subgroup to be added. """ root = QgsProject.instance().layerTreeRoot() for g in root.children(): if g.name() == groupname: group = g break else: group = root.addGroup(groupname) if subgroupname is not None: for sg in group.children(): if sg.name() == subgroupname: subgroup = sg break else: subgroup = group.addGroup(subgroupname) QgsProject.instance().addMapLayer(layer, False) subgroup.insertChildNode(1, QgsLayerTreeLayer(layer)) @pyqtSlot(bool, name='on_updatePushButton_clicked') def updateModelList(self): """ Checks current default path for models and refreshes current displayed list. If current selection is found, it is kept as active. """ QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) currentModel = self.model() self.resetModelList() self.setActiveModel(currentModel) QApplication.restoreOverrideCursor() @pyqtSlot(bool, name='on_addModelPushButton_clicked') def registerModel(self, modelPath=None): """ Registers a model to the model runner. This application register all new models to a default directory. :param modelPath: (str) path to the model to be registered. """ if modelPath is None or not isinstance(modelPath, str): fd = QFileDialog() modelPathList = fd.getOpenFileNames( caption=self.tr('Select a QGIS processing model to be added'), filter=self.tr('QGIS Processing Model (*.model *.model3)') ) modelPathList = modelPathList[0] if modelPathList else modelPathList if modelPathList == []: return msg = self.tr( "Model seems to be already registered, would you like to overwrite" " it?" ) for modelPath in modelPathList: modelName = os.path.basename(modelPath) if self.modelExists(modelName) and not self.confirmAction(msg): QgsMessageLog.logMessage( self.tr("Model {model} was not imported.").format(model=modelName), 'DSGTools Plugin', Qgis.Info ) return dest = os.path.join(self.defaultModelPath(), modelName) copy(modelPath, dest) if os.path.exists(dest): self.modelComboBox.addItem(modelName) self._newModels.append(dest) self.setActiveModel(modelName) self.modelAdded.emit(modelName) QgsMessageLog.logMessage( self.tr("Model {model} imported to {dest}.").format(model=modelName, dest=dest), 'DSGTools Plugin', Qgis.Info ) @pyqtSlot(bool, name='on_removeModelPushButton_clicked') def unregisterModel(self, modelName=None): """ Unregisters a model to the model runner. Removes the model from the default directory. :param modelName: (str) basename for the model to be removed. """ if self.modelComboBox.currentIndex() < 1: return if not modelName or isinstance(modelName, bool): modelName = self.model() modelPath = self.modelPath() else: modelPath = os.path.join(self.defaultModelPath(), modelName) msg = self.tr("Remove model '{modelName}'?".format(modelName=modelName)) if self.confirmAction(msg) and self.modelExists(modelName): try: os.remove(modelPath) if not os.path.exists(modelPath): self.modelComboBox.removeItem(self.modelComboBox.findText(modelName)) self.modelRemoved.emit(modelName) except Exception as e: msg = self.tr("Unable to remove '{model}':\n{error}.").format( model=modelName, error=", ".join(map(str, e.args)) ) self.confirmAction(msg, showCancel=False) @pyqtSlot(bool, name='on_runModelPushButton_clicked') def runModel(self, modelName=None): """ Executes chosen model, if possible. :param modelPath: (str) path to the model to be registered. """ if self.modelComboBox.currentIndex() < 1: return if not modelName or isinstance(modelName, bool): modelName = self.model() modelPath = self.modelPath() else: modelPath = os.path.join(self.defaultModelPath(), modelName) alg = QgsProcessingModelAlgorithm() if not self.modelExists(modelName): # if model was manually removed and combo box was not refreshed self.iface.messageBar().pushMessage( self.tr('Failed'), self.tr("model {model} seems to have been deleted.").format(model=modelName), level=Qgis.Critical, duration=5 ) return alg.fromFile(modelPath) alg.initAlgorithm() # as this tool assumes that every parameter is pre-set, only output shall # be passed on - ALL outputs from this tool is set to memory layers. param = {vl.name() : "memory:" for vl in alg.parameterDefinitions()} msg = self.tr("Would you like to run {model}").format(model=modelName) if self.options()["checkBeforeRunModel"] and not self.confirmAction(msg): return try: out = processing.run(alg, param) self.iface.messageBar().pushMessage( self.tr('Sucess'), self.tr("model {model} finished.").format(model=modelName), level=Qgis.Info, duration=5 ) QgsMessageLog.logMessage( self.tr( "Model {model} finished running with no errors. You may" " check model output on Processing log tab." ).format(model=modelName), 'DSGTools Plugin', Qgis.Info ) if not self.options()["loadModelOutput"]: return for var, value in out.items(): if isinstance(value, QgsMapLayer): value.setName( "{model} {layername}".format(model=modelName, layername=var) ) self.addLayerToGroup( value, "DSGTools Validation Toolbar Output", modelName ) except Exception as e: msg = self.tr( "Unable to run (check Processing tab for details on model " "execution log) {model}:\n{error}" ).format(model=modelName, error=str(e)) self.iface.messageBar().pushMessage( self.tr("Model {model} failed").format(model=modelName), self.tr("check log for more information."), level=Qgis.Critical, duration=5 ) QgsMessageLog.logMessage(msg, 'DSGTools Plugin',Qgis.Info) def unload(self): """ Method called whenever tool is being destructed. Blocks signals and clears all objects that it parents. """ if self.options()["removeModelsOnExit"]: for model in self._newModels: if os.path.exists(model): os.remove(model) for w in self._widgets(): w.blockSignals(True) del w self.iface.unregisterMainWindowAction(self.runAction) def addShortcut(self): """ Adds the action to main menu allowing QGIS to assign a shortcut for run. """ self.runAction = QAction( QIcon(':/plugins/DsgTools/icons/runModel.png'), self.tr('DSGTools: Validation Toolbar - Run Processing Model'), self.parent ) self.runAction.triggered.connect(self.runModel) if self.parent: self.parent.addAction(self.runAction) self.iface.registerMainWindowAction(self.runAction, '')
def fromOldFormatFile(filename): def _tr(s): return QCoreApplication.translate('ModelerAlgorithm', s) hardcodedValues = {} modelParameters = [] modelAlgs = [] model = ModelerAlgorithm() model.descriptionFile = filename lines = codecs.open(filename, 'r', encoding='utf-8') line = lines.readline().strip('\n').strip('\r') try: while line != '': if line.startswith('PARAMETER:'): paramLine = line[len('PARAMETER:'):] param = getParameterFromString(paramLine) if param: pass else: raise WrongModelException( _tr('Error in parameter line: %s', 'ModelerAlgorithm') % line) line = lines.readline().strip('\n') tokens = line.split(',') model.addParameter( ModelerParameter( param, QPointF(float(tokens[0]), float(tokens[1])))) modelParameters.append(param.name) elif line.startswith('VALUE:'): valueLine = line[len('VALUE:'):] tokens = valueLine.split('===') name = tokens[0] value = tokens[1].replace( ModelerAlgorithm.LINE_BREAK_STRING, '\n') hardcodedValues[name] = value elif line.startswith('NAME:'): model.name = line[len('NAME:'):] elif line.startswith('GROUP:'): model.group = line[len('GROUP:'):] elif line.startswith('ALGORITHM:'): algLine = line[len('ALGORITHM:'):] alg = ModelerUtils.getAlgorithm(algLine) if alg is not None: modelAlg = Algorithm(alg.commandLineName()) modelAlg.description = alg.name posline = lines.readline().strip('\n').strip('\r') tokens = posline.split(',') modelAlg.pos = QPointF(float(tokens[0]), float(tokens[1])) # dependenceline = lines.readline().strip('\n').strip('\r') for param in alg.parameters: if not param.hidden: line = lines.readline().strip('\n').strip('\r') if line == unicode(None): modelAlg.params[param.name] = None else: tokens = line.split('|') try: algIdx = int(tokens[0]) except: raise WrongModelException( _tr('Number of parameters in the ' '{} algorithm does not match ' 'current Processing ' 'implementation'.format( alg.name))) if algIdx == -1: if tokens[1] in modelParameters: modelAlg.params[ param.name] = ValueFromInput( tokens[1]) else: modelAlg.params[ param.name] = hardcodedValues[ tokens[1]] else: modelAlg.params[ param.name] = ValueFromOutput( algIdx, tokens[1]) for out in alg.outputs: if not out.hidden: line = lines.readline().strip('\n').strip('\r') if unicode(None) != line: if '|' in line: tokens = line.split('|') name = tokens[0] tokens = tokens[1].split(',') pos = QPointF(float(tokens[0]), float(tokens[1])) else: name = line pos = None modelerOutput = ModelerOutput(name) modelerOutput.pos = pos modelAlg.outputs[out.name] = modelerOutput model.addAlgorithm(modelAlg) modelAlgs.append(modelAlg.name) else: raise WrongModelException( _tr('Error in algorithm name: %s', ) % algLine) line = lines.readline().strip('\n').strip('\r') for modelAlg in model.algs.values(): for name, value in modelAlg.params.iteritems(): if isinstance(value, ValueFromOutput): value.alg = modelAlgs[value.alg] return model except Exception as e: if isinstance(e, WrongModelException): raise e else: raise WrongModelException( _tr('Error in model definition line: ') + '%s\n%s' % (line.strip(), traceback.format_exc()))
class Options(QDialog, FORM_CLASS): __dsgToolsModelPath__ = os.path.abspath( os.path.join(os.path.dirname(__file__), "..", "..", "..", "core", "Misc", "QGIS_Models")) __qgisModelPath__ = ModelerUtils.modelsFolders()[0] # # some options signals # modelPathChanged = pyqtSignal() def __init__(self, parent=None): """Constructor.""" super(Options, self).__init__(parent) self.setupUi(self) self.setInterfaceWithParametersFromConfig() self.setupValidationToolbarConfig() # project option still not implemented self.removeModelsProjectCheckBox.hide() @pyqtSlot(bool) def on_addPushButton_clicked(self): newValue = self.addParameterLineEdit.text() valueList = [ self.blackListWidget.itemAt(i, 0).text() for i in range(self.blackListWidget.count()) ] if newValue == '': QMessageBox.warning(self, self.tr('Warning!'), self.tr('Fill in a value before adding!')) return if newValue in valueList: QMessageBox.warning(self, self.tr('Warning!'), self.tr('Value already in black list!')) return self.blackListWidget.addItem(newValue) self.blackListWidget.sortItems(order=Qt.AscendingOrder) self.addParameterLineEdit.setText('') def getParameters(self): freeHandTolerance = self.toleranceQgsDoubleSpinBox.value() freeHandSmoothIterations = self.smoothIterationsQgsSpinBox.value() freeHandSmoothOffset = self.smoothOffsetQgsDoubleSpinBox.value() algIterations = self.algIterationsQgsSpinBox.value() minSegmentDistance = self.minSegmentDistanceQgsSpinBox.value() valueList = [ self.blackListWidget.item(i).text() for i in range(self.blackListWidget.count()) ] undoPoints = self.undoQgsSpinBox.value() decimals = self.decimalQgsSpinBox.value() return (freeHandTolerance, freeHandSmoothIterations, freeHandSmoothOffset, algIterations, minSegmentDistance, valueList, undoPoints, decimals) def loadParametersFromConfig(self): settings = QSettings() settings.beginGroup('PythonPlugins/DsgTools/Options') freeHandTolerance = settings.value('freeHandTolerance') freeHandSmoothIterations = settings.value('freeHandSmoothIterations') freeHandSmoothOffset = settings.value('freeHandSmoothOffset') algIterations = settings.value('algIterations') minSegmentDistance = settings.value('minSegmentDistance') valueList = settings.value('valueList') undoPoints = settings.value('undoPoints') decimals = settings.value('decimals') if valueList: valueList = valueList.split(';') settings.endGroup() return (freeHandTolerance, freeHandSmoothIterations, freeHandSmoothOffset, algIterations, minSegmentDistance, valueList, undoPoints, decimals) def setInterfaceWithParametersFromConfig(self): (freeHandTolerance, freeHandSmoothIterations, freeHandSmoothOffset, algIterations, minSegmentDistance, valueList, undoPoints, decimals) = self.loadParametersFromConfig() if freeHandTolerance: self.toleranceQgsDoubleSpinBox.setValue(float(freeHandTolerance)) if freeHandSmoothIterations: self.smoothIterationsQgsSpinBox.setValue( int(freeHandSmoothIterations)) if freeHandSmoothOffset: self.smoothOffsetQgsDoubleSpinBox.setValue( float(freeHandSmoothOffset)) if algIterations: self.algIterationsQgsSpinBox.setValue(int(algIterations)) if minSegmentDistance: self.minSegmentDistanceQgsSpinBox.setValue(int(minSegmentDistance)) if valueList: self.blackListWidget.clear() self.blackListWidget.addItems(valueList) self.blackListWidget.sortItems(order=Qt.AscendingOrder) if undoPoints: self.undoQgsSpinBox.setValue(int(undoPoints)) def storeParametersInConfig(self): (freeHandTolerance, freeHandSmoothIterations, freeHandSmoothOffset, algIterations, minSegmentDistance, valueList, undoPoints, decimals) = self.getParameters() settings = QSettings() settings.beginGroup('PythonPlugins/DsgTools/Options') settings.setValue('freeHandTolerance', freeHandTolerance) settings.setValue('freeHandSmoothIterations', freeHandSmoothIterations) settings.setValue('freeHandSmoothOffset', freeHandSmoothOffset) settings.setValue('algIterations', algIterations) settings.setValue('minSegmentDistance', minSegmentDistance) settings.setValue('valueList', ';'.join(valueList)) settings.setValue('undoPoints', undoPoints) settings.setValue('decimals', decimals) settings.endGroup() @pyqtSlot() def on_buttonBox_accepted(self): self.storeParametersInConfig() self.updateValidationToolbarConfig() self.close() @pyqtSlot(bool) def on_removePushButton_clicked(self): selectedItems = self.blackListWidget.selectedItems() idxList = [] for i in range(self.blackListWidget.count()): if self.blackListWidget.item(i) in selectedItems: idxList.append(i) idxList.sort(reverse=True) for i in idxList: self.blackListWidget.takeItem(i) def firstTimeConfig(self): (freeHandTolerance, freeHandSmoothIterations, freeHandSmoothOffset, algIterations, minSegmentDistance, valueList, undoPoints, decimals) = self.loadParametersFromConfig() if not (freeHandTolerance and freeHandSmoothIterations and freeHandSmoothOffset and algIterations and valueList and undoPoints and decimals is not None): self.storeParametersInConfig() def setupModelPath(self): """ Clears all model paths and leaves the default options. """ self.modelPathComboBox.clear() self.modelPathComboBox.addItems( [self.__dsgToolsModelPath__, self.__qgisModelPath__]) def setupValidationToolbarConfig(self): """ Sets up Validation Toolbar parameters to DSGTools default values. """ # reset combo box to default as well self.setupModelPath() settings = QSettings() settings.beginGroup('PythonPlugins/DsgTools/Options') if settings.value('loadModelOutput') is None: settings.setValue('loadModelOutput', True) self.loadModelOutputCheckBox.setChecked( settings.value('loadModelOutput') in (True, "true")) if settings.value('checkBeforeRunModel') is None: settings.setValue('checkBeforeRunModel', True) self.checkBeforeRunModelCheckBox.setChecked( settings.value('checkBeforeRunModel') in (True, "true")) if settings.value('removeModelsOnExit') is None: settings.setValue('', False) self.resetModelsCheckBox.setChecked( settings.value('removeModelsOnExit') in (True, "true")) if settings.value('removeModelsOnNewProject') is None: settings.setValue('removeModelsOnNewProject', False) self.removeModelsProjectCheckBox.setChecked( settings.value('removeModelsOnNewProject') in (True, "true")) if settings.value('defaultModelPath') is None: settings.setValue('defaultModelPath', self.modelPathComboBox.currentText()) idx = self.modelPathComboBox.findText( settings.value('defaultModelPath')) if idx < 0: self.modelPathComboBox.addItem(settings.value('defaultModelPath')) self.modelPathComboBox.setCurrentText( settings.value('defaultModelPath')) settings.endGroup() def validationToolbarConfig(self): """ Reads all parameters for Validation Toolbar. :return: (dict) set of parameters for Validation Toolbar. """ settings = QSettings() settings.beginGroup('PythonPlugins/DsgTools/Options') loadModelOutput = settings.value('loadModelOutput') checkBeforeRunModel = settings.value('checkBeforeRunModel') removeModelsOnExit = settings.value('removeModelsOnExit') removeModelsOnNewProject = settings.value('removeModelsOnNewProject') defaultModelPath = settings.value('defaultModelPath') settings.endGroup() return { "loadModelOutput": loadModelOutput in (True, "true"), "checkBeforeRunModel": checkBeforeRunModel in (True, "true"), "removeModelsOnExit": removeModelsOnExit in (True, "true"), "removeModelsOnNewProject": removeModelsOnNewProject in (True, "true"), "defaultModelPath": defaultModelPath } def updateValidationToolbarConfig(self): """ Updates current Validation Toolbar parameter values from GUI. """ settings = QSettings() settings.beginGroup('PythonPlugins/DsgTools/Options') settings.setValue('loadModelOutput', self.loadModelOutputCheckBox.isChecked()) settings.setValue('checkBeforeRunModel', self.checkBeforeRunModelCheckBox.isChecked()) settings.setValue('removeModelsOnExit', self.resetModelsCheckBox.isChecked()) settings.setValue('removeModelsOnNewProject', self.removeModelsProjectCheckBox.isChecked()) # oldModelPath = settings.value('defaultModelPath') # newModelPath = self.modelPathComboBox.currentText() settings.setValue('defaultModelPath', self.modelPathComboBox.currentText()) settings.endGroup() # if oldModelPath != newModelPath: # self.modelPathChanged.emit() def addNewModelPath(self, modelPath, setAsDefault=True): """ Adds a custom model path as an option. :param modelPath: (str) path to look for QGIS Processing models. :param setAsDefault: (bool) whether current selection should be updated to new model path. :return: (int) index for new model path on its selection combo box. """ if not os.path.exists( modelPath) or self.modelPathComboBox.findText(modelPath) >= 0: return -1 self.modelPathComboBox.addItem(modelPath) idx = self.modelPathComboBox.findText(modelPath) if setAsDefault: self.modelPathComboBox.setCurrentIndex(idx) return idx @pyqtSlot(bool, name="on_addModelPathPushButton_clicked") def setCustomModelPath(self): """ Adds a custom model path and sets it as default. """ fd = QFileDialog() newModelPath = fd.getExistingDirectory( caption=self.tr('Select a directory for DSGTools Validation' ' Toolbar to look for QGIS Processing models')) newModelPath = newModelPath[0] if isinstance(newModelPath, tuple) else newModelPath if not newModelPath: return self.addNewModelPath(newModelPath)
def _loadAlgorithms(self): folders = ModelerUtils.modelsFolders() self.algs = [] for f in folders: self.loadFromFolder(f)
def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = ModelerUtils.getAlgorithm(item.alg.commandLineName()) self._addAlgorithm(alg.getCopy())
def openModel(self, filename): self.algPos = [] self.paramPos = [] self.outputOutputs = [] self.algs = [] self.algParameters = [] self.algOutputs = [] self.paramValues = {} self.dependencies = [] self.descriptionFile = filename lines = codecs.open(filename, "r", encoding='utf-8') line = lines.readline().strip("\n").strip("\r") iAlg = 0 try: while line != "": if line.startswith("PARAMETER:"): paramLine = line[len("PARAMETER:"):] param = ParameterFactory.getFromString(paramLine) if param: self.parameters.append(param) else: raise WrongModelException("Error in parameter line: " + line) line = lines.readline().strip("\n") tokens = line.split(",") self.paramPos.append( QtCore.QPointF(float(tokens[0]), float(tokens[1]))) elif line.startswith("VALUE:"): valueLine = line[len("VALUE:"):] tokens = valueLine.split("===") self.paramValues[tokens[0]] = tokens[1].replace( ModelerAlgorithm.LINE_BREAK_STRING, '\n') elif line.startswith("NAME:"): self.name = line[len("NAME:"):] elif line.startswith("GROUP:"): self.group = line[len("GROUP:"):] elif line.startswith("ALGORITHM:"): algParams = {} algOutputs = {} algLine = line[len("ALGORITHM:"):] alg = ModelerUtils.getAlgorithm(algLine) if alg is not None: posline = lines.readline().strip("\n").strip("\r") tokens = posline.split(",") self.algPos.append( QtCore.QPointF(float(tokens[0]), float(tokens[1]))) self.algs.append(alg) dependenceline = lines.readline().strip("\n").strip( "\r") dependencies = [] if dependenceline != str(None): for index in dependenceline.split(","): try: dependencies.append(int(index)) except: pass #a quick fix while I figure out how to solve problems when parsing this for param in alg.parameters: line = lines.readline().strip("\n").strip("\r") if line == str(None): algParams[param.name] = None else: tokens = line.split("|") algParams[param.name] = AlgorithmAndParameter( int(tokens[0]), tokens[1]) outputPos = {} for out in alg.outputs: line = lines.readline().strip("\n").strip("\r") if str(None) != line: if "|" in line: tokens = line.split("|") name = tokens[0] tokens = tokens[1].split(",") outputPos[out.name] = QtCore.QPointF( float(tokens[0]), float(tokens[1])) else: name = line outputPos[out.name] = None algOutputs[out.name] = name #we add the output to the algorithm, with a name indicating where it comes from #that guarantees that the name is unique output = copy.deepcopy(out) output.description = name output.name = self.getSafeNameForOutput( iAlg, output) self.addOutput(output) else: algOutputs[out.name] = None self.outputPos.append(outputPos) self.algOutputs.append(algOutputs) self.algParameters.append(algParams) self.dependencies.append(dependencies) iAlg += 1 else: raise WrongModelException("Error in algorithm name: " + algLine) line = lines.readline().strip("\n").strip("\r") except Exception, e: if isinstance(e, WrongModelException): raise e else: raise WrongModelException("Error in model definition line:" + line.strip() + " : " + traceback.format_exc())
def fromOldFormatFile(filename): def _tr(s): return QtCore.QCoreApplication.translate('ModelerAlgorithm', s) hardcodedValues = {} modelParameters = [] modelAlgs = [] model = ModelerAlgorithm() model.descriptionFile = filename lines = codecs.open(filename, 'r', encoding='utf-8') line = lines.readline().strip('\n').strip('\r') try: while line != '': if line.startswith('PARAMETER:'): paramLine = line[len('PARAMETER:'):] param = getParameterFromString(paramLine) if param: pass else: raise WrongModelException( _tr('Error in parameter line: %s', 'ModelerAlgorithm') % line) line = lines.readline().strip('\n') tokens = line.split(',') model.addParameter(ModelerParameter(param, QtCore.QPointF( float(tokens[0]), float(tokens[1])))) modelParameters.append(param.name) elif line.startswith('VALUE:'): valueLine = line[len('VALUE:'):] tokens = valueLine.split('===') name = tokens[0] value = tokens[1].replace(ModelerAlgorithm.LINE_BREAK_STRING, '\n') hardcodedValues[name] = value elif line.startswith('NAME:'): model.name = line[len('NAME:'):] elif line.startswith('GROUP:'): model.group = line[len('GROUP:'):] elif line.startswith('ALGORITHM:'): algLine = line[len('ALGORITHM:'):] alg = ModelerUtils.getAlgorithm(algLine) if alg is not None: modelAlg = Algorithm(alg.commandLineName()) modelAlg.description = alg.name posline = lines.readline().strip('\n').strip('\r') tokens = posline.split(',') modelAlg.pos = QtCore.QPointF(float(tokens[0]), float(tokens[1])) dependenceline = lines.readline().strip('\n').strip('\r') #unused for param in alg.parameters: if not param.hidden: line = lines.readline().strip('\n').strip('\r') if line == str(None): modelAlg.params[param.name] = None else: tokens = line.split('|') algIdx = int(tokens[0]) if algIdx == -1: if tokens[1] in modelParameters: modelAlg.params[param.name] = ValueFromInput(tokens[1]) else: modelAlg.params[param.name] = hardcodedValues[tokens[1]] else: modelAlg.params[param.name] = ValueFromOutput(algIdx, tokens[1]) for out in alg.outputs: if not out.hidden: line = lines.readline().strip('\n').strip('\r') if str(None) != line: if '|' in line: tokens = line.split('|') name = tokens[0] tokens = tokens[1].split(',') pos = QtCore.QPointF( float(tokens[0]), float(tokens[1])) else: name = line pos = None modelerOutput = ModelerOutput(name) modelerOutput.pos = pos modelAlg.outputs[out.name] = modelerOutput model.addAlgorithm(modelAlg) modelAlgs.append(modelAlg.name) else: raise WrongModelException( _tr('Error in algorithm name: %s', ) % algLine) line = lines.readline().strip('\n').strip('\r') for modelAlg in model.algs.values(): for name, value in modelAlg.params.iteritems(): if isinstance(value, ValueFromOutput): value.alg = modelAlgs[value.alg] return model except Exception, e: if isinstance(e, WrongModelException): raise e else: raise WrongModelException(_tr('Error in model definition line: ') + '%s\n%s' % (line.strip(), traceback.format_exc()))
def initializeSettings(self): AlgorithmProvider.initializeSettings(self) ProcessingConfig.addSetting(Setting(self.name(), ModelerUtils.MODELS_FOLDER, self.tr('Models folder', 'ModelerAlgorithmProvider'), ModelerUtils.defaultModelsFolder(), valuetype=Setting.MULTIPLE_FOLDERS))
def openModel(self, filename): self.algPos = [] self.paramPos = [] self.outputOutputs = [] self.algs = [] self.algParameters = [] self.algOutputs = [] self.paramValues = {} self.dependencies = [] self.descriptionFile = filename lines = codecs.open(filename, 'r', encoding='utf-8') line = lines.readline().strip('\n').strip('\r') iAlg = 0 try: while line != '': if line.startswith('PARAMETER:'): paramLine = line[len('PARAMETER:'):] param = ParameterFactory.getFromString(paramLine) if param: self.parameters.append(param) else: raise WrongModelException('Error in parameter line: ' + line) line = lines.readline().strip('\n') tokens = line.split(',') self.paramPos.append( QtCore.QPointF(float(tokens[0]), float(tokens[1]))) elif line.startswith('VALUE:'): valueLine = line[len('VALUE:'):] tokens = valueLine.split('===') self.paramValues[tokens[0]] = \ tokens[1].replace(ModelerAlgorithm.LINE_BREAK_STRING, '\n') elif line.startswith('NAME:'): self.name = line[len('NAME:'):] elif line.startswith('GROUP:'): self.group = line[len('GROUP:'):] if self.group == '[Test models]': self.showInModeler = False self.showInToolbox = False elif line.startswith('ALGORITHM:'): algParams = {} algOutputs = {} algLine = line[len('ALGORITHM:'):] alg = ModelerUtils.getAlgorithm(algLine) if alg is not None: posline = lines.readline().strip('\n').strip('\r') tokens = posline.split(',') self.algPos.append( QtCore.QPointF(float(tokens[0]), float(tokens[1]))) self.algs.append(alg) dependenceline = lines.readline().strip('\n').strip( '\r') dependencies = [] if dependenceline != str(None): for index in dependenceline.split(','): try: dependencies.append(int(index)) except: # A quick fix while I figure out # how to solve problems when # parsing this pass for param in alg.parameters: line = lines.readline().strip('\n').strip('\r') if line == str(None): algParams[param.name] = None else: tokens = line.split('|') algParams[param.name] = \ AlgorithmAndParameter(int(tokens[0]), tokens[1]) outputPos = {} for out in alg.outputs: line = lines.readline().strip('\n').strip('\r') if str(None) != line: if '|' in line: tokens = line.split('|') name = tokens[0] tokens = tokens[1].split(',') outputPos[out.name] = QtCore.QPointF( float(tokens[0]), float(tokens[1])) else: name = line outputPos[out.name] = None algOutputs[out.name] = name # We add the output to the algorithm, # with a name indicating where it comes # from that guarantees that the name is # unique output = copy.deepcopy(out) output.description = name output.name = self.getSafeNameForOutput( iAlg, output) self.addOutput(output) else: algOutputs[out.name] = None self.outputPos.append(outputPos) self.algOutputs.append(algOutputs) self.algParameters.append(algParams) self.dependencies.append(dependencies) iAlg += 1 else: raise WrongModelException('Error in algorithm name: ' + algLine) line = lines.readline().strip('\n').strip('\r') except Exception, e: if isinstance(e, WrongModelException): raise e else: raise WrongModelException('Error in model definition line:' + line.strip() + ' : ' + traceback.format_exc())
def _loadAlgorithms(self): folder = ModelerUtils.modelsFolder() self.loadFromFolder(folder) folder = os.path.join(os.path.dirname(__file__), 'models') self.loadFromFolder(folder)
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 saveModel(self, saveAs) -> bool: if not self.validateSave(QgsModelDesignerDialog.SaveAction.SaveAsFile): return False model_name_matched_file_name = self.model().modelNameMatchesFilePath() if self.model().sourceFilePath() and not saveAs: filename = self.model().sourceFilePath() else: if self.model().sourceFilePath(): initial_path = Path(self.model().sourceFilePath()) elif self.model().name(): initial_path = Path(ModelerUtils.modelsFolders()[0]) / ( self.model().name() + '.model3') else: initial_path = Path(ModelerUtils.modelsFolders()[0]) filename, _ = QFileDialog.getSaveFileName( self, self.tr('Save Model'), initial_path.as_posix(), self.tr('Processing models (*.model3 *.MODEL3)')) if not filename: return False filename = QgsFileUtils.ensureFileNameHasExtension( filename, ['model3']) self.model().setSourceFilePath(filename) if not self.model().name() or self.model().name() == self.tr( 'model'): self.setModelName(Path(filename).stem) elif saveAs and model_name_matched_file_name: # if saving as, and the model name used to match the filename, then automatically update the # model name to match the new file name self.setModelName(Path(filename).stem) 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"), 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 False self.update_model.emit() if saveAs: self.messageBar().pushMessage( "", self.tr("Model was saved to <a href=\"{}\">{}</a>").format( QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.setDirty(False) return True
def checkBeforeOpeningParametersDialog(self): for alg in self.algs.values(): algInstance = ModelerUtils.getAlgorithm(alg.consoleName) if algInstance is None: return "The model you are trying to run contains an algorithm that is not available: <i>%s</i>" % alg.consoleName
def algorithm(self): if self._algInstance is None: self._algInstance = ModelerUtils.getAlgorithm( self.consoleName).getCopy() return self._algInstance