Skip to content

Commit f2e527f

Browse files
author
Médéric Ribreux
committedOct 27, 2015
[Processing] Add proxy support for Get scripts and models (fixes #13412)
1 parent fe6456f commit f2e527f

File tree

1 file changed

+117
-86
lines changed

1 file changed

+117
-86
lines changed
 

‎python/plugins/processing/gui/GetScriptsAndModels.py

Lines changed: 117 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,24 @@
2020

2121
__author__ = 'Victor Olaya'
2222
__date__ = 'June 2014'
23-
__copyright__ = '(C) 201, Victor Olaya'
23+
__copyright__ = '(C) 2014, Victor Olaya'
2424

2525
# This will get replaced with a git SHA1 when you do a git archive
2626

2727
__revision__ = '$Format:%H$'
2828

2929
import os
3030
import json
31-
import urllib2
32-
from urllib2 import HTTPError
31+
from functools import partial
3332

3433
from PyQt4 import uic
35-
from PyQt4.QtCore import Qt, QCoreApplication
36-
from PyQt4.QtGui import QIcon, QMessageBox, QCursor, QApplication, QTreeWidgetItem
34+
from PyQt4.QtCore import Qt, QCoreApplication, QUrl
35+
from PyQt4.QtGui import QIcon, QCursor, QApplication, QTreeWidgetItem, QPushButton
36+
from PyQt4.QtNetwork import QNetworkReply, QNetworkRequest
3737

38-
from qgis.utils import iface
38+
from qgis.utils import iface, show_message_log
39+
from qgis.core import QgsNetworkAccessManager, QgsMessageLog
40+
from qgis.gui import QgsMessageBar
3941

4042
from processing.gui.ToolboxAction import ToolboxAction
4143
from processing.script.ScriptUtils import ScriptUtils
@@ -48,7 +50,6 @@
4850
WIDGET, BASE = uic.loadUiType(
4951
os.path.join(pluginPath, 'ui', 'DlgGetScriptsAndModels.ui'))
5052

51-
5253
class GetScriptsAction(ToolboxAction):
5354

5455
def __init__(self):
@@ -59,15 +60,10 @@ def getIcon(self):
5960
return QIcon(os.path.join(pluginPath, 'images', 'script.png'))
6061

6162
def execute(self):
62-
try:
63-
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.SCRIPTS)
64-
dlg.exec_()
65-
if dlg.updateToolbox:
66-
self.toolbox.updateProvider('script')
67-
except HTTPError:
68-
QMessageBox.critical(iface.mainWindow(),
69-
self.tr('Connection problem', 'GetScriptsAction'),
70-
self.tr('Could not connect to scripts/models repository', 'GetScriptsAction'))
63+
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.SCRIPTS)
64+
dlg.exec_()
65+
if dlg.updateToolbox:
66+
self.toolbox.updateProvider('script')
7167

7268

7369
class GetRScriptsAction(ToolboxAction):
@@ -80,15 +76,10 @@ def getIcon(self):
8076
return QIcon(os.path.join(pluginPath, 'images', 'r.png'))
8177

8278
def execute(self):
83-
try:
84-
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.RSCRIPTS)
85-
dlg.exec_()
86-
if dlg.updateToolbox:
87-
self.toolbox.updateProvider('r')
88-
except HTTPError:
89-
QMessageBox.critical(iface.mainWindow(),
90-
self.tr('Connection problem', 'GetRScriptsAction'),
91-
self.tr('Could not connect to scripts/models repository', 'GetRScriptsAction'))
79+
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.RSCRIPTS)
80+
dlg.exec_()
81+
if dlg.updateToolbox:
82+
self.toolbox.updateProvider('r')
9283

9384

9485
class GetModelsAction(ToolboxAction):
@@ -101,23 +92,10 @@ def getIcon(self):
10192
return QIcon(os.path.join(pluginPath, 'images', 'model.png'))
10293

10394
def execute(self):
104-
try:
105-
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.MODELS)
106-
dlg.exec_()
107-
if dlg.updateToolbox:
108-
self.toolbox.updateProvider('model')
109-
except (HTTPError, URLError):
110-
QMessageBox.critical(iface.mainWindow(),
111-
self.tr('Connection problem', 'GetModelsAction'),
112-
self.tr('Could not connect to scripts/models repository', 'GetModelsAction'))
113-
114-
115-
def readUrl(url):
116-
try:
117-
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
118-
return urllib2.urlopen(url).read()
119-
finally:
120-
QApplication.restoreOverrideCursor()
95+
dlg = GetScriptsAndModelsDialog(GetScriptsAndModelsDialog.MODELS)
96+
dlg.exec_()
97+
if dlg.updateToolbox:
98+
self.toolbox.updateProvider('model')
12199

122100

123101
class GetScriptsAndModelsDialog(BASE, WIDGET):
@@ -137,9 +115,14 @@ class GetScriptsAndModelsDialog(BASE, WIDGET):
137115
SCRIPTS = 1
138116
RSCRIPTS = 2
139117

118+
tr_disambiguation = { 0: 'GetModelsAction',
119+
1: 'GetScriptsAction',
120+
2: 'GetRScriptsAction' }
121+
140122
def __init__(self, resourceType):
141123
super(GetScriptsAndModelsDialog, self).__init__(iface.mainWindow())
142124
self.setupUi(self)
125+
self.manager = QgsNetworkAccessManager.instance()
143126

144127
self.resourceType = resourceType
145128
if self.resourceType == self.MODELS:
@@ -160,8 +143,30 @@ def __init__(self, resourceType):
160143
self.populateTree()
161144
self.buttonBox.accepted.connect(self.okPressed)
162145
self.buttonBox.rejected.connect(self.cancelPressed)
163-
self.tree.currentItemChanged .connect(self.currentItemChanged)
164-
146+
self.tree.currentItemChanged.connect(self.currentItemChanged)
147+
148+
def popupError(self, error=None, url=None):
149+
"""Popups an Error message bar for network errors."""
150+
disambiguation = self.tr_disambiguation[self.resourceType]
151+
widget = iface.messageBar().createMessage(self.tr('Connection problem', disambiguation),
152+
self.tr('Could not connect to scripts/models repository', disambiguation))
153+
if error and url:
154+
QgsMessageLog.logMessage(self.tr(u"Network error code: {} on URL: {}").format(error, url), u"Processing", QgsMessageLog.CRITICAL)
155+
button = QPushButton(QCoreApplication.translate("Python", "View message log"), pressed=show_message_log)
156+
widget.layout().addWidget(button)
157+
158+
iface.messageBar().pushWidget(widget, level=QgsMessageBar.CRITICAL, duration=5)
159+
160+
def grabHTTP(self, url, loadFunction, arguments=None):
161+
"""Grab distant content via QGIS internal classes and QtNetwork."""
162+
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
163+
request = QUrl(url)
164+
reply = self.manager.get(QNetworkRequest(request))
165+
if arguments:
166+
reply.finished.connect(partial(loadFunction, reply, arguments))
167+
else:
168+
reply.finished.connect(partial(loadFunction, reply))
169+
165170
def populateTree(self):
166171
self.uptodateItem = QTreeWidgetItem()
167172
self.uptodateItem.setText(0, self.tr('Installed'))
@@ -172,35 +177,53 @@ def populateTree(self):
172177
self.toupdateItem.setIcon(0, self.icon)
173178
self.uptodateItem.setIcon(0, self.icon)
174179
self.notinstalledItem.setIcon(0, self.icon)
175-
resources = readUrl(self.urlBase + 'list.txt').splitlines()
176-
resources = [r.split(',') for r in resources]
177-
self.resources = {f: (v, n) for f, v, n in resources}
178-
for filename, version, name in sorted(resources, key=lambda kv: kv[2].lower()):
179-
treeBranch = self.getTreeBranchForState(filename, float(version))
180-
item = TreeItem(filename, name, self.icon)
181-
treeBranch.addChild(item)
182-
if treeBranch != self.notinstalledItem:
183-
item.setCheckState(0, Qt.Checked)
180+
self.grabHTTP(self.urlBase + 'list.txt', self.treeLoaded)
184181

182+
def treeLoaded(self, reply):
183+
"""
184+
update the tree of scripts/models whenever
185+
HTTP request is finished
186+
"""
187+
QApplication.restoreOverrideCursor()
188+
if reply.error() != QNetworkReply.NoError:
189+
self.popupError(reply.error(), reply.request().url().toString())
190+
else:
191+
resources = unicode(reply.readAll()).splitlines()
192+
resources = [r.split(',') for r in resources]
193+
self.resources = {f: (v, n) for f, v, n in resources}
194+
for filename, version, name in sorted(resources, key=lambda kv: kv[2].lower()):
195+
treeBranch = self.getTreeBranchForState(filename, float(version))
196+
item = TreeItem(filename, name, self.icon)
197+
treeBranch.addChild(item)
198+
if treeBranch != self.notinstalledItem:
199+
item.setCheckState(0, Qt.Checked)
200+
201+
reply.deleteLater()
185202
self.tree.addTopLevelItem(self.toupdateItem)
186203
self.tree.addTopLevelItem(self.notinstalledItem)
187204
self.tree.addTopLevelItem(self.uptodateItem)
188205

189206
self.webView.setHtml(self.HELP_TEXT)
190207

208+
def setHelp(self, reply, item):
209+
"""Change the webview HTML content"""
210+
QApplication.restoreOverrideCursor()
211+
if reply.error() != QNetworkReply.NoError:
212+
html = self.tr('<h2>No detailed description available for this script</h2>')
213+
else:
214+
content = unicode(reply.readAll())
215+
descriptions = json.loads(content)
216+
html = '<h2>%s</h2>' % item.name
217+
html += self.tr('<p><b>Description:</b> %s</p>') % getDescription(ALG_DESC, descriptions)
218+
html += self.tr('<p><b>Created by:</b> %s') % getDescription(ALG_CREATOR, descriptions)
219+
html += self.tr('<p><b>Version:</b> %s') % getDescription(ALG_VERSION, descriptions)
220+
reply.deleteLater()
221+
self.webView.setHtml(html)
222+
191223
def currentItemChanged(self, item, prev):
192224
if isinstance(item, TreeItem):
193-
try:
194-
url = self.urlBase + item.filename.replace(' ', '%20') + '.help'
195-
helpContent = readUrl(url)
196-
descriptions = json.loads(helpContent)
197-
html = '<h2>%s</h2>' % item.name
198-
html += self.tr('<p><b>Description:</b> %s</p>') % getDescription(ALG_DESC, descriptions)
199-
html += self.tr('<p><b>Created by:</b> %s') % getDescription(ALG_CREATOR, descriptions)
200-
html += self.tr('<p><b>Version:</b> %s') % getDescription(ALG_VERSION, descriptions)
201-
except HTTPError as e:
202-
html = self.tr('<h2>No detailed description available for this script</h2>')
203-
self.webView.setHtml(html)
225+
url = self.urlBase + item.filename.replace(' ', '%20') + '.help'
226+
self.grabHTTP(url, self.setHelp, item)
204227
else:
205228
self.webView.setHtml(self.HELP_TEXT)
206229

@@ -223,6 +246,26 @@ def getTreeBranchForState(self, filename, version):
223246
def cancelPressed(self):
224247
self.close()
225248

249+
def storeFile(self, reply, filename):
250+
"""store a script/model that has been downloaded"""
251+
QApplication.restoreOverrideCursor()
252+
if reply.error() != QNetworkReply.NoError:
253+
if os.path.splitext(filename)[1].lower() == '.help':
254+
content = '{"ALG_VERSION" : %s}' % self.resources[filename[:-5]][0]
255+
else:
256+
self.popupError(reply.error(), reply.request().url().toString())
257+
content = None
258+
else:
259+
content = reply.readAll()
260+
261+
reply.deleteLater()
262+
if content:
263+
path = os.path.join(self.folder, filename)
264+
with open(path, 'w') as f:
265+
f.write(content)
266+
267+
self.progressBar.setValue(self.progressBar.value() + 1)
268+
226269
def okPressed(self):
227270
toDownload = []
228271
for i in xrange(self.toupdateItem.childCount()):
@@ -235,39 +278,27 @@ def okPressed(self):
235278
toDownload.append(item.filename)
236279

237280
if toDownload:
238-
self.progressBar.setMaximum(len(toDownload))
281+
self.progressBar.setMaximum(len(toDownload) * 2)
239282
for i, filename in enumerate(toDownload):
240283
QCoreApplication.processEvents()
241284
url = self.urlBase + filename.replace(' ', '%20')
242-
try:
243-
code = readUrl(url)
244-
path = os.path.join(self.folder, filename)
245-
with open(path, 'w') as f:
246-
f.write(code)
247-
except HTTPError:
248-
QMessageBox.critical(iface.mainWindow(),
249-
self.tr('Connection problem'),
250-
self.tr('Could not download file: %s') % filename)
251-
return
285+
self.grabHTTP(url, self.storeFile, filename)
286+
252287
url += '.help'
253-
try:
254-
html = readUrl(url)
255-
except HTTPError:
256-
html = '{"ALG_VERSION" : %s}' % self.resources[filename][0]
257-
258-
path = os.path.join(self.folder, filename + '.help')
259-
with open(path, 'w') as f:
260-
f.write(html)
261-
self.progressBar.setValue(i + 1)
288+
self.grabHTTP(url, self.storeFile, filename + '.help')
262289

263290
toDelete = []
264291
for i in xrange(self.uptodateItem.childCount()):
265292
item = self.uptodateItem.child(i)
266293
if item.checkState(0) == Qt.Unchecked:
267294
toDelete.append(item.filename)
295+
296+
# Remove py and help files if they exist
268297
for filename in toDelete:
269-
path = os.path.join(self.folder, filename)
270-
os.remove(path)
298+
for pathname in (filename, filename + u".help"):
299+
path = os.path.join(self.folder, pathname)
300+
if os.path.exists(path):
301+
os.remove(path)
271302

272303
self.updateToolbox = len(toDownload) + len(toDelete) > 0
273304
self.close()

0 commit comments

Comments
 (0)
Please sign in to comment.