|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
00001 """! 00002 @package gui_core.ghelp 00003 00004 @brief Help window 00005 00006 Classes: 00007 - ghelp::SearchModuleWindow 00008 - ghelp::MenuTreeWindow 00009 - ghelp::MenuTree 00010 - ghelp::AboutWindow 00011 - ghelp::HelpFrame 00012 - ghelp::HelpWindow 00013 - ghelp::HelpPanel 00014 00015 (C) 2008-2011 by the GRASS Development Team 00016 00017 This program is free software under the GNU General Public License 00018 (>=v2). Read the file COPYING that comes with GRASS for details. 00019 00020 @author Martin Landa <landa.martin gmail.com> 00021 """ 00022 00023 import os 00024 import sys 00025 import codecs 00026 import platform 00027 00028 import wx 00029 from wx.html import HtmlWindow 00030 try: 00031 import wx.lib.agw.customtreectrl as CT 00032 from wx.lib.agw.hyperlink import HyperLinkCtrl 00033 except ImportError: 00034 import wx.lib.customtreectrl as CT 00035 from wx.lib.hyperlink import HyperLinkCtrl 00036 import wx.lib.flatnotebook as FN 00037 00038 import grass.script as grass 00039 00040 from core import globalvar 00041 from core import utils 00042 from lmgr.menudata import ManagerData 00043 from core.gcmd import GError, DecodeString 00044 from gui_core.widgets import GNotebook, StaticWrapText, ItemTree, ScrolledPanel 00045 00046 class SearchModuleWindow(wx.Panel): 00047 """!Search module window (used in MenuTreeWindow)""" 00048 def __init__(self, parent, id = wx.ID_ANY, cmdPrompt = None, 00049 showChoice = True, showTip = False, **kwargs): 00050 self.showTip = showTip 00051 self.showChoice = showChoice 00052 self.cmdPrompt = cmdPrompt 00053 00054 wx.Panel.__init__(self, parent = parent, id = id, **kwargs) 00055 00056 self._searchDict = { _('description') : 'description', 00057 _('command') : 'command', 00058 _('keywords') : 'keywords' } 00059 00060 self.box = wx.StaticBox(parent = self, id = wx.ID_ANY, 00061 label = " %s " % _("Find module(s)")) 00062 00063 self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY, 00064 choices = [_('description'), 00065 _('keywords'), 00066 _('command')]) 00067 self.searchBy.SetSelection(0) 00068 00069 self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY, 00070 value = "", size = (-1, 25), 00071 style = wx.TE_PROCESS_ENTER) 00072 self.search.Bind(wx.EVT_TEXT, self.OnSearchModule) 00073 00074 if self.showTip: 00075 self.searchTip = StaticWrapText(parent = self, id = wx.ID_ANY, 00076 size = (-1, 35)) 00077 00078 if self.showChoice: 00079 self.searchChoice = wx.Choice(parent = self, id = wx.ID_ANY) 00080 if self.cmdPrompt: 00081 self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems()) 00082 self.searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule) 00083 00084 self._layout() 00085 00086 def _layout(self): 00087 """!Do layout""" 00088 sizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL) 00089 gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3) 00090 gridSizer.AddGrowableCol(1) 00091 00092 gridSizer.Add(item = self.searchBy, 00093 flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0)) 00094 gridSizer.Add(item = self.search, 00095 flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (0, 1)) 00096 row = 1 00097 if self.showTip: 00098 gridSizer.Add(item = self.searchTip, 00099 flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2)) 00100 row += 1 00101 00102 if self.showChoice: 00103 gridSizer.Add(item = self.searchChoice, 00104 flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2)) 00105 00106 sizer.Add(item = gridSizer, proportion = 1) 00107 00108 self.SetSizer(sizer) 00109 sizer.Fit(self) 00110 00111 def GetSelection(self): 00112 """!Get selected element""" 00113 selection = self.searchBy.GetStringSelection() 00114 00115 return self._searchDict[selection] 00116 00117 def SetSelection(self, i): 00118 """!Set selection element""" 00119 self.searchBy.SetSelection(i) 00120 00121 def OnSearchModule(self, event): 00122 """!Search module by keywords or description""" 00123 if not self.cmdPrompt: 00124 event.Skip() 00125 return 00126 00127 text = event.GetString() 00128 if not text: 00129 self.cmdPrompt.SetFilter(None) 00130 mList = self.cmdPrompt.GetCommandItems() 00131 self.searchChoice.SetItems(mList) 00132 if self.showTip: 00133 self.searchTip.SetLabel(_("%d modules found") % len(mList)) 00134 event.Skip() 00135 return 00136 00137 modules = dict() 00138 iFound = 0 00139 for module, data in self.cmdPrompt.moduleDesc.iteritems(): 00140 found = False 00141 sel = self.searchBy.GetSelection() 00142 if sel == 0: # -> description 00143 if text in data['desc']: 00144 found = True 00145 elif sel == 1: # keywords 00146 if text in ','.join(data['keywords']): 00147 found = True 00148 else: # command 00149 if module[:len(text)] == text: 00150 found = True 00151 00152 if found: 00153 iFound += 1 00154 try: 00155 group, name = module.split('.') 00156 except ValueError: 00157 continue # TODO 00158 00159 if group not in modules: 00160 modules[group] = list() 00161 modules[group].append(name) 00162 00163 self.cmdPrompt.SetFilter(modules) 00164 self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems()) 00165 if self.showTip: 00166 self.searchTip.SetLabel(_("%d modules found") % iFound) 00167 00168 event.Skip() 00169 00170 def OnSelectModule(self, event): 00171 """!Module selected from choice, update command prompt""" 00172 cmd = event.GetString().split(' ', 1)[0] 00173 text = cmd + ' ' 00174 pos = len(text) 00175 00176 if self.cmdPrompt: 00177 self.cmdPrompt.SetText(text) 00178 self.cmdPrompt.SetSelectionStart(pos) 00179 self.cmdPrompt.SetCurrentPos(pos) 00180 self.cmdPrompt.SetFocus() 00181 00182 desc = self.cmdPrompt.GetCommandDesc(cmd) 00183 if self.showTip: 00184 self.searchTip.SetLabel(desc) 00185 00186 def Reset(self): 00187 """!Reset widget""" 00188 self.searchBy.SetSelection(0) 00189 self.search.SetValue('') 00190 if self.showTip: 00191 self.searchTip.SetLabel('') 00192 00193 class MenuTreeWindow(wx.Panel): 00194 """!Show menu tree""" 00195 def __init__(self, parent, id = wx.ID_ANY, **kwargs): 00196 self.parent = parent # LayerManager 00197 00198 wx.Panel.__init__(self, parent = parent, id = id, **kwargs) 00199 00200 self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY, 00201 label = " %s " % _("Menu tree (double-click to run command)")) 00202 # tree 00203 self.tree = MenuTree(parent = self, data = ManagerData()) 00204 self.tree.Load() 00205 00206 # search widget 00207 self.search = SearchModuleWindow(parent = self, showChoice = False) 00208 00209 # buttons 00210 self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("&Run")) 00211 self.btnRun.SetToolTipString(_("Run selected command")) 00212 self.btnRun.Enable(False) 00213 00214 # bindings 00215 self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun) 00216 self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated) 00217 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected) 00218 self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem) 00219 self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar) 00220 00221 self._layout() 00222 00223 self.search.SetFocus() 00224 00225 def _layout(self): 00226 """!Do dialog layout""" 00227 sizer = wx.BoxSizer(wx.VERTICAL) 00228 00229 # body 00230 dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL) 00231 dataSizer.Add(item = self.tree, proportion =1, 00232 flag = wx.EXPAND) 00233 00234 # buttons 00235 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00236 btnSizer.Add(item = self.btnRun, proportion = 0) 00237 00238 sizer.Add(item = dataSizer, proportion = 1, 00239 flag = wx.EXPAND | wx.ALL, border = 5) 00240 00241 sizer.Add(item = self.search, proportion = 0, 00242 flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5) 00243 00244 sizer.Add(item = btnSizer, proportion = 0, 00245 flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5) 00246 00247 sizer.Fit(self) 00248 sizer.SetSizeHints(self) 00249 00250 self.SetSizer(sizer) 00251 00252 self.Fit() 00253 self.SetAutoLayout(True) 00254 self.Layout() 00255 00256 def OnCloseWindow(self, event): 00257 """!Close window""" 00258 self.Destroy() 00259 00260 def OnRun(self, event): 00261 """!Run selected command""" 00262 if not self.tree.GetSelected(): 00263 return # should not happen 00264 00265 data = self.tree.GetPyData(self.tree.GetSelected()) 00266 if not data: 00267 return 00268 00269 handler = 'self.parent.' + data['handler'].lstrip('self.') 00270 if data['handler'] == 'self.OnXTerm': 00271 wx.MessageBox(parent = self, 00272 message = _('You must run this command from the menu or command line', 00273 'This command require an XTerm'), 00274 caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE) 00275 elif data['command']: 00276 eval(handler)(event = None, cmd = data['command'].split()) 00277 else: 00278 eval(handler)(None) 00279 00280 def OnShowItem(self, event): 00281 """!Show selected item""" 00282 self.tree.OnShowItem(event) 00283 if self.tree.GetSelected(): 00284 self.btnRun.Enable() 00285 else: 00286 self.btnRun.Enable(False) 00287 00288 def OnItemActivated(self, event): 00289 """!Item activated (double-click)""" 00290 item = event.GetItem() 00291 if not item or not item.IsOk(): 00292 return 00293 00294 data = self.tree.GetPyData(item) 00295 if not data or 'command' not in data: 00296 return 00297 00298 self.tree.itemSelected = item 00299 00300 self.OnRun(None) 00301 00302 def OnItemSelected(self, event): 00303 """!Item selected""" 00304 item = event.GetItem() 00305 if not item or not item.IsOk(): 00306 return 00307 00308 data = self.tree.GetPyData(item) 00309 if not data or 'command' not in data: 00310 return 00311 00312 if data['command']: 00313 label = data['command'] + ' -- ' + data['description'] 00314 else: 00315 label = data['description'] 00316 00317 self.parent.SetStatusText(label, 0) 00318 00319 def OnUpdateStatusBar(self, event): 00320 """!Update statusbar text""" 00321 element = self.search.GetSelection() 00322 self.tree.SearchItems(element = element, 00323 value = event.GetString()) 00324 00325 nItems = len(self.tree.itemsMarked) 00326 if event.GetString(): 00327 self.parent.SetStatusText(_("%d modules match") % nItems, 0) 00328 else: 00329 self.parent.SetStatusText("", 0) 00330 00331 event.Skip() 00332 00333 class MenuTree(ItemTree): 00334 """!Menu tree class""" 00335 def __init__(self, parent, data, **kwargs): 00336 self.parent = parent 00337 self.menudata = data 00338 00339 super(MenuTree, self).__init__(parent, **kwargs) 00340 00341 def Load(self, data = None): 00342 """!Load menu data tree 00343 00344 @param data menu data (None to use self.menudata) 00345 """ 00346 if not data: 00347 data = self.menudata 00348 00349 self.itemsMarked = [] # list of marked items 00350 for eachMenuData in data.GetMenu(): 00351 for label, items in eachMenuData: 00352 item = self.AppendItem(parentId = self.root, 00353 text = label.replace('&', '')) 00354 self.__AppendItems(item, items) 00355 00356 def __AppendItems(self, item, data): 00357 """!Append items into tree (used by Load() 00358 00359 @param item tree item (parent) 00360 @parent data menu data""" 00361 for eachItem in data: 00362 if len(eachItem) == 2: 00363 if eachItem[0]: 00364 itemSub = self.AppendItem(parentId = item, 00365 text = eachItem[0]) 00366 self.__AppendItems(itemSub, eachItem[1]) 00367 else: 00368 if eachItem[0]: 00369 itemNew = self.AppendItem(parentId = item, 00370 text = eachItem[0]) 00371 00372 data = { 'item' : eachItem[0], 00373 'description' : eachItem[1], 00374 'handler' : eachItem[2], 00375 'command' : eachItem[3], 00376 'keywords' : eachItem[4] } 00377 00378 self.SetPyData(itemNew, data) 00379 00380 class AboutWindow(wx.Frame): 00381 """!Create custom About Window 00382 00383 @todo improve styling 00384 """ 00385 def __init__(self, parent, size = (750, 450), 00386 title = _('About GRASS GIS'), **kwargs): 00387 wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, title = title, size = size, **kwargs) 00388 00389 panel = wx.Panel(parent = self, id = wx.ID_ANY) 00390 00391 # icon 00392 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 00393 00394 # get version and web site 00395 vInfo = grass.version() 00396 00397 infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY) 00398 infoSizer = wx.BoxSizer(wx.VERTICAL) 00399 infoGridSizer = wx.GridBagSizer(vgap = 5, hgap = 5) 00400 infoGridSizer.AddGrowableCol(0) 00401 infoGridSizer.AddGrowableCol(1) 00402 logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass-64x64.png") 00403 logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY, 00404 bitmap = wx.Bitmap(name = logo, 00405 type = wx.BITMAP_TYPE_PNG)) 00406 infoSizer.Add(item = logoBitmap, proportion = 0, 00407 flag = wx.ALL | wx.ALIGN_CENTER, border = 25) 00408 00409 info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00410 label = 'GRASS GIS ' + vInfo['version'] + '\n\n') 00411 info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) 00412 info.SetForegroundColour(wx.Colour(35, 142, 35)) 00413 infoSizer.Add(item = info, proportion = 0, 00414 flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15) 00415 00416 row = 0 00417 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00418 label = _('Official GRASS site:')), 00419 pos = (row, 0), 00420 flag = wx.ALIGN_RIGHT) 00421 00422 infoGridSizer.Add(item = HyperLinkCtrl(parent = infoTxt, id = wx.ID_ANY, 00423 label = 'http://grass.osgeo.org'), 00424 pos = (row, 1), 00425 flag = wx.ALIGN_LEFT) 00426 00427 row += 2 00428 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00429 label = _('SVN Revision:')), 00430 pos = (row, 0), 00431 flag = wx.ALIGN_RIGHT) 00432 00433 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00434 label = vInfo['revision']), 00435 pos = (row, 1), 00436 flag = wx.ALIGN_LEFT) 00437 00438 row += 1 00439 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00440 label = _('GIS Library Revision:')), 00441 pos = (row, 0), 00442 flag = wx.ALIGN_RIGHT) 00443 00444 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00445 label = vInfo['libgis_revision'] + ' (' + 00446 vInfo['libgis_date'].split(' ')[0] + ')'), 00447 pos = (row, 1), 00448 flag = wx.ALIGN_LEFT) 00449 00450 row += 2 00451 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00452 label = _('Python:')), 00453 pos = (row, 0), 00454 flag = wx.ALIGN_RIGHT) 00455 00456 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00457 label = platform.python_version()), 00458 pos = (row, 1), 00459 flag = wx.ALIGN_LEFT) 00460 00461 row += 1 00462 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00463 label = _('wxPython:')), 00464 pos = (row, 0), 00465 flag = wx.ALIGN_RIGHT) 00466 00467 infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY, 00468 label = wx.__version__), 00469 pos = (row, 1), 00470 flag = wx.ALIGN_LEFT) 00471 00472 infoSizer.Add(item = infoGridSizer, 00473 proportion = 1, 00474 flag = wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL) 00475 00476 # create a flat notebook for displaying information about GRASS 00477 aboutNotebook = GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON) 00478 aboutNotebook.SetTabAreaColour(globalvar.FNPageColor) 00479 00480 for title, win in ((_("Info"), infoTxt), 00481 (_("Copyright"), self._pageCopyright()), 00482 (_("License"), self._pageLicense()), 00483 (_("Authors"), self._pageCredit()), 00484 (_("Contributors"), self._pageContributors()), 00485 (_("Extra contributors"), self._pageContributors(extra = True)), 00486 (_("Translators"), self._pageTranslators())): 00487 aboutNotebook.AddPage(page = win, text = title) 00488 wx.CallAfter(aboutNotebook.SetSelection, 0) 00489 00490 # buttons 00491 btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE) 00492 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00493 btnSizer.Add(item = btnClose, proportion = 0, 00494 flag = wx.ALL | wx.ALIGN_RIGHT, 00495 border = 5) 00496 # bindings 00497 btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow) 00498 00499 infoTxt.SetSizer(infoSizer) 00500 infoSizer.Fit(infoTxt) 00501 00502 sizer = wx.BoxSizer(wx.VERTICAL) 00503 sizer.Add(item = aboutNotebook, proportion = 1, 00504 flag = wx.EXPAND | wx.ALL, border = 1) 00505 sizer.Add(item = btnSizer, proportion = 0, 00506 flag = wx.ALL | wx.ALIGN_RIGHT, border = 1) 00507 panel.SetSizer(sizer) 00508 00509 self.Layout() 00510 self.SetMinSize((500, 400)) 00511 00512 def _pageCopyright(self): 00513 """Copyright information""" 00514 copyfile = os.path.join(os.getenv("GISBASE"), "COPYING") 00515 if os.path.exists(copyfile): 00516 copyrightFile = open(copyfile, 'r') 00517 copytext = copyrightFile.read() 00518 copyrightFile.close() 00519 else: 00520 copytext = _('%s file missing') % 'COPYING' 00521 00522 # put text into a scrolling panel 00523 copyrightwin = ScrolledPanel(self) 00524 00525 copyrighttxt = wx.StaticText(copyrightwin, id = wx.ID_ANY, label = copytext) 00526 copyrightwin.SetAutoLayout(True) 00527 copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL) 00528 copyrightwin.sizer.Add(item = copyrighttxt, proportion = 1, 00529 flag = wx.EXPAND | wx.ALL, border = 3) 00530 copyrightwin.SetSizer(copyrightwin.sizer) 00531 copyrightwin.Layout() 00532 copyrightwin.SetupScrolling() 00533 00534 return copyrightwin 00535 00536 def _pageLicense(self): 00537 """Licence about""" 00538 licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT") 00539 if os.path.exists(licfile): 00540 licenceFile = open(licfile, 'r') 00541 license = ''.join(licenceFile.readlines()) 00542 licenceFile.close() 00543 else: 00544 license = _('%s file missing') % 'GPL.TXT' 00545 # put text into a scrolling panel 00546 licensewin = ScrolledPanel(self) 00547 licensetxt = wx.StaticText(licensewin, id = wx.ID_ANY, label = license) 00548 licensewin.SetAutoLayout(True) 00549 licensewin.sizer = wx.BoxSizer(wx.VERTICAL) 00550 licensewin.sizer.Add(item = licensetxt, proportion = 1, 00551 flag = wx.EXPAND | wx.ALL, border = 3) 00552 licensewin.SetSizer(licensewin.sizer) 00553 licensewin.Layout() 00554 licensewin.SetupScrolling() 00555 00556 return licensewin 00557 00558 def _pageCredit(self): 00559 """Credit about""" 00560 # credits 00561 authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS") 00562 if os.path.exists(authfile): 00563 authorsFile = open(authfile, 'r') 00564 authors = unicode(''.join(authorsFile.readlines()), "utf-8") 00565 authorsFile.close() 00566 else: 00567 authors = _('%s file missing') % 'AUTHORS' 00568 authorwin = ScrolledPanel(self) 00569 authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors) 00570 authorwin.SetAutoLayout(True) 00571 authorwin.SetupScrolling() 00572 authorwin.sizer = wx.BoxSizer(wx.VERTICAL) 00573 authorwin.sizer.Add(item = authortxt, proportion = 1, 00574 flag = wx.EXPAND | wx.ALL, border = 3) 00575 authorwin.SetSizer(authorwin.sizer) 00576 authorwin.Layout() 00577 00578 return authorwin 00579 00580 def _pageContributors(self, extra = False): 00581 """Contributors info""" 00582 if extra: 00583 contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv") 00584 else: 00585 contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv") 00586 if os.path.exists(contribfile): 00587 contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r') 00588 contribs = list() 00589 errLines = list() 00590 for line in contribFile.readlines()[1:]: 00591 line = line.rstrip('\n') 00592 try: 00593 if extra: 00594 name, email, rfc2_agreed = line.split(',') 00595 else: 00596 cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',') 00597 except ValueError: 00598 errLines.append(line) 00599 continue 00600 if extra: 00601 contribs.append((name, email)) 00602 else: 00603 contribs.append((name, email, country, osgeo_id)) 00604 00605 contribFile.close() 00606 00607 if errLines: 00608 GError(parent = self, 00609 message = _("Error when reading file '%s'.") % contribfile + \ 00610 "\n\n" + _("Lines:") + " %s" % \ 00611 os.linesep.join(map(DecodeString, errLines))) 00612 else: 00613 contribs = None 00614 00615 contribwin = ScrolledPanel(self) 00616 contribwin.SetAutoLayout(True) 00617 contribwin.SetupScrolling() 00618 contribwin.sizer = wx.BoxSizer(wx.VERTICAL) 00619 00620 if not contribs: 00621 contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY, 00622 label = _('%s file missing') % contribfile) 00623 contribwin.sizer.Add(item = contribtxt, proportion = 1, 00624 flag = wx.EXPAND | wx.ALL, border = 3) 00625 else: 00626 if extra: 00627 items = (_('Name'), _('E-mail')) 00628 else: 00629 items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID')) 00630 contribBox = wx.FlexGridSizer(cols = len(items), vgap = 5, hgap = 5) 00631 for item in items: 00632 contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY, 00633 label = item)) 00634 for vals in sorted(contribs, key = lambda x: x[0]): 00635 for item in vals: 00636 contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY, 00637 label = item)) 00638 contribwin.sizer.Add(item = contribBox, proportion = 1, 00639 flag = wx.EXPAND | wx.ALL, border = 3) 00640 00641 contribwin.SetSizer(contribwin.sizer) 00642 contribwin.Layout() 00643 00644 return contribwin 00645 00646 def _pageTranslators(self): 00647 """Translators info""" 00648 translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv") 00649 if os.path.exists(translatorsfile): 00650 translatorsFile = open(translatorsfile, 'r') 00651 translators = dict() 00652 errLines = list() 00653 for line in translatorsFile.readlines()[1:]: 00654 line = line.rstrip('\n') 00655 try: 00656 name, email, languages = line.split(',') 00657 except ValueError: 00658 errLines.append(line) 00659 continue 00660 for language in languages.split(' '): 00661 if language not in translators: 00662 translators[language] = list() 00663 translators[language].append((name, email)) 00664 translatorsFile.close() 00665 00666 if errLines: 00667 GError(parent = self, 00668 message = _("Error when reading file '%s'.") % translatorsfile + \ 00669 "\n\n" + _("Lines:") + " %s" % \ 00670 os.linesep.join(map(DecodeString, errLines))) 00671 else: 00672 translators = None 00673 00674 translatorswin = ScrolledPanel(self) 00675 translatorswin.SetAutoLayout(True) 00676 translatorswin.SetupScrolling() 00677 translatorswin.sizer = wx.BoxSizer(wx.VERTICAL) 00678 00679 if not translators: 00680 translatorstxt = wx.StaticText(translatorswin, id = wx.ID_ANY, 00681 label = _('%s file missing') % 'translators.csv') 00682 translatorswin.sizer.Add(item = translatorstxt, proportion = 1, 00683 flag = wx.EXPAND | wx.ALL, border = 3) 00684 else: 00685 translatorsBox = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5) 00686 languages = translators.keys() 00687 languages.sort() 00688 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00689 label = _('Name'))) 00690 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00691 label = _('E-mail'))) 00692 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00693 label = _('Language'))) 00694 for lang in languages: 00695 for translator in translators[lang]: 00696 name, email = translator 00697 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00698 label = unicode(name, "utf-8"))) 00699 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00700 label = email)) 00701 translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY, 00702 label = lang)) 00703 00704 translatorswin.sizer.Add(item = translatorsBox, proportion = 1, 00705 flag = wx.EXPAND | wx.ALL, border = 3) 00706 00707 translatorswin.SetSizer(translatorswin.sizer) 00708 translatorswin.Layout() 00709 00710 return translatorswin 00711 00712 def OnCloseWindow(self, event): 00713 """!Close window""" 00714 self.Close() 00715 00716 class HelpFrame(wx.Frame): 00717 """!GRASS Quickstart help window""" 00718 def __init__(self, parent, id, title, size, file): 00719 wx.Frame.__init__(self, parent = parent, id = id, title = title, size = size) 00720 00721 sizer = wx.BoxSizer(wx.VERTICAL) 00722 00723 # text 00724 content = HelpPanel(parent = self) 00725 content.LoadPage(file) 00726 00727 sizer.Add(item = content, proportion = 1, flag = wx.EXPAND) 00728 00729 self.SetAutoLayout(True) 00730 self.SetSizer(sizer) 00731 self.Layout() 00732 00733 class HelpWindow(wx.html.HtmlWindow): 00734 """!This panel holds the text from GRASS docs. 00735 00736 GISBASE must be set in the environment to find the html docs dir. 00737 The SYNOPSIS section is skipped, since this Panel is supposed to 00738 be integrated into the cmdPanel and options are obvious there. 00739 """ 00740 def __init__(self, parent, grass_command, text, skip_description, 00741 **kwargs): 00742 """!If grass_command is given, the corresponding HTML help 00743 file will be presented, with all links pointing to absolute 00744 paths of local files. 00745 00746 If 'skip_description' is True, the HTML corresponding to 00747 SYNOPSIS will be skipped, thus only presenting the help file 00748 from the DESCRIPTION section onwards. 00749 00750 If 'text' is given, it must be the HTML text to be presented 00751 in the Panel. 00752 """ 00753 self.parent = parent 00754 wx.InitAllImageHandlers() 00755 wx.html.HtmlWindow.__init__(self, parent = parent, **kwargs) 00756 00757 gisbase = os.getenv("GISBASE") 00758 self.loaded = False 00759 self.history = list() 00760 self.historyIdx = 0 00761 self.fspath = os.path.join(gisbase, "docs", "html") 00762 00763 self.SetStandardFonts (size = 10) 00764 self.SetBorders(10) 00765 00766 if text is None: 00767 if skip_description: 00768 url = os.path.join(self.fspath, grass_command + ".html") 00769 self.fillContentsFromFile(url, 00770 skip_description = skip_description) 00771 self.history.append(url) 00772 self.loaded = True 00773 else: 00774 ### FIXME: calling LoadPage() is strangely time-consuming (only first call) 00775 # self.LoadPage(self.fspath + grass_command + ".html") 00776 self.loaded = False 00777 else: 00778 self.SetPage(text) 00779 self.loaded = True 00780 00781 def OnLinkClicked(self, linkinfo): 00782 url = linkinfo.GetHref() 00783 if url[:4] != 'http': 00784 url = os.path.join(self.fspath, url) 00785 self.history.append(url) 00786 self.historyIdx += 1 00787 self.parent.OnHistory() 00788 00789 super(HelpWindow, self).OnLinkClicked(linkinfo) 00790 00791 def fillContentsFromFile(self, htmlFile, skip_description = True): 00792 """!Load content from file""" 00793 aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE) 00794 imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE) 00795 try: 00796 contents = [] 00797 skip = False 00798 for l in file(htmlFile, "rb").readlines(): 00799 if "DESCRIPTION" in l: 00800 skip = False 00801 if not skip: 00802 # do skip the options description if requested 00803 if "SYNOPSIS" in l: 00804 skip = skip_description 00805 else: 00806 # FIXME: find only first item 00807 findALink = aLink.search(l) 00808 if findALink is not None: 00809 contents.append(aLink.sub(findALink.group(1)+ 00810 self.fspath+findALink.group(2),l)) 00811 findImgLink = imgLink.search(l) 00812 if findImgLink is not None: 00813 contents.append(imgLink.sub(findImgLink.group(1)+ 00814 self.fspath+findImgLink.group(2),l)) 00815 00816 if findALink is None and findImgLink is None: 00817 contents.append(l) 00818 self.SetPage("".join(contents)) 00819 self.loaded = True 00820 except: # The Manual file was not found 00821 self.loaded = False 00822 00823 class HelpPanel(wx.Panel): 00824 def __init__(self, parent, grass_command = "index", text = None, 00825 skip_description = False, **kwargs): 00826 self.grass_command = grass_command 00827 wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY) 00828 00829 self.content = HelpWindow(self, grass_command, text, 00830 skip_description) 00831 00832 self.btnNext = wx.Button(parent = self, id = wx.ID_ANY, 00833 label = _("&Next")) 00834 self.btnNext.Enable(False) 00835 self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY, 00836 label = _("&Previous")) 00837 self.btnPrev.Enable(False) 00838 00839 self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext) 00840 self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev) 00841 00842 self._layout() 00843 00844 def _layout(self): 00845 """!Do layout""" 00846 sizer = wx.BoxSizer(wx.VERTICAL) 00847 btnSizer = wx.BoxSizer(wx.HORIZONTAL) 00848 00849 btnSizer.Add(item = self.btnPrev, proportion = 0, 00850 flag = wx.ALL, border = 5) 00851 btnSizer.Add(item = wx.Size(1, 1), proportion = 1) 00852 btnSizer.Add(item = self.btnNext, proportion = 0, 00853 flag = wx.ALIGN_RIGHT | wx.ALL, border = 5) 00854 00855 sizer.Add(item = self.content, proportion = 1, 00856 flag = wx.EXPAND) 00857 sizer.Add(item = btnSizer, proportion = 0, 00858 flag = wx.EXPAND) 00859 00860 self.SetSizer(sizer) 00861 sizer.Fit(self) 00862 00863 def LoadPage(self, path = None): 00864 """!Load page""" 00865 if not path: 00866 path = os.path.join(self.content.fspath, self.grass_command + ".html") 00867 self.content.history.append(path) 00868 self.content.LoadPage(path) 00869 00870 def IsFile(self): 00871 """!Check if file exists""" 00872 return os.path.isfile(os.path.join(self.content.fspath, self.grass_command + ".html")) 00873 00874 def IsLoaded(self): 00875 return self.content.loaded 00876 00877 def OnHistory(self): 00878 """!Update buttons""" 00879 nH = len(self.content.history) 00880 iH = self.content.historyIdx 00881 if iH == nH - 1: 00882 self.btnNext.Enable(False) 00883 elif iH > -1: 00884 self.btnNext.Enable(True) 00885 if iH < 1: 00886 self.btnPrev.Enable(False) 00887 else: 00888 self.btnPrev.Enable(True) 00889 00890 def OnNext(self, event): 00891 """Load next page""" 00892 self.content.historyIdx += 1 00893 idx = self.content.historyIdx 00894 path = self.content.history[idx] 00895 self.content.LoadPage(path) 00896 self.OnHistory() 00897 00898 event.Skip() 00899 00900 def OnPrev(self, event): 00901 """Load previous page""" 00902 self.content.historyIdx -= 1 00903 idx = self.content.historyIdx 00904 path = self.content.history[idx] 00905 self.content.LoadPage(path) 00906 self.OnHistory() 00907 00908 event.Skip()