20
20
21
21
__author__ = 'Victor Olaya'
22
22
__date__ = 'June 2014'
23
- __copyright__ = '(C) 201 , Victor Olaya'
23
+ __copyright__ = '(C) 2014 , Victor Olaya'
24
24
25
25
# This will get replaced with a git SHA1 when you do a git archive
26
26
27
27
__revision__ = '$Format:%H$'
28
28
29
29
import os
30
30
import json
31
- import urllib2
32
- from urllib2 import HTTPError
31
+ from functools import partial
33
32
34
33
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
37
37
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
39
41
40
42
from processing .gui .ToolboxAction import ToolboxAction
41
43
from processing .script .ScriptUtils import ScriptUtils
48
50
WIDGET , BASE = uic .loadUiType (
49
51
os .path .join (pluginPath , 'ui' , 'DlgGetScriptsAndModels.ui' ))
50
52
51
-
52
53
class GetScriptsAction (ToolboxAction ):
53
54
54
55
def __init__ (self ):
@@ -59,15 +60,10 @@ def getIcon(self):
59
60
return QIcon (os .path .join (pluginPath , 'images' , 'script.png' ))
60
61
61
62
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' )
71
67
72
68
73
69
class GetRScriptsAction (ToolboxAction ):
@@ -80,15 +76,10 @@ def getIcon(self):
80
76
return QIcon (os .path .join (pluginPath , 'images' , 'r.png' ))
81
77
82
78
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' )
92
83
93
84
94
85
class GetModelsAction (ToolboxAction ):
@@ -101,23 +92,10 @@ def getIcon(self):
101
92
return QIcon (os .path .join (pluginPath , 'images' , 'model.png' ))
102
93
103
94
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' )
121
99
122
100
123
101
class GetScriptsAndModelsDialog (BASE , WIDGET ):
@@ -137,9 +115,14 @@ class GetScriptsAndModelsDialog(BASE, WIDGET):
137
115
SCRIPTS = 1
138
116
RSCRIPTS = 2
139
117
118
+ tr_disambiguation = { 0 : 'GetModelsAction' ,
119
+ 1 : 'GetScriptsAction' ,
120
+ 2 : 'GetRScriptsAction' }
121
+
140
122
def __init__ (self , resourceType ):
141
123
super (GetScriptsAndModelsDialog , self ).__init__ (iface .mainWindow ())
142
124
self .setupUi (self )
125
+ self .manager = QgsNetworkAccessManager .instance ()
143
126
144
127
self .resourceType = resourceType
145
128
if self .resourceType == self .MODELS :
@@ -160,8 +143,30 @@ def __init__(self, resourceType):
160
143
self .populateTree ()
161
144
self .buttonBox .accepted .connect (self .okPressed )
162
145
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
+
165
170
def populateTree (self ):
166
171
self .uptodateItem = QTreeWidgetItem ()
167
172
self .uptodateItem .setText (0 , self .tr ('Installed' ))
@@ -172,35 +177,53 @@ def populateTree(self):
172
177
self .toupdateItem .setIcon (0 , self .icon )
173
178
self .uptodateItem .setIcon (0 , self .icon )
174
179
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 )
184
181
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 ()
185
202
self .tree .addTopLevelItem (self .toupdateItem )
186
203
self .tree .addTopLevelItem (self .notinstalledItem )
187
204
self .tree .addTopLevelItem (self .uptodateItem )
188
205
189
206
self .webView .setHtml (self .HELP_TEXT )
190
207
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
+
191
223
def currentItemChanged (self , item , prev ):
192
224
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 )
204
227
else :
205
228
self .webView .setHtml (self .HELP_TEXT )
206
229
@@ -223,6 +246,26 @@ def getTreeBranchForState(self, filename, version):
223
246
def cancelPressed (self ):
224
247
self .close ()
225
248
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
+
226
269
def okPressed (self ):
227
270
toDownload = []
228
271
for i in xrange (self .toupdateItem .childCount ()):
@@ -235,39 +278,27 @@ def okPressed(self):
235
278
toDownload .append (item .filename )
236
279
237
280
if toDownload :
238
- self .progressBar .setMaximum (len (toDownload ))
281
+ self .progressBar .setMaximum (len (toDownload ) * 2 )
239
282
for i , filename in enumerate (toDownload ):
240
283
QCoreApplication .processEvents ()
241
284
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
+
252
287
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' )
262
289
263
290
toDelete = []
264
291
for i in xrange (self .uptodateItem .childCount ()):
265
292
item = self .uptodateItem .child (i )
266
293
if item .checkState (0 ) == Qt .Unchecked :
267
294
toDelete .append (item .filename )
295
+
296
+ # Remove py and help files if they exist
268
297
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 )
271
302
272
303
self .updateToolbox = len (toDownload ) + len (toDelete ) > 0
273
304
self .close ()
0 commit comments