QGIS/python/plugins/processing/gui/GetScriptsAndModels.py
2015-10-03 14:05:33 +02:00

285 lines
12 KiB
Python

# -*- 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. *
* *
***************************************************************************
"""
__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 os
import json
import urllib2
from urllib2 import HTTPError
from PyQt4 import uic
from PyQt4.QtCore import Qt, QCoreApplication
from PyQt4.QtGui import QIcon, QMessageBox, QCursor, QApplication, QTreeWidgetItem
from qgis.utils import iface
from processing.gui.ToolboxAction import ToolboxAction
from processing.script.ScriptUtils import ScriptUtils
from processing.algs.r.RUtils import RUtils
from processing.modeler.ModelerUtils import ModelerUtils
from processing.gui import Help2Html
from processing.gui.Help2Html import getDescription, ALG_DESC, ALG_VERSION, ALG_CREATOR
pluginPath = os.path.split(os.path.dirname(__file__))[0]
WIDGET, BASE = uic.loadUiType(
os.path.join(pluginPath, 'ui', 'DlgGetScriptsAndModels.ui'))
class GetScriptsAction(ToolboxAction):
def __init__(self):
self.name = self.tr('Get scripts from on-line scripts collection', 'GetScriptsAction')
self.group = self.tr('Tools', 'GetScriptsAction')
def getIcon(self):
return QIcon(os.path.join(pluginPath, '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(),
self.tr('Connection problem', 'GetScriptsAction'),
self.tr('Could not connect to scripts/models repository', 'GetScriptsAction'))
class GetRScriptsAction(ToolboxAction):
def __init__(self):
self.name = self.tr('Get R scripts from on-line scripts collection', 'GetRScriptsAction')
self.group = self.tr('Tools', 'GetRScriptsAction')
def getIcon(self):
return QIcon(os.path.join(pluginPath, 'images', 'r.png'))
def execute(self):
try:
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.RSCRIPTS)
dlg.exec_()
if dlg.updateToolbox:
self.toolbox.updateProvider('r')
except HTTPError:
QMessageBox.critical(iface.mainWindow(),
self.tr('Connection problem', 'GetRScriptsAction'),
self.tr('Could not connect to scripts/models repository', 'GetRScriptsAction'))
class GetModelsAction(ToolboxAction):
def __init__(self):
self.name = self.tr('Get models from on-line scripts collection', 'GetModelsAction')
self.group = self.tr('Tools', 'GetModelsAction')
def getIcon(self):
return QIcon(os.path.join(pluginPath, 'images', 'model.png'))
def execute(self):
try:
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.MODELS)
dlg.exec_()
if dlg.updateToolbox:
self.toolbox.updateProvider('model')
except (HTTPError, URLError):
QMessageBox.critical(iface.mainWindow(),
self.tr('Connection problem', 'GetModelsAction'),
self.tr('Could not connect to scripts/models repository', 'GetModelsAction'))
def readUrl(url):
try:
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
return urllib2.urlopen(url).read()
finally:
QApplication.restoreOverrideCursor()
class GetScriptsAndModelsDialog(BASE, WIDGET):
HELP_TEXT = QCoreApplication.translate('GetScriptsAndModelsDialog',
'<h3> Processing resources manager </h3>'
'<p>Check/uncheck algorithms in the tree to select the ones that you '
'want to install or remove</p>'
'<p>Algorithms are divided in 3 groups:</p>'
'<ul><li><b>Installed:</b> Algorithms already in your system, with '
'the latest version available</li>'
'<li><b>Updatable:</b> Algorithms already in your system, but with '
'a newer version available in the server</li>'
'<li><b>Not installed:</b> Algorithms not installed in your '
'system</li></ul>')
MODELS = 0
SCRIPTS = 1
RSCRIPTS = 2
def __init__(self, resourceType):
super(GetScriptsAndModelsDialog, self).__init__(iface.mainWindow())
self.setupUi(self)
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 populateTree(self):
self.uptodateItem = QTreeWidgetItem()
self.uptodateItem.setText(0, self.tr('Installed'))
self.toupdateItem = QTreeWidgetItem()
self.toupdateItem.setText(0, self.tr('Updatable'))
self.notinstalledItem = QTreeWidgetItem()
self.notinstalledItem.setText(0, self.tr('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]
self.resources = {f: (v, n) for f, v, n in resources}
for filename, version, name in sorted(resources, key=lambda kv: kv[2].lower()):
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 = '<h2>%s</h2>' % item.name
html += self.tr('<p><b>Description:</b> %s</p>') % getDescription(ALG_DESC, descriptions)
html += self.tr('<p><b>Created by:</b> %s') % getDescription(ALG_CREATOR, descriptions)
html += self.tr('<p><b>Version:</b> %s') % getDescription(ALG_VERSION, descriptions)
except HTTPError as e:
html = self.tr('<h2>No detailed description available for this script</h2>')
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')
try:
with open(helpFile) as f:
helpContent = json.load(f)
currentVersion = float(helpContent[Help2Html.ALG_VERSION])
except Exception:
currentVersion = 0
if 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')
try:
code = readUrl(url)
path = os.path.join(self.folder, filename)
with open(path, 'w') as f:
f.write(code)
except HTTPError:
QMessageBox.critical(iface.mainWindow(),
self.tr('Connection problem'),
self.tr('Could not download file: %s') % filename)
return
url += '.help'
try:
html = readUrl(url)
except HTTPError:
html = '{"ALG_VERSION" : %s}' % self.resources[filename][0]
path = os.path.join(self.folder, filename + '.help')
with open(path, 'w') as f:
f.write(html)
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)