|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
00001 """! 00002 @package modules.extensions 00003 00004 @brief GRASS Addons extensions management classes 00005 00006 Classes: 00007 - extensions::InstallExtensionWindow 00008 - extensions::ExtensionTree 00009 - extensions::UninstallExtensionWindow 00010 - extensions::CheckListExtension 00011 00012 (C) 2008-2011 by the GRASS Development Team 00013 00014 This program is free software under the GNU General Public License 00015 (>=v2). Read the file COPYING that comes with GRASS for details. 00016 00017 @author Martin Landa <landa.martin gmail.com> 00018 """ 00019 00020 import os 00021 import sys 00022 00023 import wx 00024 import wx.lib.mixins.listctrl as listmix 00025 try: 00026 import wx.lib.agw.customtreectrl as CT 00027 except ImportError: 00028 import wx.lib.customtreectrl as CT 00029 import wx.lib.flatnotebook as FN 00030 00031 import grass.script as grass 00032 from grass.script import task as gtask 00033 00034 from core import globalvar 00035 from core.gcmd import GError, RunCommand 00036 from gui_core.forms import GUI 00037 from gui_core.widgets import ItemTree 00038 from gui_core.ghelp import SearchModuleWindow 00039 00040 class InstallExtensionWindow(wx.Frame): 00041 def __init__(self, parent, id = wx.ID_ANY, 00042 title = _("Fetch & install extension from GRASS Addons"), **kwargs): 00043 self.parent = parent 00044 self.options = dict() # list of options 00045 00046 wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs) 00047 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 00048 00049 self.panel = wx.Panel(parent = self, id = wx.ID_ANY) 00050 00051 self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 00052 label = " %s " % _("Repository")) 00053 self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 00054 label = " %s " % _("List of extensions")) 00055 00056 self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY) 00057 self.fullDesc = wx.CheckBox(parent = self.panel, id = wx.ID_ANY, 00058 label = _("Fetch full info including description and keywords")) 00059 self.fullDesc.SetValue(True) 00060 00061 self.search = SearchModuleWindow(parent = self.panel) 00062 self.search.SetSelection(0) 00063 00064 self.tree = ExtensionTree(parent = self.panel, log = parent.GetLogWindow()) 00065 00066 self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 00067 label = " %s " % _("Options")) 00068 00069 task = gtask.parse_interface('g.extension.py') 00070 00071 ignoreFlags = ['l', 'c', 'g', 'a', 'f', 'quiet', 'verbose'] 00072 if sys.platform == 'win32': 00073 ignoreFlags.append('d') 00074 ignoreFlags.append('i') 00075 00076 for f in task.get_options()['flags']: 00077 name = f.get('name', '') 00078 desc = f.get('label', '') 00079 if not desc: 00080 desc = f.get('description', '') 00081 if not name and not desc: 00082 continue 00083 if name in ignoreFlags: 00084 continue 00085 self.options[name] = wx.CheckBox(parent = self.panel, id = wx.ID_ANY, 00086 label = desc) 00087 self.repo.SetValue(task.get_param(value = 'svnurl').get('default', 00088 'http://svn.osgeo.org/grass/grass-addons')) 00089 00090 self.statusbar = self.CreateStatusBar(number = 1) 00091 00092 self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY, 00093 label = _("&Fetch")) 00094 self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository")) 00095 self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE) 00096 self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY, 00097 label = _("&Install")) 00098 self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module")) 00099 self.btnInstall.Enable(False) 00100 self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY, 00101 label = _("Command dialog")) 00102 self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension.py') 00103 00104 self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow) 00105 self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch) 00106 self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall) 00107 self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog) 00108 self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated) 00109 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected) 00110 self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem) 00111 self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar) 00112 00113 self._layout() 00114 00115 def _layout(self): 00116 """!Do layout""" 00117 sizer = wx.BoxSizer(wx.VERTICAL) 00118 repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL) 00119 repo1Sizer = wx.BoxSizer(wx.HORIZONTAL) 00120 repo1Sizer.Add(item = self.repo, proportion = 1, 00121 flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1) 00122 repo1Sizer.Add(item = self.btnFetch, proportion = 0, 00123 flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1) 00124 repoSizer.Add(item = repo1Sizer, 00125 flag = wx.EXPAND) 00126 repoSizer.Add(item = self.fullDesc) 00127 00128 findSizer = wx.BoxSizer(wx.HORIZONTAL) 00129 findSizer.Add(item = self.search, proportion = 1) 00130 00131 treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL) 00132 treeSizer.Add(item = self.tree, proportion = 1, 00133 flag = wx.ALL | wx.EXPAND, border = 1) 00134 00135 # options 00136 optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL) 00137 for key in self.options.keys(): 00138 optionSizer.Add(item = self.options[key], proportion = 0) 00139 00140 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00141 btnSizer.Add(item = self.btnCmd, proportion = 0, 00142 flag = wx.RIGHT, border = 5) 00143 btnSizer.AddSpacer(10) 00144 btnSizer.Add(item = self.btnClose, proportion = 0, 00145 flag = wx.RIGHT, border = 5) 00146 btnSizer.Add(item = self.btnInstall, proportion = 0) 00147 00148 sizer.Add(item = repoSizer, proportion = 0, 00149 flag = wx.ALL | wx.EXPAND, border = 3) 00150 sizer.Add(item = findSizer, proportion = 0, 00151 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 00152 sizer.Add(item = treeSizer, proportion = 1, 00153 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 00154 sizer.Add(item = optionSizer, proportion = 0, 00155 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 00156 sizer.Add(item = btnSizer, proportion = 0, 00157 flag = wx.ALIGN_RIGHT | wx.ALL, border = 5) 00158 00159 self.panel.SetSizer(sizer) 00160 sizer.Fit(self.panel) 00161 00162 self.Layout() 00163 00164 def _getCmd(self): 00165 item = self.tree.GetSelected() 00166 if not item or not item.IsOk(): 00167 return ['g.extension.py'] 00168 00169 name = self.tree.GetItemText(item) 00170 if not name: 00171 GError(_("Extension not defined"), parent = self) 00172 return 00173 flags = list() 00174 for key in self.options.keys(): 00175 if self.options[key].IsChecked(): 00176 flags.append('-%s' % key) 00177 00178 return ['g.extension.py'] + flags + ['extension=' + name, 00179 'svnurl=' + self.repo.GetValue().strip()] 00180 00181 def OnUpdateStatusBar(self, event): 00182 """!Update statusbar text""" 00183 element = self.search.GetSelection() 00184 if not self.tree.IsLoaded(): 00185 self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0) 00186 return 00187 00188 self.tree.SearchItems(element = element, 00189 value = event.GetString()) 00190 00191 nItems = len(self.tree.itemsMarked) 00192 if event.GetString(): 00193 self.SetStatusText(_("%d items match") % nItems, 0) 00194 else: 00195 self.SetStatusText("", 0) 00196 00197 event.Skip() 00198 00199 def OnCloseWindow(self, event): 00200 """!Close window""" 00201 self.Destroy() 00202 00203 def OnFetch(self, event): 00204 """!Fetch list of available extensions""" 00205 wx.BeginBusyCursor() 00206 self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0) 00207 self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked()) 00208 self.SetStatusText("", 0) 00209 wx.EndBusyCursor() 00210 00211 def OnItemActivated(self, event): 00212 item = event.GetItem() 00213 data = self.tree.GetPyData(item) 00214 if data and 'command' in data: 00215 self.OnInstall(event = None) 00216 00217 def OnInstall(self, event): 00218 """!Install selected extension""" 00219 log = self.parent.GetLogWindow() 00220 log.RunCmd(self._getCmd(), onDone = self.OnDone) 00221 00222 def OnDone(self, cmd, returncode): 00223 item = self.tree.GetSelected() 00224 if not item or not item.IsOk() or \ 00225 returncode != 0 or \ 00226 not os.getenv('GRASS_ADDON_PATH'): 00227 return 00228 00229 name = self.tree.GetItemText(item) 00230 globalvar.grassCmd.add(name) 00231 00232 def OnItemSelected(self, event): 00233 """!Item selected""" 00234 item = event.GetItem() 00235 self.tree.itemSelected = item 00236 data = self.tree.GetPyData(item) 00237 if data is None: 00238 self.SetStatusText('', 0) 00239 self.btnInstall.Enable(False) 00240 else: 00241 self.SetStatusText(data.get('description', ''), 0) 00242 self.btnInstall.Enable(True) 00243 00244 def OnShowItem(self, event): 00245 """!Show selected item""" 00246 self.tree.OnShowItem(event) 00247 if self.tree.GetSelected(): 00248 self.btnInstall.Enable() 00249 else: 00250 self.btnInstall.Enable(False) 00251 00252 def OnCmdDialog(self, event): 00253 """!Shows command dialog""" 00254 GUI(parent = self).ParseCommand(cmd = self._getCmd()) 00255 00256 class ExtensionTree(ItemTree): 00257 """!List of available extensions""" 00258 def __init__(self, parent, log, id = wx.ID_ANY, 00259 ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS | 00260 CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, 00261 **kwargs): 00262 self.parent = parent # GMFrame 00263 self.log = log 00264 00265 super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs) 00266 00267 self._initTree() 00268 00269 def _initTree(self): 00270 for prefix in ('display', 'database', 00271 'general', 'imagery', 00272 'misc', 'postscript', 'paint', 00273 'raster', 'raster3d', 'sites', 'vector', 'wxGUI', 'other'): 00274 self.AppendItem(parentId = self.root, 00275 text = prefix) 00276 self._loaded = False 00277 00278 def _expandPrefix(self, c): 00279 name = { 'd' : 'display', 00280 'db' : 'database', 00281 'g' : 'general', 00282 'i' : 'imagery', 00283 'm' : 'misc', 00284 'ps' : 'postscript', 00285 'p' : 'paint', 00286 'r' : 'raster', 00287 'r3' : 'raster3d', 00288 's' : 'sites', 00289 'v' : 'vector', 00290 'wx' : 'wxGUI', 00291 '' : 'other' } 00292 00293 if c in name: 00294 return name[c] 00295 00296 return c 00297 00298 def _findItem(self, text): 00299 """!Find item""" 00300 item = self.GetFirstChild(self.root)[0] 00301 while item and item.IsOk(): 00302 if text == self.GetItemText(item): 00303 return item 00304 00305 item = self.GetNextSibling(item) 00306 00307 return None 00308 00309 def Load(self, url, full = False): 00310 """!Load list of extensions""" 00311 self.DeleteAllItems() 00312 self.root = self.AddRoot(_("Menu tree")) 00313 self._initTree() 00314 00315 if full: 00316 flags = 'g' 00317 else: 00318 flags = 'l' 00319 ret = RunCommand('g.extension.py', read = True, parent = self, 00320 svnurl = url, 00321 flags = flags, quiet = True) 00322 if not ret: 00323 return 00324 00325 mdict = dict() 00326 for line in ret.splitlines(): 00327 if full: 00328 try: 00329 key, value = line.split('=', 1) 00330 except ValueError: 00331 key = 'name' 00332 value = line 00333 00334 if key == 'name': 00335 try: 00336 prefix, name = value.split('.', 1) 00337 except ValueError: 00338 prefix = '' 00339 name = value 00340 if prefix not in mdict: 00341 mdict[prefix] = dict() 00342 mdict[prefix][name] = dict() 00343 else: 00344 mdict[prefix][name][key] = value 00345 else: 00346 try: 00347 prefix, name = line.strip().split('.', 1) 00348 except: 00349 prefix = '' 00350 name = line.strip() 00351 00352 if self._expandPrefix(prefix) == prefix: 00353 prefix = '' 00354 00355 if prefix not in mdict: 00356 mdict[prefix] = dict() 00357 00358 mdict[prefix][name] = { 'command' : prefix + '.' + name } 00359 00360 for prefix in mdict.keys(): 00361 prefixName = self._expandPrefix(prefix) 00362 item = self._findItem(prefixName) 00363 names = mdict[prefix].keys() 00364 names.sort() 00365 for name in names: 00366 if prefix: 00367 text = prefix + '.' + name 00368 else: 00369 text = name 00370 new = self.AppendItem(parentId = item, 00371 text = text) 00372 data = dict() 00373 for key in mdict[prefix][name].keys(): 00374 data[key] = mdict[prefix][name][key] 00375 00376 self.SetPyData(new, data) 00377 00378 self._loaded = True 00379 00380 def IsLoaded(self): 00381 """Check if items are loaded""" 00382 return self._loaded 00383 00384 class UninstallExtensionWindow(wx.Frame): 00385 def __init__(self, parent, id = wx.ID_ANY, 00386 title = _("Uninstall GRASS Addons extensions"), **kwargs): 00387 self.parent = parent 00388 00389 wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs) 00390 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 00391 00392 self.panel = wx.Panel(parent = self, id = wx.ID_ANY) 00393 00394 self.extBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 00395 label = " %s " % _("List of installed extensions")) 00396 00397 self.extList = CheckListExtension(parent = self.panel) 00398 00399 # buttons 00400 self.btnUninstall = wx.Button(parent = self.panel, id = wx.ID_ANY, 00401 label = _("&Uninstall")) 00402 self.btnUninstall.SetToolTipString(_("Uninstall selected AddOns extensions")) 00403 self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY, 00404 label = _("Command dialog")) 00405 self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension') 00406 self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE) 00407 00408 self.btnUninstall.Bind(wx.EVT_BUTTON, self.OnUninstall) 00409 self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog) 00410 self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow) 00411 00412 self._layout() 00413 00414 def _layout(self): 00415 """!Do layout""" 00416 sizer = wx.BoxSizer(wx.VERTICAL) 00417 00418 extSizer = wx.StaticBoxSizer(self.extBox, wx.HORIZONTAL) 00419 extSizer.Add(item = self.extList, proportion = 1, 00420 flag = wx.ALL | wx.EXPAND, border = 1) 00421 00422 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00423 btnSizer.Add(item = self.btnCmd, proportion = 0, 00424 flag = wx.RIGHT, border = 5) 00425 btnSizer.AddSpacer(10) 00426 btnSizer.Add(item = self.btnClose, proportion = 0, 00427 flag = wx.RIGHT, border = 5) 00428 btnSizer.Add(item = self.btnUninstall, proportion = 0) 00429 00430 sizer.Add(item = extSizer, proportion = 1, 00431 flag = wx.ALL | wx.EXPAND, border = 3) 00432 sizer.Add(item = btnSizer, proportion = 0, 00433 flag = wx.ALIGN_RIGHT | wx.ALL, border = 5) 00434 00435 self.panel.SetSizer(sizer) 00436 sizer.Fit(self.panel) 00437 00438 self.Layout() 00439 00440 def OnCloseWindow(self, event): 00441 """!Close window""" 00442 self.Destroy() 00443 00444 def OnUninstall(self, event): 00445 """!Uninstall selected extensions""" 00446 log = self.parent.GetLogWindow() 00447 eList = self.extList.GetExtensions() 00448 if not eList: 00449 GError(_("No extension selected for removal. " 00450 "Operation canceled."), 00451 parent = self) 00452 return 00453 00454 for ext in eList: 00455 files = RunCommand('g.extension.py', parent = self, read = True, quiet = True, 00456 extension = ext, operation = 'remove').splitlines() 00457 dlg = wx.MessageDialog(parent = self, 00458 message = _("List of files to be removed:\n%(files)s\n\n" 00459 "Do you want really to remove <%(ext)s> extension?") % \ 00460 { 'files' : os.linesep.join(files), 'ext' : ext }, 00461 caption = _("Remove extension"), 00462 style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) 00463 00464 if dlg.ShowModal() == wx.ID_YES: 00465 RunCommand('g.extension.py', flags = 'f', parent = self, quiet = True, 00466 extension = ext, operation = 'remove') 00467 00468 self.extList.LoadData() 00469 00470 def OnCmdDialog(self, event): 00471 """!Shows command dialog""" 00472 GUI(parent = self).ParseCommand(cmd = ['g.extension.py']) 00473 00474 class CheckListExtension(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin): 00475 """!List of mapset/owner/group""" 00476 def __init__(self, parent): 00477 self.parent = parent 00478 00479 wx.ListCtrl.__init__(self, parent, id = wx.ID_ANY, 00480 style = wx.LC_REPORT) 00481 listmix.CheckListCtrlMixin.__init__(self) 00482 00483 # setup mixins 00484 listmix.ListCtrlAutoWidthMixin.__init__(self) 00485 00486 self.InsertColumn(0, _('Extension')) 00487 self.LoadData() 00488 00489 def LoadData(self): 00490 """!Load data into list""" 00491 self.DeleteAllItems() 00492 for ext in RunCommand('g.extension.py', 00493 quiet = True, parent = self, read = True, 00494 flags = 'a').splitlines(): 00495 if ext: 00496 self.InsertStringItem(sys.maxint, ext) 00497 00498 def GetExtensions(self): 00499 """!Get extensions to be un-installed 00500 """ 00501 extList = list() 00502 for i in range(self.GetItemCount()): 00503 if self.IsChecked(i): 00504 name = self.GetItemText(i) 00505 if name: 00506 extList.append(name) 00507 00508 return extList