[processing] support multiple scripts/models folders (fix #10476)

This commit is contained in:
Alexander Bruy 2016-05-24 21:56:17 +03:00
parent d17094611b
commit d16f04bf3a
11 changed files with 231 additions and 43 deletions

View File

@ -62,8 +62,8 @@ class RAlgorithmProvider(AlgorithmProvider):
AlgorithmProvider.initializeSettings(self)
ProcessingConfig.addSetting(Setting(
self.getDescription(), RUtils.RSCRIPTS_FOLDER,
self.tr('R Scripts folder'), RUtils.RScriptsFolder(),
valuetype=Setting.FOLDER))
self.tr('R Scripts folder'), RUtils.defaultRScriptsFolder(),
valuetype=Setting.MULTIPLE_FOLDERS))
if isWindows():
ProcessingConfig.addSetting(Setting(
self.getDescription(),
@ -95,8 +95,11 @@ class RAlgorithmProvider(AlgorithmProvider):
return 'r'
def _loadAlgorithms(self):
folder = RUtils.RScriptsFolder()
self.loadFromFolder(folder)
folders = RUtils.RScriptsFolders()
self.algs = []
for f in folders:
self.loadFromFolder(f)
folder = os.path.join(os.path.dirname(__file__), 'scripts')
self.loadFromFolder(folder)

View File

@ -84,18 +84,19 @@ class RUtils:
return os.path.abspath(unicode(folder))
@staticmethod
def RScriptsFolder():
folder = ProcessingConfig.getSetting(RUtils.RSCRIPTS_FOLDER)
if folder is None:
folder = unicode(os.path.join(userFolder(), 'rscripts'))
try:
mkdir(folder)
except:
folder = unicode(os.path.join(userFolder(), 'rscripts'))
mkdir(folder)
def defaultRScriptsFolder():
folder = unicode(os.path.join(userFolder(), 'rscripts'))
mkdir(folder)
return os.path.abspath(folder)
@staticmethod
def RScriptsFolders():
folder = ProcessingConfig.getSetting(RUtils.RSCRIPTS_FOLDER)
if folder is not None:
return folder.split(';')
else:
return [RUtils.defaultRScriptsFolder()]
@staticmethod
def createRScriptFromRCommands(commands):
scriptfile = open(RUtils.getRScriptFilename(), 'w')

View File

@ -228,6 +228,7 @@ class Setting:
SELECTION = 3
FLOAT = 4
INT = 5
MULTIPLE_FOLDERS = 6
def __init__(self, group, name, description, default, hidden=False, valuetype=None,
validator=None, options=None):
@ -264,6 +265,13 @@ class Setting:
if v and not os.path.exists(v):
raise ValueError(self.tr('Specified path does not exist:\n%s') % unicode(v))
validator = checkFileOrFolder
elif valuetype == self.MULTIPLE_FOLDERS:
def checkMultipleFolders(v):
folders = v.split(';')
for f in folders:
if f and not os.path.exists(f):
raise ValueError(self.tr('Specified path does not exist:\n%s') % unicode(f))
validator = checkMultipleFolders
else:
def validator(x):
return True

View File

@ -54,6 +54,7 @@ from processing.core.ProcessingConfig import (ProcessingConfig,
settingsWatcher,
Setting)
from processing.core.Processing import Processing
from processing.gui.DirectorySelectorDialog import DirectorySelectorDialog
from processing.gui.menus import updateMenus
from processing.gui.menus import menusSettingsGroup
@ -284,12 +285,7 @@ class SettingDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
QStyledItemDelegate.__init__(self, parent)
def createEditor(
self,
parent,
options,
index,
):
def createEditor(self, parent, options, index):
setting = index.model().data(index, Qt.UserRole)
if setting.valuetype == Setting.FOLDER:
return FileDirectorySelector(parent)
@ -299,6 +295,8 @@ class SettingDelegate(QStyledItemDelegate):
combo = QComboBox(parent)
combo.addItems(setting.options)
return combo
elif setting.valuetype == Setting.MULTIPLE_FOLDERS:
return MultipleDirectorySelector(parent)
else:
value = self.convertValue(index.model().data(index, Qt.EditRole))
if isinstance(value, (int, long)):
@ -398,3 +396,44 @@ class FileDirectorySelector(QWidget):
def setText(self, value):
self.lineEdit.setText(value)
class MultipleDirectorySelector(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
# create gui
self.btnSelect = QToolButton()
self.btnSelect.setText(self.tr('...'))
self.lineEdit = QLineEdit()
self.hbl = QHBoxLayout()
self.hbl.setMargin(0)
self.hbl.setSpacing(0)
self.hbl.addWidget(self.lineEdit)
self.hbl.addWidget(self.btnSelect)
self.setLayout(self.hbl)
self.canFocusOut = False
self.setFocusPolicy(Qt.StrongFocus)
self.btnSelect.clicked.connect(self.select)
def select(self):
text = self.lineEdit.text()
if text != '':
items = text.split(';')
dlg = DirectorySelectorDialog(None, items)
if dlg.exec_():
text = dlg.value()
self.lineEdit.setText(text)
self.canFocusOut = True
def text(self):
return self.lineEdit.text()
def setText(self, value):
self.lineEdit.setText(value)

View File

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
"""
***************************************************************************
DirectorySelectorDialog.py
---------------------
Date : May 2016
Copyright : (C) 2016 by Alexander Bruy
Email : alexander dot bruy at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""
__author__ = 'Alexander Bruy'
__date__ = 'May 2016'
__copyright__ = '(C) 2016, Victor Olaya'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import os
from qgis.PyQt import uic
from qgis.PyQt.QtCore import QSettings
from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QPushButton, QDialogButtonBox, QFileDialog
from qgis.PyQt.QtGui import QStandardItemModel, QStandardItem
pluginPath = os.path.split(os.path.dirname(__file__))[0]
WIDGET, BASE = uic.loadUiType(
os.path.join(pluginPath, 'ui', 'DlgMultipleSelection.ui'))
class DirectorySelectorDialog(BASE, WIDGET):
def __init__(self, parent, options):
super(DirectorySelectorDialog, self).__init__(None)
self.setupUi(self)
self.lstLayers.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.options = options
# Additional buttons
self.btnAdd = QPushButton(self.tr('Add'))
self.buttonBox.addButton(self.btnAdd,
QDialogButtonBox.ActionRole)
self.btnRemove = QPushButton(self.tr('Remove'))
self.buttonBox.addButton(self.btnRemove,
QDialogButtonBox.ActionRole)
self.btnRemoveAll = QPushButton(self.tr('Remove all'))
self.buttonBox.addButton(self.btnRemoveAll,
QDialogButtonBox.ActionRole)
self.btnAdd.clicked.connect(self.addDirectory)
self.btnRemove.clicked.connect(lambda: self.removeRows())
self.btnRemoveAll.clicked.connect(lambda: self.removeRows(True))
self.populateList()
def populateList(self):
model = QStandardItemModel()
for option in self.options:
item = QStandardItem(option)
model.appendRow(item)
self.lstLayers.setModel(model)
def accept(self):
self.selectedoptions = []
model = self.lstLayers.model()
for i in xrange(model.rowCount()):
item = model.item(i)
self.selectedoptions.append(item.text())
QDialog.accept(self)
def reject(self):
QDialog.reject(self)
def addDirectory(self):
settings = QSettings()
if settings.contains('/Processing/lastDirectory'):
path = settings.value('/Processing/lastDirectory')
else:
path = ''
folder = QFileDialog.getExistingDirectory(self,
self.tr('Select directory'),
path,
QFileDialog.ShowDirsOnly)
if folder == '':
return
model = self.lstLayers.model()
item = QStandardItem(folder)
model.appendRow(item)
settings.setValue('/Processing/lastDirectory',
os.path.dirname(folder))
def removeRows(self, removeAll=False):
if removeAll:
self.lstLayers.model().clear()
else:
self.lstLayers.setUpdatesEnabled(False)
indexes = sorted(self.lstLayers.selectionModel().selectedIndexes())
for i in reversed(indexes):
self.lstLayers.model().removeRow(i.row())
self.lstLayers.setUpdatesEnabled(True)
def value(self):
folders = []
model = self.lstLayers.model()
for i in xrange(model.rowCount()):
folders.append(model.item(i).text())
return ';'.join(folders)

View File

@ -193,10 +193,10 @@ class ScriptEditorDialog(BASE, WIDGET):
return
if self.algType == self.SCRIPT_PYTHON:
scriptDir = ScriptUtils.scriptsFolder()
scriptDir = ScriptUtils.defaultScriptsFolder()
filterName = self.tr('Python scripts (*.py)')
elif self.algType == self.SCRIPT_R:
scriptDir = RUtils.RScriptsFolder()
scriptDir = RUtils.defaultRScriptsFolder()
filterName = self.tr('Processing R script (*.rsx)')
self.filename = QFileDialog.getOpenFileName(
@ -224,10 +224,10 @@ class ScriptEditorDialog(BASE, WIDGET):
def saveScript(self, saveAs):
if self.filename is None or saveAs:
if self.algType == self.SCRIPT_PYTHON:
scriptDir = ScriptUtils.scriptsFolder()
scriptDir = ScriptUtils.defaultScriptsFolder()
filterName = self.tr('Python scripts (*.py)')
elif self.algType == self.SCRIPT_R:
scriptDir = RUtils.RScriptsFolder()
scriptDir = RUtils.defaultRScriptsFolder()
filterName = self.tr('Processing R script (*.rsx)')
self.filename = unicode(QFileDialog.getSaveFileName(self,

View File

@ -55,7 +55,7 @@ class ModelerAlgorithmProvider(AlgorithmProvider):
AlgorithmProvider.initializeSettings(self)
ProcessingConfig.addSetting(Setting(self.getDescription(),
ModelerUtils.MODELS_FOLDER, self.tr('Models folder', 'ModelerAlgorithmProvider'),
ModelerUtils.modelsFolder(), valuetype=Setting.FOLDER))
ModelerUtils.defaultModelsFolder(), valuetype=Setting.MULTIPLE_FOLDERS))
def modelsFolder(self):
return ModelerUtils.modelsFolder()
@ -70,11 +70,12 @@ class ModelerAlgorithmProvider(AlgorithmProvider):
return QIcon(os.path.join(pluginPath, 'images', 'model.png'))
def _loadAlgorithms(self):
folder = ModelerUtils.modelsFolder()
self.loadFromFolder(folder)
folders = ModelerUtils.modelsFolders()
self.algs = []
for f in folders:
self.loadFromFolder(f)
def loadFromFolder(self, folder):
self.algs = []
if not os.path.exists(folder):
return
for path, subdirs, files in os.walk(folder):

View File

@ -310,7 +310,7 @@ class ModelerDialog(BASE, WIDGET):
else:
filename = unicode(QFileDialog.getSaveFileName(self,
self.tr('Save Model'),
ModelerUtils.modelsFolder(),
ModelerUtils.defaultModelsFolder(),
self.tr('Processing models (*.model)')))
if filename:
if not filename.endswith('.model'):
@ -341,7 +341,7 @@ class ModelerDialog(BASE, WIDGET):
def openModel(self):
filename = unicode(QFileDialog.getOpenFileName(self,
self.tr('Open Model'), ModelerUtils.modelsFolder(),
self.tr('Open Model'), ModelerUtils.defaultModelsFolder(),
self.tr('Processing models (*.model *.MODEL)')))
if filename:
try:

View File

@ -36,11 +36,16 @@ class ModelerUtils:
ACTIVATE_MODELS = 'ACTIVATE_MODELS'
@staticmethod
def modelsFolder():
folder = ProcessingConfig.getSetting(ModelerUtils.MODELS_FOLDER)
if folder is None:
folder = unicode(os.path.join(userFolder(), 'models'))
def defaultModelsFolder():
folder = unicode(os.path.join(userFolder(), 'models'))
mkdir(folder)
return os.path.abspath(folder)
@staticmethod
def modelsFolders():
folder = ProcessingConfig.getSetting(ModelerUtils.MODELS_FOLDER)
if folder is not None:
return folder.split(';')
else:
return [ModelerUtils.defaultModelsFolder()]

View File

@ -58,7 +58,7 @@ class ScriptAlgorithmProvider(AlgorithmProvider):
ProcessingConfig.addSetting(Setting(self.getDescription(),
ScriptUtils.SCRIPTS_FOLDER,
self.tr('Scripts folder', 'ScriptAlgorithmProvider'),
ScriptUtils.scriptsFolder(), valuetype=Setting.FOLDER))
ScriptUtils.defaultScriptsFolder(), valuetype=Setting.MULTIPLE_FOLDERS))
def unload(self):
AlgorithmProvider.unload(self)
@ -74,8 +74,10 @@ class ScriptAlgorithmProvider(AlgorithmProvider):
return self.tr('Scripts', 'ScriptAlgorithmProvider')
def _loadAlgorithms(self):
folder = ScriptUtils.scriptsFolder()
self.algs = ScriptUtils.loadFromFolder(folder)
folders = ScriptUtils.scriptsFolders()
self.algs = []
for f in folders:
self.algs.extend(ScriptUtils.loadFromFolder(f))
def addAlgorithmsFromFolder(self, folder):
self.algs.extend(ScriptUtils.loadFromFolder(folder))

View File

@ -39,14 +39,19 @@ class ScriptUtils(object):
ACTIVATE_SCRIPTS = 'ACTIVATE_SCRIPTS'
@staticmethod
def scriptsFolder():
folder = ProcessingConfig.getSetting(ScriptUtils.SCRIPTS_FOLDER)
if folder is None:
folder = unicode(os.path.join(userFolder(), 'scripts'))
def defaultScriptsFolder():
folder = unicode(os.path.join(userFolder(), 'scripts'))
mkdir(folder)
return os.path.abspath(folder)
@staticmethod
def scriptsFolders():
folder = ProcessingConfig.getSetting(ScriptUtils.SCRIPTS_FOLDER)
if folder is not None:
return folder.split(';')
else:
return [ScriptUtils.defaultScriptsFolder()]
@staticmethod
def loadFromFolder(folder):
if not os.path.exists(folder):