GRASS Programmer's Manual
6.4.2(2012)
|
00001 """! 00002 @package help.py 00003 00004 @brief Help window 00005 00006 Classes: 00007 - SearchModuleWindow 00008 - ItemTree 00009 - MenuTreeWindow 00010 - MenuTree 00011 - AboutWindow 00012 - InstallExtensionWindow 00013 - ExtensionTree 00014 - UninstallExtensionWindow 00015 - CheckListExtension 00016 - HelpFrame 00017 - HelpWindow 00018 - HelpPanel 00019 00020 (C) 2008-2011 by the GRASS Development Team 00021 This program is free software under the GNU General Public License 00022 (>=v2). Read the file COPYING that comes with GRASS for details. 00023 00024 @author Martin Landa <landa.martin gmail.com> 00025 """ 00026 00027 import os 00028 import codecs 00029 import sys 00030 00031 import wx 00032 import wx.lib.mixins.listctrl as listmix 00033 try: 00034 import wx.lib.agw.customtreectrl as CT 00035 # import wx.lib.agw.hyperlink as hl 00036 except ImportError: 00037 import wx.lib.customtreectrl as CT 00038 # import wx.lib.hyperlink as hl 00039 import wx.lib.flatnotebook as FN 00040 import wx.lib.scrolledpanel as scrolled 00041 00042 from grass.script import core as grass 00043 from grass.script import task as gtask 00044 00045 import menudata 00046 import gcmd 00047 import globalvar 00048 import gdialogs 00049 import utils 00050 import menuform 00051 00052 class HelpFrame(wx.Frame): 00053 """!GRASS Quickstart help window""" 00054 def __init__(self, parent, id, title, size, file): 00055 wx.Frame.__init__(self, parent = parent, id = id, title = title, size = size) 00056 00057 sizer = wx.BoxSizer(wx.VERTICAL) 00058 00059 # text 00060 content = HelpPanel(parent = self) 00061 content.LoadPage(file) 00062 00063 sizer.Add(item = content, proportion = 1, flag = wx.EXPAND) 00064 00065 self.SetAutoLayout(True) 00066 self.SetSizer(sizer) 00067 self.Layout() 00068 00069 class SearchModuleWindow(wx.Panel): 00070 """!Search module window (used in MenuTreeWindow)""" 00071 def __init__(self, parent, id = wx.ID_ANY, cmdPrompt = None, 00072 showChoice = True, showTip = False, **kwargs): 00073 self.showTip = showTip 00074 self.showChoice = showChoice 00075 self.cmdPrompt = cmdPrompt 00076 00077 wx.Panel.__init__(self, parent = parent, id = id, **kwargs) 00078 00079 self._searchDict = { _('description') : 'description', 00080 _('command') : 'command', 00081 _('keywords') : 'keywords' } 00082 00083 self.box = wx.StaticBox(parent = self, id = wx.ID_ANY, 00084 label = " %s " % _("Find module(s)")) 00085 00086 self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY, 00087 choices = [_('description'), 00088 _('keywords'), 00089 _('command')]) 00090 self.searchBy.SetSelection(0) 00091 00092 self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY, 00093 value = "", size = (-1, 25), 00094 style = wx.TE_PROCESS_ENTER) 00095 self.search.Bind(wx.EVT_TEXT, self.OnSearchModule) 00096 00097 if self.showTip: 00098 self.searchTip = gdialogs.StaticWrapText(parent = self, id = wx.ID_ANY, 00099 size = (-1, 35)) 00100 00101 if self.showChoice: 00102 self.searchChoice = wx.Choice(parent = self, id = wx.ID_ANY) 00103 if self.cmdPrompt: 00104 self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems()) 00105 self.searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule) 00106 00107 self._layout() 00108 00109 def _layout(self): 00110 """!Do layout""" 00111 sizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL) 00112 gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3) 00113 gridSizer.AddGrowableCol(1) 00114 00115 gridSizer.Add(item = self.searchBy, 00116 flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0)) 00117 gridSizer.Add(item = self.search, 00118 flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (0, 1)) 00119 row = 1 00120 if self.showTip: 00121 gridSizer.Add(item = self.searchTip, 00122 flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2)) 00123 row += 1 00124 00125 if self.showChoice: 00126 gridSizer.Add(item = self.searchChoice, 00127 flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2)) 00128 00129 sizer.Add(item = gridSizer, proportion = 1) 00130 00131 self.SetSizer(sizer) 00132 sizer.Fit(self) 00133 00134 def GetSelection(self): 00135 """!Get selected element""" 00136 selection = self.searchBy.GetStringSelection() 00137 00138 return self._searchDict[selection] 00139 00140 def SetSelection(self, i): 00141 """!Set selection element""" 00142 self.searchBy.SetSelection(i) 00143 00144 def OnSearchModule(self, event): 00145 """!Search module by keywords or description""" 00146 if not self.cmdPrompt: 00147 event.Skip() 00148 return 00149 00150 text = event.GetString() 00151 if not text: 00152 self.cmdPrompt.SetFilter(None) 00153 mList = self.cmdPrompt.GetCommandItems() 00154 self.searchChoice.SetItems(mList) 00155 if self.showTip: 00156 self.searchTip.SetLabel(_("%d modules found") % len(mList)) 00157 event.Skip() 00158 return 00159 00160 modules = dict() 00161 iFound = 0 00162 for module, data in self.cmdPrompt.moduleDesc.iteritems(): 00163 found = False 00164 sel = self.searchBy.GetSelection() 00165 if sel == 0: # -> description 00166 if text in data['desc']: 00167 found = True 00168 elif sel == 1: # keywords 00169 if text in ','.join(data['keywords']): 00170 found = True 00171 else: # command 00172 if module[:len(text)] == text: 00173 found = True 00174 00175 if found: 00176 iFound += 1 00177 try: 00178 group, name = module.split('.') 00179 except ValueError: 00180 continue # TODO 00181 00182 if group not in modules: 00183 modules[group] = list() 00184 modules[group].append(name) 00185 00186 self.cmdPrompt.SetFilter(modules) 00187 self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems()) 00188 if self.showTip: 00189 self.searchTip.SetLabel(_("%d modules found") % iFound) 00190 00191 event.Skip() 00192 00193 def OnSelectModule(self, event): 00194 """!Module selected from choice, update command prompt""" 00195 cmd = event.GetString().split(' ', 1)[0] 00196 text = cmd + ' ' 00197 pos = len(text) 00198 00199 if self.cmdPrompt: 00200 self.cmdPrompt.SetText(text) 00201 self.cmdPrompt.SetSelectionStart(pos) 00202 self.cmdPrompt.SetCurrentPos(pos) 00203 self.cmdPrompt.SetFocus() 00204 00205 desc = self.cmdPrompt.GetCommandDesc(cmd) 00206 if self.showTip: 00207 self.searchTip.SetLabel(desc) 00208 00209 def Reset(self): 00210 """!Reset widget""" 00211 self.searchBy.SetSelection(0) 00212 self.search.SetValue('') 00213 if self.showTip: 00214 self.searchTip.SetLabel('') 00215 00216 class MenuTreeWindow(wx.Panel): 00217 """!Show menu tree""" 00218 def __init__(self, parent, id = wx.ID_ANY, **kwargs): 00219 self.parent = parent # LayerManager 00220 00221 wx.Panel.__init__(self, parent = parent, id = id, **kwargs) 00222 00223 self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY, 00224 label = " %s " % _("Menu tree (double-click to run command)")) 00225 # tree 00226 self.tree = MenuTree(parent = self, data = menudata.ManagerData()) 00227 self.tree.Load() 00228 00229 # search widget 00230 self.search = SearchModuleWindow(parent = self, showChoice = False) 00231 00232 # buttons 00233 self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("&Run")) 00234 self.btnRun.SetToolTipString(_("Run selected command")) 00235 self.btnRun.Enable(False) 00236 00237 # bindings 00238 self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun) 00239 self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated) 00240 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected) 00241 self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem) 00242 self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar) 00243 00244 self._layout() 00245 00246 self.search.SetFocus() 00247 00248 def _layout(self): 00249 """!Do dialog layout""" 00250 sizer = wx.BoxSizer(wx.VERTICAL) 00251 00252 # body 00253 dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL) 00254 dataSizer.Add(item = self.tree, proportion =1, 00255 flag = wx.EXPAND) 00256 00257 # buttons 00258 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00259 btnSizer.Add(item = self.btnRun, proportion = 0) 00260 00261 sizer.Add(item = dataSizer, proportion = 1, 00262 flag = wx.EXPAND | wx.ALL, border = 5) 00263 00264 sizer.Add(item = self.search, proportion = 0, 00265 flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5) 00266 00267 sizer.Add(item = btnSizer, proportion = 0, 00268 flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5) 00269 00270 sizer.Fit(self) 00271 sizer.SetSizeHints(self) 00272 00273 self.SetSizer(sizer) 00274 00275 self.Fit() 00276 self.SetAutoLayout(True) 00277 self.Layout() 00278 00279 def OnCloseWindow(self, event): 00280 """!Close window""" 00281 self.Destroy() 00282 00283 def OnRun(self, event): 00284 """!Run selected command""" 00285 if not self.tree.GetSelected(): 00286 return # should not happen 00287 00288 data = self.tree.GetPyData(self.tree.GetSelected()) 00289 if not data: 00290 return 00291 00292 handler = 'self.parent.' + data['handler'].lstrip('self.') 00293 if data['handler'] == 'self.OnXTerm': 00294 wx.MessageBox(parent = self, 00295 message = _('You must run this command from the menu or command line', 00296 'This command require an XTerm'), 00297 caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE) 00298 elif data['command']: 00299 eval(handler)(event = None, cmd = data['command'].split()) 00300 else: 00301 eval(handler)(None) 00302 00303 def OnShowItem(self, event): 00304 """!Show selected item""" 00305 self.tree.OnShowItem(event) 00306 if self.tree.GetSelected(): 00307 self.btnRun.Enable() 00308 else: 00309 self.btnRun.Enable(False) 00310 00311 def OnItemActivated(self, event): 00312 """!Item activated (double-click)""" 00313 item = event.GetItem() 00314 if not item or not item.IsOk(): 00315 return 00316 00317 data = self.tree.GetPyData(item) 00318 if not data or 'command' not in data: 00319 return 00320 00321 self.tree.itemSelected = item 00322 00323 self.OnRun(None) 00324 00325 def OnItemSelected(self, event): 00326 """!Item selected""" 00327 item = event.GetItem() 00328 if not item or not item.IsOk(): 00329 return 00330 00331 data = self.tree.GetPyData(item) 00332 if not data or 'command' not in data: 00333 return 00334 00335 if data['command']: 00336 label = data['command'] + ' -- ' + data['description'] 00337 else: 00338 label = data['description'] 00339 00340 self.parent.SetStatusText(label, 0) 00341 00342 def OnUpdateStatusBar(self, event): 00343 """!Update statusbar text""" 00344 element = self.search.GetSelection() 00345 self.tree.SearchItems(element = element, 00346 value = event.GetString()) 00347 00348 nItems = len(self.tree.itemsMarked) 00349 if event.GetString(): 00350 self.parent.SetStatusText(_("%d modules match") % nItems, 0) 00351 else: 00352 self.parent.SetStatusText("", 0) 00353 00354 event.Skip() 00355 00356 class ItemTree(CT.CustomTreeCtrl): 00357 def __init__(self, parent, id = wx.ID_ANY, 00358 ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS | 00359 CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs): 00360 if globalvar.hasAgw: 00361 super(ItemTree, self).__init__(parent, id, agwStyle = ctstyle, **kwargs) 00362 else: 00363 super(ItemTree, self).__init__(parent, id, style = ctstyle, **kwargs) 00364 00365 self.root = self.AddRoot(_("Menu tree")) 00366 self.itemsMarked = [] # list of marked items 00367 self.itemSelected = None 00368 00369 def SearchItems(self, element, value): 00370 """!Search item 00371 00372 @param element element index (see self.searchBy) 00373 @param value 00374 00375 @return list of found tree items 00376 """ 00377 items = list() 00378 if not value: 00379 return items 00380 00381 item = self.GetFirstChild(self.root)[0] 00382 self._processItem(item, element, value, items) 00383 00384 self.itemsMarked = items 00385 self.itemSelected = None 00386 00387 return items 00388 00389 def _processItem(self, item, element, value, listOfItems): 00390 """!Search items (used by SearchItems) 00391 00392 @param item reference item 00393 @param listOfItems list of found items 00394 """ 00395 while item and item.IsOk(): 00396 subItem = self.GetFirstChild(item)[0] 00397 if subItem: 00398 self._processItem(subItem, element, value, listOfItems) 00399 data = self.GetPyData(item) 00400 00401 if data and element in data and \ 00402 value.lower() in data[element].lower(): 00403 listOfItems.append(item) 00404 00405 item = self.GetNextSibling(item) 00406 00407 def GetSelected(self): 00408 """!Get selected item""" 00409 return self.itemSelected 00410 00411 def OnShowItem(self, event): 00412 """!Highlight first found item in menu tree""" 00413 if len(self.itemsMarked) > 0: 00414 if self.GetSelected(): 00415 self.ToggleItemSelection(self.GetSelected()) 00416 idx = self.itemsMarked.index(self.GetSelected()) + 1 00417 else: 00418 idx = 0 00419 try: 00420 self.ToggleItemSelection(self.itemsMarked[idx]) 00421 self.itemSelected = self.itemsMarked[idx] 00422 self.EnsureVisible(self.itemsMarked[idx]) 00423 except IndexError: 00424 self.ToggleItemSelection(self.itemsMarked[0]) # reselect first item 00425 self.EnsureVisible(self.itemsMarked[0]) 00426 self.itemSelected = self.itemsMarked[0] 00427 else: 00428 for item in self.root.GetChildren(): 00429 self.Collapse(item) 00430 itemSelected = self.GetSelection() 00431 if itemSelected: 00432 self.ToggleItemSelection(itemSelected) 00433 self.itemSelected = None 00434 00435 class MenuTree(ItemTree): 00436 """!Menu tree class""" 00437 def __init__(self, parent, data, **kwargs): 00438 self.parent = parent 00439 self.menudata = data 00440 00441 super(MenuTree, self).__init__(parent, **kwargs) 00442 00443 def Load(self, data = None): 00444 """!Load menu data tree 00445 00446 @param data menu data (None to use self.menudata) 00447 """ 00448 if not data: 00449 data = self.menudata 00450 00451 self.itemsMarked = [] # list of marked items 00452 for eachMenuData in data.GetMenu(): 00453 for label, items in eachMenuData: 00454 item = self.AppendItem(parentId = self.root, 00455 text = label.replace('&', '')) 00456 self.__AppendItems(item, items) 00457 00458 def __AppendItems(self, item, data): 00459 """!Append items into tree (used by Load() 00460 00461 @param item tree item (parent) 00462 @parent data menu data""" 00463 for eachItem in data: 00464 if len(eachItem) == 2: 00465 if eachItem[0]: 00466 itemSub = self.AppendItem(parentId = item, 00467 text = eachItem[0]) 00468 self.__AppendItems(itemSub, eachItem[1]) 00469 else: 00470 if eachItem[0]: 00471 itemNew = self.AppendItem(parentId = item, 00472 text = eachItem[0]) 00473 00474 data = { 'item' : eachItem[0], 00475 'description' : eachItem[1], 00476 'handler' : eachItem[2], 00477 'command' : eachItem[3], 00478 'keywords' : eachItem[4] } 00479 00480 self.SetPyData(itemNew, data) 00481 00482 class AboutWindow(wx.Frame): 00483 """!Create custom About Window 00484 00485 @todo improve styling 00486 """ 00487 def __init__(self, parent, size = (750, 400), 00488 title = _('About GRASS GIS'), **kwargs): 00489 wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, size = size, **kwargs) 00490 00491 panel = wx.Panel(parent = self, id = wx.ID_ANY) 00492 00493 # icon 00494 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 00495 00496 # get version and web site 00497 vInfo = grass.version() 00498 00499 infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY) 00500 infoSizer = wx.BoxSizer(wx.VERTICAL) 00501 infoGridSizer = wx.GridBagSizer(vgap = 5, hgap = 5) 00502 infoGridSizer.AddGrowableCol(0) 00503 infoGridSizer.AddGrowableCol(1) 00504 logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico") 00505 logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY, 00506 bitmap = wx.Bitmap(name = logo, 00507 type = wx.BITMAP_TYPE_ICO)) 00508 infoSizer.Add(item = logoBitmap, proportion = 0, 00509 flag = wx.ALL | wx.ALIGN_CENTER, border = 25) 00510 00511 info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00512 label = 'GRASS GIS ' + vInfo['version'] + '\n\n') 00513 info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) 00514 infoSizer.Add(item = info, proportion = 0, 00515 flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15) 00516 00517 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00518 label = _('Official GRASS site:')), 00519 pos = (0, 0), 00520 flag = wx.ALIGN_RIGHT) 00521 00522 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00523 label = 'http://grass.osgeo.org'), 00524 pos = (0, 1), 00525 flag = wx.ALIGN_LEFT) 00526 00527 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00528 label = _('SVN Revision:')), 00529 pos = (2, 0), 00530 flag = wx.ALIGN_RIGHT) 00531 00532 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00533 label = vInfo['revision']), 00534 pos = (2, 1), 00535 flag = wx.ALIGN_LEFT) 00536 00537 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00538 label = _('GIS Library Revision:')), 00539 pos = (3, 0), 00540 flag = wx.ALIGN_RIGHT) 00541 00542 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00543 label = vInfo['libgis_revision'] + ' (' + 00544 vInfo['libgis_date'].split(' ')[0] + ')'), 00545 pos = (3, 1), 00546 flag = wx.ALIGN_LEFT) 00547 00548 infoSizer.Add(item = infoGridSizer, 00549 proportion = 1, 00550 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, 00551 border = 25) 00552 00553 # create a flat notebook for displaying information about GRASS 00554 aboutNotebook = menuform.GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON) 00555 aboutNotebook.SetTabAreaColour(globalvar.FNPageColor) 00556 00557 for title, win in ((_("Info"), infoTxt), 00558 (_("Copyright"), self._pageCopyright()), 00559 (_("License"), self._pageLicense()), 00560 (_("Authors"), self._pageCredit()), 00561 (_("Contributors"), self._pageContributors()), 00562 (_("Extra contributors"), self._pageContributors(extra = True)), 00563 (_("Translators"), self._pageTranslators())): 00564 aboutNotebook.AddPage(page = win, text = title) 00565 wx.CallAfter(aboutNotebook.SetSelection, 0) 00566 00567 # buttons 00568 btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE) 00569 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00570 btnSizer.Add(item = btnClose, proportion = 0, 00571 flag = wx.ALL | wx.ALIGN_RIGHT, 00572 border = 5) 00573 # bindings 00574 btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow) 00575 00576 infoTxt.SetSizer(infoSizer) 00577 infoSizer.Fit(infoTxt) 00578 00579 sizer = wx.BoxSizer(wx.VERTICAL) 00580 sizer.Add(item = aboutNotebook, proportion = 1, 00581 flag = wx.EXPAND | wx.ALL, border = 1) 00582 sizer.Add(item = btnSizer, proportion = 0, 00583 flag = wx.ALL | wx.ALIGN_RIGHT, border = 1) 00584 panel.SetSizer(sizer) 00585 self.Layout() 00586 00587 def _pageCopyright(self): 00588 """Copyright information""" 00589 copyfile = os.path.join(os.getenv("GISBASE"), "COPYING") 00590 if os.path.exists(copyfile): 00591 copyrightFile = open(copyfile, 'r') 00592 copytext = copyrightFile.read() 00593 copyrightFile.close() 00594 else: 00595 copytext = _('%s file missing') % 'COPYING' 00596 00597 # put text into a scrolling panel 00598 copyrightwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 00599 size = wx.DefaultSize, 00600 style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER) 00601 copyrighttxt = wx.StaticText(copyrightwin, id = wx.ID_ANY, label = copytext) 00602 copyrightwin.SetAutoLayout(True) 00603 copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL) 00604 copyrightwin.sizer.Add(item = copyrighttxt, proportion = 1, 00605 flag = wx.EXPAND | wx.ALL, border = 3) 00606 copyrightwin.SetSizer(copyrightwin.sizer) 00607 copyrightwin.Layout() 00608 copyrightwin.SetupScrolling() 00609 00610 return copyrightwin 00611 00612 def _pageLicense(self): 00613 """Licence about""" 00614 licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT") 00615 if os.path.exists(licfile): 00616 licenceFile = open(licfile, 'r') 00617 license = ''.join(licenceFile.readlines()) 00618 licenceFile.close() 00619 else: 00620 license = _('%s file missing') % 'GPL.TXT' 00621 # put text into a scrolling panel 00622 licensewin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 00623 style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER) 00624 licensetxt = wx.StaticText(licensewin, id = wx.ID_ANY, label = license) 00625 licensewin.SetAutoLayout(True) 00626 licensewin.sizer = wx.BoxSizer(wx.VERTICAL) 00627 licensewin.sizer.Add(item = licensetxt, proportion = 1, 00628 flag = wx.EXPAND | wx.ALL, border = 3) 00629 licensewin.SetSizer(licensewin.sizer) 00630 licensewin.Layout() 00631 licensewin.SetupScrolling() 00632 00633 return licensewin 00634 00635 def _pageCredit(self): 00636 """Credit about""" 00637 # credits 00638 authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS") 00639 if os.path.exists(authfile): 00640 authorsFile = open(authfile, 'r') 00641 authors = unicode(''.join(authorsFile.readlines()), "utf-8") 00642 authorsFile.close() 00643 else: 00644 authors = _('%s file missing') % 'AUTHORS' 00645 authorwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 00646 style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) 00647 authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors) 00648 authorwin.SetAutoLayout(1) 00649 authorwin.SetupScrolling() 00650 authorwin.sizer = wx.BoxSizer(wx.VERTICAL) 00651 authorwin.sizer.Add(item = authortxt, proportion = 1, 00652 flag = wx.EXPAND | wx.ALL, border = 3) 00653 authorwin.SetSizer(authorwin.sizer) 00654 authorwin.Layout() 00655 00656 return authorwin 00657 00658 def _pageContributors(self, extra = False): 00659 """Contributors info""" 00660 if extra: 00661 contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv") 00662 else: 00663 contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv") 00664 if os.path.exists(contribfile): 00665 contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r') 00666 contribs = list() 00667 errLines = list() 00668 for line in contribFile.readlines()[1:]: 00669 line = line.rstrip('\n') 00670 try: 00671 if extra: 00672 name, email, rfc2_agreed = line.split(',') 00673 else: 00674 cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',') 00675 except ValueError: 00676 errLines.append(line) 00677 continue 00678 if extra: 00679 contribs.append((name, email)) 00680 else: 00681 contribs.append((name, email, country, osgeo_id)) 00682 00683 contribFile.close() 00684 00685 if errLines: 00686 gcmd.GError(parent = self, 00687 message = _("Error when reading file '%s'.") % contribfile + \ 00688 "\n\n" + _("Lines:") + " %s" % \ 00689 os.linesep.join(map(utils.UnicodeString, errLines))) 00690 else: 00691 contribs = None 00692 00693 contribwin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 00694 style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER) 00695 contribwin.SetAutoLayout(True) 00696 contribwin.SetupScrolling() 00697 contribwin.sizer = wx.BoxSizer(wx.VERTICAL) 00698 00699 if not contribs: 00700 contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY, 00701 label = _('%s file missing') % contribfile) 00702 contribwin.sizer.Add(item = contribtxt, proportion = 1, 00703 flag = wx.EXPAND | wx.ALL, border = 3) 00704 else: 00705 if extra: 00706 items = (_('Name'), _('E-mail')) 00707 else: 00708 items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID')) 00709 contribBox = wx.FlexGridSizer(cols = len(items), vgap = 5, hgap = 5) 00710 for item in items: 00711 contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY, 00712 label = item)) 00713 for vals in contribs: 00714 for item in vals: 00715 contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY, 00716 label = item)) 00717 contribwin.sizer.Add(item = contribBox, proportion = 1, 00718 flag = wx.EXPAND | wx.ALL, border = 3) 00719 00720 contribwin.SetSizer(contribwin.sizer) 00721 contribwin.Layout() 00722 00723 return contribwin 00724 00725 def _pageTranslators(self): 00726 """Translators info""" 00727 translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv") 00728 if os.path.exists(translatorsfile): 00729 translatorsFile = open(translatorsfile, 'r') 00730 translators = dict() 00731 errLines = list() 00732 for line in translatorsFile.readlines()[1:]: 00733 line = line.rstrip('\n') 00734 try: 00735 name, email, languages = line.split(',') 00736 except ValueError: 00737 errLines.append(line) 00738 continue 00739 for language in languages.split(' '): 00740 if language not in translators: 00741 translators[language] = list() 00742 translators[language].append((name, email)) 00743 translatorsFile.close() 00744 00745 if errLines: 00746 gcmd.GError(parent = self, 00747 message = _("Error when reading file '%s'.") % translatorsfile + \ 00748 "\n\n" + _("Lines:") + " %s" % \ 00749 os.linesep.join(map(utils.UnicodeString, errLines))) 00750 else: 00751 translators = None 00752 00753 translatorswin = scrolled.ScrolledPanel(self, id = wx.ID_ANY, 00754 style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) 00755 translatorswin.SetAutoLayout(1) 00756 translatorswin.SetupScrolling() 00757 translatorswin.sizer = wx.BoxSizer(wx.VERTICAL) 00758 00759 if not translators: 00760 translatorstxt = wx.StaticText(translatorswin, id = wx.ID_ANY, 00761 label = _('%s file missing') % 'translators.csv') 00762 translatorswin.sizer.Add(item = translatorstxt, proportion = 1, 00763 flag = wx.EXPAND | wx.ALL, border = 3) 00764 else: 00765 translatorsBox = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5) 00766 languages = translators.keys() 00767 languages.sort() 00768 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00769 label = _('Name'))) 00770 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00771 label = _('E-mail'))) 00772 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00773 label = _('Language'))) 00774 for lang in languages: 00775 for translator in translators[lang]: 00776 name, email = translator 00777 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00778 label = unicode(name, "utf-8"))) 00779 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00780 label = email)) 00781 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00782 label = lang)) 00783 00784 translatorswin.sizer.Add(item = translatorsBox, proportion = 1, 00785 flag = wx.EXPAND | wx.ALL, border = 3) 00786 00787 translatorswin.SetSizer(translatorswin.sizer) 00788 translatorswin.Layout() 00789 00790 return translatorswin 00791 00792 def OnCloseWindow(self, event): 00793 """!Close window""" 00794 self.Close() 00795 00796 class InstallExtensionWindow(wx.Frame): 00797 def __init__(self, parent, id = wx.ID_ANY, 00798 title = _("Fetch & install extension from GRASS Addons"), **kwargs): 00799 self.parent = parent 00800 self.options = dict() # list of options 00801 00802 wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs) 00803 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 00804 00805 self.panel = wx.Panel(parent = self, id = wx.ID_ANY) 00806 00807 self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 00808 label = " %s " % _("Repository")) 00809 self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 00810 label = " %s " % _("List of extensions")) 00811 00812 self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY) 00813 self.fullDesc = wx.CheckBox(parent = self.panel, id = wx.ID_ANY, 00814 label = _("Fetch full info including description and keywords")) 00815 self.fullDesc.SetValue(True) 00816 00817 self.search = SearchModuleWindow(parent = self.panel) 00818 self.search.SetSelection(0) 00819 00820 self.tree = ExtensionTree(parent = self.panel, log = parent.GetLogWindow()) 00821 00822 self.optionBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 00823 label = " %s " % _("Options")) 00824 00825 task = gtask.parse_interface('g.extension.py') 00826 00827 ignoreFlags = ['l', 'c', 'g', 'a', 'f', 'quiet', 'verbose'] 00828 if sys.platform == 'win32': 00829 ignoreFlags.append('d') 00830 ignoreFlags.append('i') 00831 00832 for f in task.get_options()['flags']: 00833 name = f.get('name', '') 00834 desc = f.get('label', '') 00835 if not desc: 00836 desc = f.get('description', '') 00837 if not name and not desc: 00838 continue 00839 if name in ignoreFlags: 00840 continue 00841 self.options[name] = wx.CheckBox(parent = self.panel, id = wx.ID_ANY, 00842 label = desc) 00843 self.repo.SetValue(task.get_param(value = 'svnurl').get('default', 00844 'http://svn.osgeo.org/grass/grass-addons')) 00845 00846 self.statusbar = self.CreateStatusBar(number = 1) 00847 00848 self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY, 00849 label = _("&Fetch")) 00850 self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository")) 00851 self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE) 00852 self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY, 00853 label = _("&Install")) 00854 self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module")) 00855 self.btnInstall.Enable(False) 00856 self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY, 00857 label = _("Command dialog")) 00858 self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension.py') 00859 00860 self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow) 00861 self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch) 00862 self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall) 00863 self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog) 00864 self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated) 00865 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected) 00866 self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem) 00867 self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar) 00868 00869 self._layout() 00870 00871 def _layout(self): 00872 """!Do layout""" 00873 sizer = wx.BoxSizer(wx.VERTICAL) 00874 repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL) 00875 repo1Sizer = wx.BoxSizer(wx.HORIZONTAL) 00876 repo1Sizer.Add(item = self.repo, proportion = 1, 00877 flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1) 00878 repo1Sizer.Add(item = self.btnFetch, proportion = 0, 00879 flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1) 00880 repoSizer.Add(item = repo1Sizer, 00881 flag = wx.EXPAND) 00882 repoSizer.Add(item = self.fullDesc) 00883 00884 findSizer = wx.BoxSizer(wx.HORIZONTAL) 00885 findSizer.Add(item = self.search, proportion = 1) 00886 00887 treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL) 00888 treeSizer.Add(item = self.tree, proportion = 1, 00889 flag = wx.ALL | wx.EXPAND, border = 1) 00890 00891 # options 00892 optionSizer = wx.StaticBoxSizer(self.optionBox, wx.VERTICAL) 00893 for key in self.options.keys(): 00894 optionSizer.Add(item = self.options[key], proportion = 0) 00895 00896 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00897 btnSizer.Add(item = self.btnCmd, proportion = 0, 00898 flag = wx.RIGHT, border = 5) 00899 btnSizer.AddSpacer(10) 00900 btnSizer.Add(item = self.btnClose, proportion = 0, 00901 flag = wx.RIGHT, border = 5) 00902 btnSizer.Add(item = self.btnInstall, proportion = 0) 00903 00904 sizer.Add(item = repoSizer, proportion = 0, 00905 flag = wx.ALL | wx.EXPAND, border = 3) 00906 sizer.Add(item = findSizer, proportion = 0, 00907 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 00908 sizer.Add(item = treeSizer, proportion = 1, 00909 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 00910 sizer.Add(item = optionSizer, proportion = 0, 00911 flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 00912 sizer.Add(item = btnSizer, proportion = 0, 00913 flag = wx.ALIGN_RIGHT | wx.ALL, border = 5) 00914 00915 self.panel.SetSizer(sizer) 00916 sizer.Fit(self.panel) 00917 00918 self.Layout() 00919 00920 def _getCmd(self): 00921 item = self.tree.GetSelected() 00922 if not item or not item.IsOk(): 00923 return ['g.extension.py'] 00924 00925 name = self.tree.GetItemText(item) 00926 if not name: 00927 gcmd.GError(_("Extension not defined"), parent = self) 00928 return 00929 flags = list() 00930 for key in self.options.keys(): 00931 if self.options[key].IsChecked(): 00932 flags.append('-%s' % key) 00933 00934 return ['g.extension.py'] + flags + ['extension=' + name, 00935 'svnurl=' + self.repo.GetValue().strip()] 00936 00937 def OnUpdateStatusBar(self, event): 00938 """!Update statusbar text""" 00939 element = self.search.GetSelection() 00940 if not self.tree.IsLoaded(): 00941 self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0) 00942 return 00943 00944 self.tree.SearchItems(element = element, 00945 value = event.GetString()) 00946 00947 nItems = len(self.tree.itemsMarked) 00948 if event.GetString(): 00949 self.SetStatusText(_("%d items match") % nItems, 0) 00950 else: 00951 self.SetStatusText("", 0) 00952 00953 event.Skip() 00954 00955 def OnCloseWindow(self, event): 00956 """!Close window""" 00957 self.Destroy() 00958 00959 def OnFetch(self, event): 00960 """!Fetch list of available extensions""" 00961 wx.BeginBusyCursor() 00962 self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0) 00963 self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked()) 00964 self.SetStatusText("", 0) 00965 wx.EndBusyCursor() 00966 00967 def OnItemActivated(self, event): 00968 item = event.GetItem() 00969 data = self.tree.GetPyData(item) 00970 if data and 'command' in data: 00971 self.OnInstall(event = None) 00972 00973 def OnInstall(self, event): 00974 """!Install selected extension""" 00975 log = self.parent.GetLogWindow() 00976 log.RunCmd(self._getCmd(), onDone = self.OnDone) 00977 00978 def OnDone(self, cmd, returncode): 00979 item = self.tree.GetSelected() 00980 if not item or not item.IsOk() or \ 00981 returncode != 0 or \ 00982 not os.getenv('GRASS_ADDON_PATH'): 00983 return 00984 00985 name = self.tree.GetItemText(item) 00986 globalvar.grassCmd['all'].append(name) 00987 00988 def OnItemSelected(self, event): 00989 """!Item selected""" 00990 item = event.GetItem() 00991 self.tree.itemSelected = item 00992 data = self.tree.GetPyData(item) 00993 if data is None: 00994 self.SetStatusText('', 0) 00995 self.btnInstall.Enable(False) 00996 else: 00997 self.SetStatusText(data.get('description', ''), 0) 00998 self.btnInstall.Enable(True) 00999 01000 def OnShowItem(self, event): 01001 """!Show selected item""" 01002 self.tree.OnShowItem(event) 01003 if self.tree.GetSelected(): 01004 self.btnInstall.Enable() 01005 else: 01006 self.btnInstall.Enable(False) 01007 01008 def OnCmdDialog(self, event): 01009 """!Shows command dialog""" 01010 menuform.GUI(parent = self).ParseCommand(cmd = self._getCmd()) 01011 01012 class ExtensionTree(ItemTree): 01013 """!List of available extensions""" 01014 def __init__(self, parent, log, id = wx.ID_ANY, 01015 ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS | 01016 CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, 01017 **kwargs): 01018 self.parent = parent # GMFrame 01019 self.log = log 01020 01021 super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs) 01022 01023 self._initTree() 01024 01025 def _initTree(self): 01026 for prefix in ('display', 'database', 01027 'general', 'imagery', 01028 'misc', 'postscript', 'paint', 01029 'raster', 'raster3d', 'sites', 'vector', 'wxGUI', 'other'): 01030 self.AppendItem(parentId = self.root, 01031 text = prefix) 01032 self._loaded = False 01033 01034 def _expandPrefix(self, c): 01035 name = { 'd' : 'display', 01036 'db' : 'database', 01037 'g' : 'general', 01038 'i' : 'imagery', 01039 'm' : 'misc', 01040 'ps' : 'postscript', 01041 'p' : 'paint', 01042 'r' : 'raster', 01043 'r3' : 'raster3d', 01044 's' : 'sites', 01045 'v' : 'vector', 01046 'wx' : 'wxGUI', 01047 '' : 'other' } 01048 01049 if c in name: 01050 return name[c] 01051 01052 return c 01053 01054 def _findItem(self, text): 01055 """!Find item""" 01056 item = self.GetFirstChild(self.root)[0] 01057 while item and item.IsOk(): 01058 if text == self.GetItemText(item): 01059 return item 01060 01061 item = self.GetNextSibling(item) 01062 01063 return None 01064 01065 def Load(self, url, full = False): 01066 """!Load list of extensions""" 01067 self.DeleteAllItems() 01068 self.root = self.AddRoot(_("Menu tree")) 01069 self._initTree() 01070 01071 if full: 01072 flags = 'g' 01073 else: 01074 flags = 'l' 01075 ret = gcmd.RunCommand('g.extension.py', read = True, parent = self, 01076 svnurl = url, 01077 flags = flags, quiet = True) 01078 if not ret: 01079 return 01080 01081 mdict = dict() 01082 for line in ret.splitlines(): 01083 if full: 01084 try: 01085 key, value = line.split('=', 1) 01086 except ValueError: 01087 key = 'name' 01088 value = line 01089 01090 if key == 'name': 01091 try: 01092 prefix, name = value.split('.', 1) 01093 except ValueError: 01094 prefix = '' 01095 name = value 01096 if prefix not in mdict: 01097 mdict[prefix] = dict() 01098 mdict[prefix][name] = dict() 01099 else: 01100 mdict[prefix][name][key] = value 01101 else: 01102 try: 01103 prefix, name = line.strip().split('.', 1) 01104 except: 01105 prefix = '' 01106 name = line.strip() 01107 01108 if self._expandPrefix(prefix) == prefix: 01109 prefix = '' 01110 01111 if prefix not in mdict: 01112 mdict[prefix] = dict() 01113 01114 mdict[prefix][name] = { 'command' : prefix + '.' + name } 01115 01116 for prefix in mdict.keys(): 01117 prefixName = self._expandPrefix(prefix) 01118 item = self._findItem(prefixName) 01119 names = mdict[prefix].keys() 01120 names.sort() 01121 for name in names: 01122 if prefix: 01123 text = prefix + '.' + name 01124 else: 01125 text = name 01126 new = self.AppendItem(parentId = item, 01127 text = text) 01128 data = dict() 01129 for key in mdict[prefix][name].keys(): 01130 data[key] = mdict[prefix][name][key] 01131 01132 self.SetPyData(new, data) 01133 01134 self._loaded = True 01135 01136 def IsLoaded(self): 01137 """Check if items are loaded""" 01138 return self._loaded 01139 01140 class UninstallExtensionWindow(wx.Frame): 01141 def __init__(self, parent, id = wx.ID_ANY, 01142 title = _("Uninstall GRASS Addons extensions"), **kwargs): 01143 self.parent = parent 01144 01145 wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs) 01146 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 01147 01148 self.panel = wx.Panel(parent = self, id = wx.ID_ANY) 01149 01150 self.extBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 01151 label = " %s " % _("List of installed extensions")) 01152 01153 self.extList = CheckListExtension(parent = self.panel) 01154 01155 # buttons 01156 self.btnUninstall = wx.Button(parent = self.panel, id = wx.ID_ANY, 01157 label = _("&Uninstall")) 01158 self.btnUninstall.SetToolTipString(_("Uninstall selected AddOns extensions")) 01159 self.btnCmd = wx.Button(parent = self.panel, id = wx.ID_ANY, 01160 label = _("Command dialog")) 01161 self.btnCmd.SetToolTipString(_('Open %s dialog') % 'g.extension') 01162 self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE) 01163 01164 self.btnUninstall.Bind(wx.EVT_BUTTON, self.OnUninstall) 01165 self.btnCmd.Bind(wx.EVT_BUTTON, self.OnCmdDialog) 01166 self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow) 01167 01168 self._layout() 01169 01170 def _layout(self): 01171 """!Do layout""" 01172 sizer = wx.BoxSizer(wx.VERTICAL) 01173 01174 extSizer = wx.StaticBoxSizer(self.extBox, wx.HORIZONTAL) 01175 extSizer.Add(item = self.extList, proportion = 1, 01176 flag = wx.ALL | wx.EXPAND, border = 1) 01177 01178 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 01179 btnSizer.Add(item = self.btnCmd, proportion = 0, 01180 flag = wx.RIGHT, border = 5) 01181 btnSizer.AddSpacer(10) 01182 btnSizer.Add(item = self.btnClose, proportion = 0, 01183 flag = wx.RIGHT, border = 5) 01184 btnSizer.Add(item = self.btnUninstall, proportion = 0) 01185 01186 sizer.Add(item = extSizer, proportion = 1, 01187 flag = wx.ALL | wx.EXPAND, border = 3) 01188 sizer.Add(item = btnSizer, proportion = 0, 01189 flag = wx.ALIGN_RIGHT | wx.ALL, border = 5) 01190 01191 self.panel.SetSizer(sizer) 01192 sizer.Fit(self.panel) 01193 01194 self.Layout() 01195 01196 def OnCloseWindow(self, event): 01197 """!Close window""" 01198 self.Destroy() 01199 01200 def OnUninstall(self, event): 01201 """!Uninstall selected extensions""" 01202 log = self.parent.GetLogWindow() 01203 eList = self.extList.GetExtensions() 01204 if not eList: 01205 gcmd.GError(_("No extension selected for removal. " 01206 "Operation canceled."), 01207 parent = self) 01208 return 01209 01210 for ext in eList: 01211 files = gcmd.RunCommand('g.extension.py', parent = self, read = True, quiet = True, 01212 extension = ext, operation = 'remove').splitlines() 01213 dlg = wx.MessageDialog(parent = self, 01214 message = _("List of files to be removed:\n%(files)s\n\n" 01215 "Do you want really to remove <%(ext)s> extension?") % \ 01216 { 'files' : os.linesep.join(files), 'ext' : ext }, 01217 caption = _("Remove extension"), 01218 style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) 01219 01220 if dlg.ShowModal() == wx.ID_YES: 01221 gcmd.RunCommand('g.extension.py', flags = 'f', parent = self, quiet = True, 01222 extension = ext, operation = 'remove') 01223 01224 self.extList.LoadData() 01225 01226 def OnCmdDialog(self, event): 01227 """!Shows command dialog""" 01228 menuform.GUI(parent = self).ParseCommand(cmd = ['g.extension.py']) 01229 01230 class CheckListExtension(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.CheckListCtrlMixin): 01231 """!List of mapset/owner/group""" 01232 def __init__(self, parent): 01233 self.parent = parent 01234 01235 wx.ListCtrl.__init__(self, parent, id = wx.ID_ANY, 01236 style = wx.LC_REPORT) 01237 listmix.CheckListCtrlMixin.__init__(self) 01238 01239 # setup mixins 01240 listmix.ListCtrlAutoWidthMixin.__init__(self) 01241 01242 self.InsertColumn(0, _('Extension')) 01243 self.LoadData() 01244 01245 def LoadData(self): 01246 """!Load data into list""" 01247 self.DeleteAllItems() 01248 for ext in gcmd.RunCommand('g.extension.py', 01249 quiet = True, parent = self, read = True, 01250 flags = 'a').splitlines(): 01251 if ext: 01252 self.InsertStringItem(sys.maxint, ext) 01253 01254 def GetExtensions(self): 01255 """!Get extensions to be un-installed 01256 """ 01257 extList = list() 01258 for i in range(self.GetItemCount()): 01259 if self.IsChecked(i): 01260 name = self.GetItemText(i) 01261 if name: 01262 extList.append(name) 01263 01264 return extList 01265 01266 class HelpWindow(wx.html.HtmlWindow): 01267 """!This panel holds the text from GRASS docs. 01268 01269 GISBASE must be set in the environment to find the html docs dir. 01270 The SYNOPSIS section is skipped, since this Panel is supposed to 01271 be integrated into the cmdPanel and options are obvious there. 01272 """ 01273 def __init__(self, parent, grass_command, text, skip_description, 01274 **kwargs): 01275 """!If grass_command is given, the corresponding HTML help 01276 file will be presented, with all links pointing to absolute 01277 paths of local files. 01278 01279 If 'skip_description' is True, the HTML corresponding to 01280 SYNOPSIS will be skipped, thus only presenting the help file 01281 from the DESCRIPTION section onwards. 01282 01283 If 'text' is given, it must be the HTML text to be presented 01284 in the Panel. 01285 """ 01286 self.parent = parent 01287 wx.InitAllImageHandlers() 01288 wx.html.HtmlWindow.__init__(self, parent = parent, **kwargs) 01289 01290 gisbase = os.getenv("GISBASE") 01291 self.loaded = False 01292 self.history = list() 01293 self.historyIdx = 0 01294 self.fspath = os.path.join(gisbase, "docs", "html") 01295 01296 self.SetStandardFonts (size = 10) 01297 self.SetBorders(10) 01298 01299 if text is None: 01300 if skip_description: 01301 url = os.path.join(self.fspath, grass_command + ".html") 01302 self.fillContentsFromFile(url, 01303 skip_description = skip_description) 01304 self.history.append(url) 01305 self.loaded = True 01306 else: 01307 ### FIXME: calling LoadPage() is strangely time-consuming (only first call) 01308 # self.LoadPage(self.fspath + grass_command + ".html") 01309 self.loaded = False 01310 else: 01311 self.SetPage(text) 01312 self.loaded = True 01313 01314 def OnLinkClicked(self, linkinfo): 01315 url = linkinfo.GetHref() 01316 if url[:4] != 'http': 01317 url = os.path.join(self.fspath, url) 01318 self.history.append(url) 01319 self.historyIdx += 1 01320 self.parent.OnHistory() 01321 01322 super(HelpWindow, self).OnLinkClicked(linkinfo) 01323 01324 def fillContentsFromFile(self, htmlFile, skip_description = True): 01325 """!Load content from file""" 01326 aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE) 01327 imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE) 01328 try: 01329 contents = [] 01330 skip = False 01331 for l in file(htmlFile, "rb").readlines(): 01332 if "DESCRIPTION" in l: 01333 skip = False 01334 if not skip: 01335 # do skip the options description if requested 01336 if "SYNOPSIS" in l: 01337 skip = skip_description 01338 else: 01339 # FIXME: find only first item 01340 findALink = aLink.search(l) 01341 if findALink is not None: 01342 contents.append(aLink.sub(findALink.group(1)+ 01343 self.fspath+findALink.group(2),l)) 01344 findImgLink = imgLink.search(l) 01345 if findImgLink is not None: 01346 contents.append(imgLink.sub(findImgLink.group(1)+ 01347 self.fspath+findImgLink.group(2),l)) 01348 01349 if findALink is None and findImgLink is None: 01350 contents.append(l) 01351 self.SetPage("".join(contents)) 01352 self.loaded = True 01353 except: # The Manual file was not found 01354 self.loaded = False 01355 01356 class HelpPanel(wx.Panel): 01357 def __init__(self, parent, grass_command = "index", text = None, 01358 skip_description = False, **kwargs): 01359 self.grass_command = grass_command 01360 wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY) 01361 01362 self.content = HelpWindow(self, grass_command, text, 01363 skip_description) 01364 01365 self.btnNext = wx.Button(parent = self, id = wx.ID_ANY, 01366 label = _("&Next")) 01367 self.btnNext.Enable(False) 01368 self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY, 01369 label = _("&Previous")) 01370 self.btnPrev.Enable(False) 01371 01372 self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext) 01373 self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev) 01374 01375 self._layout() 01376 01377 def _layout(self): 01378 """!Do layout""" 01379 sizer = wx.BoxSizer(wx.VERTICAL) 01380 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 01381 01382 btnSizer.Add(item = self.btnPrev, proportion = 0, 01383 flag = wx.ALL, border = 5) 01384 btnSizer.Add(item = wx.Size(1, 1), proportion = 1) 01385 btnSizer.Add(item = self.btnNext, proportion = 0, 01386 flag = wx.ALIGN_RIGHT | wx.ALL, border = 5) 01387 01388 sizer.Add(item = self.content, proportion = 1, 01389 flag = wx.EXPAND) 01390 sizer.Add(item = btnSizer, proportion = 0, 01391 flag = wx.EXPAND) 01392 01393 self.SetSizer(sizer) 01394 sizer.Fit(self) 01395 01396 def LoadPage(self, path = None): 01397 """!Load page""" 01398 if not path: 01399 path = os.path.join(self.content.fspath, self.grass_command + ".html") 01400 self.content.history.append(path) 01401 self.content.LoadPage(path) 01402 01403 def IsFile(self): 01404 """!Check if file exists""" 01405 return os.path.isfile(os.path.join(self.content.fspath, self.grass_command + ".html")) 01406 01407 def IsLoaded(self): 01408 return self.content.loaded 01409 01410 def OnHistory(self): 01411 """!Update buttons""" 01412 nH = len(self.content.history) 01413 iH = self.content.historyIdx 01414 if iH == nH - 1: 01415 self.btnNext.Enable(False) 01416 elif iH > -1: 01417 self.btnNext.Enable(True) 01418 if iH < 1: 01419 self.btnPrev.Enable(False) 01420 else: 01421 self.btnPrev.Enable(True) 01422 01423 def OnNext(self, event): 01424 """Load next page""" 01425 self.content.historyIdx += 1 01426 idx = self.content.historyIdx 01427 path = self.content.history[idx] 01428 self.content.LoadPage(path) 01429 self.OnHistory() 01430 01431 event.Skip() 01432 01433 def OnPrev(self, event): 01434 """Load previous page""" 01435 self.content.historyIdx -= 1 01436 idx = self.content.historyIdx 01437 path = self.content.history[idx] 01438 self.content.LoadPage(path) 01439 self.OnHistory() 01440 01441 event.Skip()