diff --git a/python/plugins/processing/gui/GetScriptsAndModels.py b/python/plugins/processing/gui/GetScriptsAndModels.py
new file mode 100644
index 00000000000..cbf4f4055cf
--- /dev/null
+++ b/python/plugins/processing/gui/GetScriptsAndModels.py
@@ -0,0 +1,237 @@
+# -*- coding: utf-8 -*-
+
+"""
+***************************************************************************
+ GetScriptsAndModels.py
+ ---------------------
+ Date : June 2014
+ Copyright : (C) 2014 by Victor Olaya
+ Email : volayaf 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. *
+* *
+***************************************************************************
+"""
+import os
+from PyQt4 import QtGui
+from processing.ui.ui_DlgGetScriptsAndModels import Ui_DlgGetScriptsAndModels
+from processing.script.ScriptUtils import ScriptUtils
+from processing.modeler.ModelerUtils import ModelerUtils
+import json
+from processing.gui import Help2Html
+from qgis.utils import iface
+from processing.gui.Help2Html import getDescription, ALG_DESC, ALG_VERSION,\
+ ALG_CREATOR
+
+__author__ = 'Victor Olaya'
+__date__ = 'June 2014'
+__copyright__ = '(C) 201, Victor Olaya'
+
+# This will get replaced with a git SHA1 when you do a git archive
+
+__revision__ = '$Format:%H$'
+
+import urllib2
+from urllib2 import HTTPError
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+from processing.gui.ToolboxAction import ToolboxAction
+
+
+class GetScriptsAction(ToolboxAction):
+
+ def __init__(self):
+ self.name = "Get scripts from on-line scripts collection"
+ self.group = 'Tools'
+
+ def getIcon(self):
+ return QIcon(':/processing/images/script.png')
+
+ def execute(self):
+ try:
+ dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.SCRIPTS)
+ dlg.exec_()
+ if dlg.updateToolbox:
+ self.toolbox.updateProvider('script')
+
+ except HTTPError:
+ QMessageBox.critical(iface.mainWindow(), "Connection problem", "Could not connect to scripts/models repository")
+
+class GetModelsAction(ToolboxAction):
+
+ def __init__(self):
+ self.name = "Get models from on-line scripts collection"
+ self.group = 'Tools'
+
+ def getIcon(self):
+ return QIcon(':/processing/images/model.png')
+
+ def execute(self):
+ try:
+ dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.MODELS)
+ dlg.exec_()
+ if dlg.updateToolbox:
+ self.toolbox.updateProvider('model')
+ except HTTPError:
+ QMessageBox.critical(iface.mainWindow(), "Connection problem", "Could not connect to scripts/models repository")
+
+
+def readUrl(url):
+ try:
+ QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
+ return urllib2.urlopen(url).read()
+ finally:
+ QApplication.restoreOverrideCursor()
+
+
+class GetScriptsAndModelsDialog(QDialog, Ui_DlgGetScriptsAndModels):
+
+ HELP_TEXT = ("
Processing resources manager
"
+ "Check/uncheck algorithms in the tree to select the ones that you want to install or remove
"
+ "Algorithms are divided in 3 groups:
"
+ "- Installed: Algorihms already in your system, with the latest version available
"
+ "- Upgradable: Algorihms already in your system, but with a newer version available in the server
"
+ "- Not installed: Algorithms not installed in your system
")
+ MODELS = 0
+ SCRIPTS = 1
+
+ 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 populateTree(self):
+ self.uptodateItem = QTreeWidgetItem()
+ self.uptodateItem.setText(0, "Installed")
+ self.toupdateItem = QTreeWidgetItem()
+ self.toupdateItem.setText(0, "Upgradable")
+ self.notinstalledItem = QTreeWidgetItem()
+ self.notinstalledItem.setText(0, "Not installed")
+ self.toupdateItem.setIcon(0, self.icon)
+ self.uptodateItem.setIcon(0, self.icon)
+ self.notinstalledItem.setIcon(0, self.icon)
+ resources = readUrl(self.urlBase + "list.txt").splitlines()
+ resources = [r.split(",") for r in resources]
+ for filename, version, name in resources:
+ treeBranch = self.getTreeBranchForState(filename, float(version))
+ item = TreeItem(filename, name, self.icon)
+ treeBranch.addChild(item)
+ if treeBranch != self.notinstalledItem:
+ item.setCheckState(0, Qt.Checked)
+
+ self.tree.addTopLevelItem(self.toupdateItem)
+ self.tree.addTopLevelItem(self.notinstalledItem)
+ self.tree.addTopLevelItem(self.uptodateItem)
+
+ self.webView.setHtml(self.HELP_TEXT)
+
+ def currentItemChanged(self, item, prev):
+ if isinstance(item, TreeItem):
+ try:
+ url = self.urlBase + item.filename.replace(" ","%20") + ".help"
+ helpContent = readUrl(url)
+ descriptions = json.loads(helpContent)
+ html = "%s
" % item.name
+ html+="Description: " + getDescription(ALG_DESC, descriptions)+"
"
+ html+="Created by: " + getDescription(ALG_CREATOR, descriptions)+"
"
+ html+="Version: " + getDescription(ALG_VERSION, descriptions)+"
"
+ except HTTPError, e:
+ html = "No detailed description available for this script
"
+ self.webView.setHtml(html)
+ else:
+ self.webView.setHtml(self.HELP_TEXT)
+
+ def getTreeBranchForState(self, filename, version):
+ if not os.path.exists(os.path.join(self.folder, filename)):
+ return self.notinstalledItem
+ else:
+ helpFile = os.path.join(self.folder, filename + ".help")
+ if not os.path.exists(helpFile):
+ currentVersion = 1
+ else:
+ with open(helpFile) as f:
+ helpContent = json.load(f)
+ try:
+ currentVersion = float(helpContent[Help2Html.ALG_VERSION])
+ except:
+ currentVersion = 1
+ print filename, currentVersion, version
+ if version > currentVersion:
+ print version - currentVersion
+ return self.toupdateItem
+ else:
+ return self.uptodateItem
+
+
+ def cancelPressed(self):
+ self.close()
+
+ def okPressed(self):
+ toDownload = []
+ for i in xrange(self.toupdateItem.childCount()):
+ item = self.toupdateItem.child(i)
+ if item.checkState(0) == Qt.Checked:
+ toDownload.append(item.filename)
+ for i in xrange(self.notinstalledItem.childCount()):
+ item = self.notinstalledItem.child(i)
+ if item.checkState(0) == Qt.Checked:
+ toDownload.append(item.filename)
+
+ if toDownload:
+ self.progressBar.setMaximum(len(toDownload))
+ for i, filename in enumerate(toDownload):
+ QCoreApplication.processEvents()
+ url = self.urlBase + filename.replace(" ","%20")
+ code = readUrl(url)
+ path = os.path.join(self.folder, filename)
+ with open(path, "w") as f:
+ f.write(code)
+ self.progressBar.setValue(i + 1)
+
+ toDelete = []
+ for i in xrange(self.uptodateItem.childCount()):
+ item = self.uptodateItem.child(i)
+ if item.checkState(0) == Qt.Unchecked:
+ toDelete.append(item.filename)
+ for filename in toDelete:
+ path = os.path.join(self.folder, filename)
+ os.remove(path)
+
+ self.updateToolbox = len(toDownload) + len(toDelete)> 0
+ self.close()
+
+
+class TreeItem(QTreeWidgetItem):
+
+ def __init__(self, filename, name, icon):
+ QTreeWidgetItem.__init__(self)
+ self.name = name
+ self.filename = filename
+ self.setText(0, name)
+ self.setIcon(0, icon)
+ self.setCheckState(0, Qt.Unchecked)
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python/plugins/processing/gui/Help2Html.py b/python/plugins/processing/gui/Help2Html.py
index a508f7f01c8..a001d1a1f19 100644
--- a/python/plugins/processing/gui/Help2Html.py
+++ b/python/plugins/processing/gui/Help2Html.py
@@ -33,6 +33,7 @@ import json
ALG_DESC = 'ALG_DESC'
ALG_CREATOR = 'ALG_CREATOR'
ALG_HELP_CREATOR = 'ALG_HELP_CREATOR'
+ALG_VERSION = 'ALG_VERSION'
exps = [(r"\*(.*?)\*", r"\1"),
("``(.*?)``", r'\1'),
@@ -56,7 +57,6 @@ def getHtmlFromRstFile(rst):
def getHtmlFromHelpFile(alg, helpFile):
if not os.path.exists(helpFile):
return None
- alg = alg
with open(helpFile) as f:
descriptions = json.load(f)
s = 'Algorithm description
\n'
@@ -72,6 +72,7 @@ def getHtmlFromHelpFile(alg, helpFile):
s += '
'
s += 'Algorithm author: ' + getDescription(ALG_CREATOR, descriptions) + '
'
s += 'Help author: ' + getDescription(ALG_HELP_CREATOR, descriptions) + '
'
+ s += 'Algorithm version: ' + getDescription(ALG_VERSION, descriptions) + '
'
s += ''
return s
@@ -80,3 +81,4 @@ def getDescription(name, descriptions):
return descriptions[name].replace("\n", "
")
else:
return ''
+
diff --git a/python/plugins/processing/gui/HelpEditionDialog.py b/python/plugins/processing/gui/HelpEditionDialog.py
index 80878e94cc9..5336f1a6731 100644
--- a/python/plugins/processing/gui/HelpEditionDialog.py
+++ b/python/plugins/processing/gui/HelpEditionDialog.py
@@ -40,6 +40,7 @@ class HelpEditionDialog(QDialog, Ui_DlgHelpEdition):
ALG_DESC = 'ALG_DESC'
ALG_CREATOR = 'ALG_CREATOR'
ALG_HELP_CREATOR = 'ALG_HELP_CREATOR'
+ ALG_VERSION = 'ALG_VERSION'
def __init__(self, alg):
QDialog.__init__(self)
@@ -117,6 +118,9 @@ class HelpEditionDialog(QDialog, Ui_DlgHelpEdition):
item = TreeDescriptionItem('Algorithm help written by',
self.ALG_HELP_CREATOR)
self.tree.addTopLevelItem(item)
+ item = TreeDescriptionItem('Algorithm version',
+ self.ALG_VERSION)
+ self.tree.addTopLevelItem(item)
def changeItem(self):
item = self.tree.currentItem()
diff --git a/python/plugins/processing/modeler/ModelerAlgorithmProvider.py b/python/plugins/processing/modeler/ModelerAlgorithmProvider.py
index 2b092f0e563..6f77f9d2900 100644
--- a/python/plugins/processing/modeler/ModelerAlgorithmProvider.py
+++ b/python/plugins/processing/modeler/ModelerAlgorithmProvider.py
@@ -40,13 +40,14 @@ from processing.modeler.EditModelAction import EditModelAction
from processing.modeler.CreateNewModelAction import CreateNewModelAction
from processing.modeler.DeleteModelAction import DeleteModelAction
from processing.modeler.AddModelFromFileAction import AddModelFromFileAction
+from processing.gui.GetScriptsAndModels import GetModelsAction
class ModelerAlgorithmProvider(AlgorithmProvider):
def __init__(self):
AlgorithmProvider.__init__(self)
- self.actions = [CreateNewModelAction(), AddModelFromFileAction()]
+ self.actions = [CreateNewModelAction(), AddModelFromFileAction(), GetModelsAction()]
self.contextMenuActions = [EditModelAction(), DeleteModelAction(),
SaveAsPythonScriptAction()]
diff --git a/python/plugins/processing/script/ScriptAlgorithmProvider.py b/python/plugins/processing/script/ScriptAlgorithmProvider.py
index d259696899b..ebc888d14bd 100644
--- a/python/plugins/processing/script/ScriptAlgorithmProvider.py
+++ b/python/plugins/processing/script/ScriptAlgorithmProvider.py
@@ -38,7 +38,7 @@ from processing.script.ScriptAlgorithm import ScriptAlgorithm
from processing.script.ScriptUtils import ScriptUtils
from processing.script.WrongScriptException import WrongScriptException
from processing.script.AddScriptFromFileAction import AddScriptFromFileAction
-
+from processing.gui.GetScriptsAndModels import GetScriptsAction
import processing.resources_rc
@@ -48,7 +48,8 @@ class ScriptAlgorithmProvider(AlgorithmProvider):
AlgorithmProvider.__init__(self)
self.actions.extend([CreateNewScriptAction('Create new script',
CreateNewScriptAction.SCRIPT_PYTHON),
- AddScriptFromFileAction()])
+ AddScriptFromFileAction(),
+ GetScriptsAction()])
self.contextMenuActions = \
[EditScriptAction(EditScriptAction.SCRIPT_PYTHON),
DeleteScriptAction(DeleteScriptAction.SCRIPT_PYTHON)]
diff --git a/python/plugins/processing/script/scripts/Unique_values_count.py b/python/plugins/processing/script/scripts/Unique_values_count.py
index bf466803ca3..682d29688d1 100644
--- a/python/plugins/processing/script/scripts/Unique_values_count.py
+++ b/python/plugins/processing/script/scripts/Unique_values_count.py
@@ -1,4 +1,4 @@
-##[Example scripts]aster processing=group
+##[Example scripts]=group
##input=raster
##round_values_to_ndigits=number 3
##output_file=output html
diff --git a/python/plugins/processing/ui/DlgGetScriptsAndModels.ui b/python/plugins/processing/ui/DlgGetScriptsAndModels.ui
new file mode 100644
index 00000000000..735a0ce3ad2
--- /dev/null
+++ b/python/plugins/processing/ui/DlgGetScriptsAndModels.ui
@@ -0,0 +1,154 @@
+
+
+ DlgGetScriptsAndModels
+
+
+
+ 0
+ 0
+ 826
+ 520
+
+
+
+ Get scripts and models
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+ 350
+ 0
+
+
+
+
+ 100000
+ 100000
+
+
+
+ QAbstractItemView::SingleSelection
+
+
+ false
+
+
+ false
+
+
+ 350
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+
+ 1
+
+
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 10000
+ 10000
+
+
+
+
+ about:blank
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+ 0
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 200
+ 16777215
+
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+ QWebView
+ QWidget
+
+
+
+
+
+
diff --git a/python/plugins/processing/ui/ui_DlgGetScriptsAndModels.py b/python/plugins/processing/ui/ui_DlgGetScriptsAndModels.py
new file mode 100644
index 00000000000..507c77ec167
--- /dev/null
+++ b/python/plugins/processing/ui/ui_DlgGetScriptsAndModels.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'DlgGetScriptsAndModels.ui'
+#
+# Created: Mon Jun 02 20:12:43 2014
+# by: PyQt4 UI code generator 4.9.6
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt4 import QtCore, QtGui
+
+try:
+ _fromUtf8 = QtCore.QString.fromUtf8
+except AttributeError:
+ def _fromUtf8(s):
+ return s
+
+try:
+ _encoding = QtGui.QApplication.UnicodeUTF8
+ def _translate(context, text, disambig):
+ return QtGui.QApplication.translate(context, text, disambig, _encoding)
+except AttributeError:
+ def _translate(context, text, disambig):
+ return QtGui.QApplication.translate(context, text, disambig)
+
+class Ui_DlgGetScriptsAndModels(object):
+ def setupUi(self, DlgGetScriptsAndModels):
+ DlgGetScriptsAndModels.setObjectName(_fromUtf8("DlgGetScriptsAndModels"))
+ DlgGetScriptsAndModels.resize(826, 520)
+ self.verticalLayout = QtGui.QVBoxLayout(DlgGetScriptsAndModels)
+ self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
+ self.splitter = QtGui.QSplitter(DlgGetScriptsAndModels)
+ self.splitter.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter.setObjectName(_fromUtf8("splitter"))
+ self.tree = QtGui.QTreeWidget(self.splitter)
+ self.tree.setMinimumSize(QtCore.QSize(350, 0))
+ self.tree.setMaximumSize(QtCore.QSize(100000, 100000))
+ self.tree.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+ self.tree.setObjectName(_fromUtf8("tree"))
+ self.tree.headerItem().setText(0, _fromUtf8("1"))
+ self.tree.header().setVisible(False)
+ self.tree.header().setCascadingSectionResizes(False)
+ self.tree.header().setDefaultSectionSize(350)
+ self.tree.header().setHighlightSections(False)
+ self.tree.header().setSortIndicatorShown(True)
+ self.tree.header().setStretchLastSection(True)
+ self.frame = QtGui.QFrame(self.splitter)
+ self.frame.setFrameShape(QtGui.QFrame.StyledPanel)
+ self.frame.setFrameShadow(QtGui.QFrame.Sunken)
+ self.frame.setObjectName(_fromUtf8("frame"))
+ self.horizontalLayout = QtGui.QHBoxLayout(self.frame)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setMargin(0)
+ self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
+ self.webView = QtWebKit.QWebView(self.frame)
+ self.webView.setMaximumSize(QtCore.QSize(10000, 10000))
+ self.webView.setUrl(QtCore.QUrl(_fromUtf8("about:blank")))
+ self.webView.setObjectName(_fromUtf8("webView"))
+ self.horizontalLayout.addWidget(self.webView)
+ self.verticalLayout.addWidget(self.splitter)
+ self.horizontalLayout_2 = QtGui.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
+ self.progressBar = QtGui.QProgressBar(DlgGetScriptsAndModels)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.progressBar.sizePolicy().hasHeightForWidth())
+ self.progressBar.setSizePolicy(sizePolicy)
+ self.progressBar.setMinimumSize(QtCore.QSize(0, 0))
+ self.progressBar.setProperty("value", 0)
+ self.progressBar.setInvertedAppearance(False)
+ self.progressBar.setObjectName(_fromUtf8("progressBar"))
+ self.horizontalLayout_2.addWidget(self.progressBar)
+ self.buttonBox = QtGui.QDialogButtonBox(DlgGetScriptsAndModels)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.buttonBox.sizePolicy().hasHeightForWidth())
+ self.buttonBox.setSizePolicy(sizePolicy)
+ self.buttonBox.setMaximumSize(QtCore.QSize(200, 16777215))
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
+ self.horizontalLayout_2.addWidget(self.buttonBox)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+
+ self.retranslateUi(DlgGetScriptsAndModels)
+ QtCore.QMetaObject.connectSlotsByName(DlgGetScriptsAndModels)
+
+ def retranslateUi(self, DlgGetScriptsAndModels):
+ DlgGetScriptsAndModels.setWindowTitle(_translate("DlgGetScriptsAndModels", "Get scripts and models", None))
+
+from PyQt4 import QtWebKit