GRASS Programmer's Manual  6.4.4(2014)-r
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
ghelp.py
Go to the documentation of this file.
1 """!
2 @package gui_core.ghelp
3 
4 @brief Help window
5 
6 Classes:
7  - ghelp::SearchModuleWindow
8  - ghelp::MenuTreeWindow
9  - ghelp::MenuTree
10  - ghelp::AboutWindow
11  - ghelp::HelpFrame
12  - ghelp::HelpWindow
13  - ghelp::HelpPanel
14 
15 (C) 2008-2011 by the GRASS Development Team
16 
17 This program is free software under the GNU General Public License
18 (>=v2). Read the file COPYING that comes with GRASS for details.
19 
20 @author Martin Landa <landa.martin gmail.com>
21 """
22 
23 import os
24 import sys
25 import re
26 import codecs
27 import platform
28 
29 import wx
30 from wx.html import HtmlWindow
31 try:
32  import wx.lib.agw.customtreectrl as CT
33  from wx.lib.agw.hyperlink import HyperLinkCtrl
34 except ImportError:
35  import wx.lib.customtreectrl as CT
36  from wx.lib.hyperlink import HyperLinkCtrl
37 import wx.lib.flatnotebook as FN
38 
39 import grass.script as grass
40 
41 from core import globalvar
42 from core import utils
43 from lmgr.menudata import ManagerData
44 from core.gcmd import GError, DecodeString
45 from gui_core.widgets import GNotebook, StaticWrapText, ItemTree, ScrolledPanel
46 
47 class SearchModuleWindow(wx.Panel):
48  """!Search module window (used in MenuTreeWindow)"""
49  def __init__(self, parent, id = wx.ID_ANY, cmdPrompt = None,
50  showChoice = True, showTip = False, **kwargs):
51  self.showTip = showTip
52  self.showChoice = showChoice
53  self.cmdPrompt = cmdPrompt
54 
55  wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
56 
57  self._searchDict = { _('description') : 'description',
58  _('command') : 'command',
59  _('keywords') : 'keywords' }
60 
61  self.box = wx.StaticBox(parent = self, id = wx.ID_ANY,
62  label = " %s " % _("Find module(s)"))
63 
64  self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY,
65  choices = [_('description'),
66  _('keywords'),
67  _('command')])
68  self.searchBy.SetSelection(0)
69 
70  self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY,
71  value = "", size = (-1, 25),
72  style = wx.TE_PROCESS_ENTER)
73  self.search.Bind(wx.EVT_TEXT, self.OnSearchModule)
74 
75  if self.showTip:
76  self.searchTip = StaticWrapText(parent = self, id = wx.ID_ANY,
77  size = (-1, 35))
78 
79  if self.showChoice:
80  self.searchChoice = wx.Choice(parent = self, id = wx.ID_ANY)
81  if self.cmdPrompt:
82  self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
83  self.searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule)
84 
85  self._layout()
86 
87  def _layout(self):
88  """!Do layout"""
89  sizer = wx.StaticBoxSizer(self.box, wx.HORIZONTAL)
90  gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3)
91  gridSizer.SetCols(2)
92 
93  gridSizer.Add(item = self.searchBy,
94  flag = wx.ALIGN_CENTER_VERTICAL, pos = (0, 0))
95  gridSizer.Add(item = self.search,
96  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (0, 1))
97  row = 1
98  if self.showTip:
99  gridSizer.Add(item = self.searchTip,
100  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
101  row += 1
102 
103  if self.showChoice:
104  gridSizer.Add(item = self.searchChoice,
105  flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, pos = (row, 0), span = (1, 2))
106 
107  gridSizer.AddGrowableCol(1)
108  sizer.Add(item = gridSizer, proportion = 1)
109 
110  self.SetSizer(sizer)
111  sizer.Fit(self)
112 
113  def GetSelection(self):
114  """!Get selected element"""
115  selection = self.searchBy.GetStringSelection()
116 
117  return self._searchDict[selection]
118 
119  def SetSelection(self, i):
120  """!Set selection element"""
121  self.searchBy.SetSelection(i)
122 
123  def OnSearchModule(self, event):
124  """!Search module by keywords or description"""
125  if not self.cmdPrompt:
126  event.Skip()
127  return
128 
129  text = event.GetString()
130  if not text:
131  self.cmdPrompt.SetFilter(None)
132  mList = self.cmdPrompt.GetCommandItems()
133  self.searchChoice.SetItems(mList)
134  if self.showTip:
135  self.searchTip.SetLabel(_("%d modules found") % len(mList))
136  event.Skip()
137  return
138 
139  modules = dict()
140  iFound = 0
141  for module, data in self.cmdPrompt.moduleDesc.iteritems():
142  found = False
143  sel = self.searchBy.GetSelection()
144  if sel == 0: # -> description
145  if text in data['desc']:
146  found = True
147  elif sel == 1: # keywords
148  if text in ','.join(data['keywords']):
149  found = True
150  else: # command
151  if module[:len(text)] == text:
152  found = True
153 
154  if found:
155  iFound += 1
156  try:
157  group, name = module.split('.')
158  except ValueError:
159  continue # TODO
160 
161  if group not in modules:
162  modules[group] = list()
163  modules[group].append(name)
164 
165  self.cmdPrompt.SetFilter(modules)
166  self.searchChoice.SetItems(self.cmdPrompt.GetCommandItems())
167  if self.showTip:
168  self.searchTip.SetLabel(_("%d modules found") % iFound)
169 
170  event.Skip()
171 
172  def OnSelectModule(self, event):
173  """!Module selected from choice, update command prompt"""
174  cmd = event.GetString().split(' ', 1)[0]
175  text = cmd + ' '
176  pos = len(text)
177 
178  if self.cmdPrompt:
179  self.cmdPrompt.SetText(text)
180  self.cmdPrompt.SetSelectionStart(pos)
181  self.cmdPrompt.SetCurrentPos(pos)
182  self.cmdPrompt.SetFocus()
183 
184  desc = self.cmdPrompt.GetCommandDesc(cmd)
185  if self.showTip:
186  self.searchTip.SetLabel(desc)
187 
188  def Reset(self):
189  """!Reset widget"""
190  self.searchBy.SetSelection(0)
191  self.search.SetValue('')
192  if self.showTip:
193  self.searchTip.SetLabel('')
194 
195 class MenuTreeWindow(wx.Panel):
196  """!Show menu tree"""
197  def __init__(self, parent, id = wx.ID_ANY, **kwargs):
198  self.parent = parent # LayerManager
199 
200  wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
201 
202  self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
203  label = " %s " % _("Menu tree (double-click to run command)"))
204  # tree
205  self.tree = MenuTree(parent = self, data = ManagerData())
206  self.tree.Load()
207 
208  # search widget
209  self.search = SearchModuleWindow(parent = self, showChoice = False)
210 
211  # buttons
212  self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("&Run"))
213  self.btnRun.SetToolTipString(_("Run selected command"))
214  self.btnRun.Enable(False)
215 
216  # bindings
217  self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun)
218  self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
219  self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
220  self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
221  self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
222 
223  self._layout()
224 
225  self.search.SetFocus()
226 
227  def _layout(self):
228  """!Do dialog layout"""
229  sizer = wx.BoxSizer(wx.VERTICAL)
230 
231  # body
232  dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
233  dataSizer.Add(item = self.tree, proportion =1,
234  flag = wx.EXPAND)
235 
236  # buttons
237  btnSizer = wx.BoxSizer(wx.HORIZONTAL)
238  btnSizer.Add(item = self.btnRun, proportion = 0)
239 
240  sizer.Add(item = dataSizer, proportion = 1,
241  flag = wx.EXPAND | wx.ALL, border = 5)
242 
243  sizer.Add(item = self.search, proportion = 0,
244  flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
245 
246  sizer.Add(item = btnSizer, proportion = 0,
247  flag = wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border = 5)
248 
249  sizer.Fit(self)
250  sizer.SetSizeHints(self)
251 
252  self.SetSizer(sizer)
253 
254  self.Fit()
255  self.SetAutoLayout(True)
256  self.Layout()
257 
258  def OnCloseWindow(self, event):
259  """!Close window"""
260  self.Destroy()
261 
262  def OnRun(self, event):
263  """!Run selected command"""
264  if not self.tree.GetSelected():
265  return # should not happen
266 
267  data = self.tree.GetPyData(self.tree.GetSelected())
268  if not data:
269  return
270 
271  handler = 'self.parent.' + data['handler'].lstrip('self.')
272  if data['handler'] == 'self.OnXTerm':
273  wx.MessageBox(parent = self,
274  message = _('You must run this command from the menu or command line',
275  'This command require an XTerm'),
276  caption = _('Message'), style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
277  elif data['command']:
278  eval(handler)(event = None, cmd = data['command'].split())
279  else:
280  eval(handler)(None)
281 
282  def OnShowItem(self, event):
283  """!Show selected item"""
284  self.tree.OnShowItem(event)
285  if self.tree.GetSelected():
286  self.btnRun.Enable()
287  else:
288  self.btnRun.Enable(False)
289 
290  def OnItemActivated(self, event):
291  """!Item activated (double-click)"""
292  item = event.GetItem()
293  if not item or not item.IsOk():
294  return
295 
296  data = self.tree.GetPyData(item)
297  if not data or 'command' not in data:
298  return
299 
300  self.tree.itemSelected = item
301 
302  self.OnRun(None)
303 
304  def OnItemSelected(self, event):
305  """!Item selected"""
306  item = event.GetItem()
307  if not item or not item.IsOk():
308  return
309 
310  data = self.tree.GetPyData(item)
311  if not data or 'command' not in data:
312  return
313 
314  if data['command']:
315  label = data['command'] + ' -- ' + data['description']
316  else:
317  label = data['description']
318 
319  self.parent.SetStatusText(label, 0)
320 
321  def OnUpdateStatusBar(self, event):
322  """!Update statusbar text"""
323  element = self.search.GetSelection()
324  self.tree.SearchItems(element = element,
325  value = event.GetString())
326 
327  nItems = len(self.tree.itemsMarked)
328  if event.GetString():
329  self.parent.SetStatusText(_("%d modules match") % nItems, 0)
330  else:
331  self.parent.SetStatusText("", 0)
332 
333  event.Skip()
334 
335 class MenuTree(ItemTree):
336  """!Menu tree class"""
337  def __init__(self, parent, data, **kwargs):
338  self.parent = parent
339  self.menudata = data
340 
341  super(MenuTree, self).__init__(parent, **kwargs)
342 
343  def Load(self, data = None):
344  """!Load menu data tree
345 
346  @param data menu data (None to use self.menudata)
347  """
348  if not data:
349  data = self.menudata
350 
351  self.itemsMarked = [] # list of marked items
352  for eachMenuData in data.GetMenu():
353  for label, items in eachMenuData:
354  item = self.AppendItem(parentId = self.root,
355  text = label.replace('&', ''))
356  self.__AppendItems(item, items)
357 
358  def __AppendItems(self, item, data):
359  """!Append items into tree (used by Load()
360 
361  @param item tree item (parent)
362  @parent data menu data"""
363  for eachItem in data:
364  if len(eachItem) == 2:
365  if eachItem[0]:
366  itemSub = self.AppendItem(parentId = item,
367  text = eachItem[0])
368  self.__AppendItems(itemSub, eachItem[1])
369  else:
370  if eachItem[0]:
371  itemNew = self.AppendItem(parentId = item,
372  text = eachItem[0])
373 
374  data = { 'item' : eachItem[0],
375  'description' : eachItem[1],
376  'handler' : eachItem[2],
377  'command' : eachItem[3],
378  'keywords' : eachItem[4] }
379 
380  self.SetPyData(itemNew, data)
381 
382 class AboutWindow(wx.Frame):
383  """!Create custom About Window
384  """
385  def __init__(self, parent, size = (650, 460),
386  title = _('About GRASS GIS'), **kwargs):
387  wx.Frame.__init__(self, parent = parent, id = wx.ID_ANY, title = title, size = size, **kwargs)
388 
389  panel = wx.Panel(parent = self, id = wx.ID_ANY)
390 
391  # icon
392  self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
393 
394  # get version and web site
395  vInfo = grass.version()
396 
397  infoTxt = ScrolledPanel(parent = panel)
398  infoTxt.SetupScrolling()
399  infoSizer = wx.BoxSizer(wx.VERTICAL)
400  infoGridSizer = wx.GridBagSizer(vgap = 5, hgap = 5)
401  infoGridSizer.SetCols(2)
402  logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass-64x64.png")
403  logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
404  bitmap = wx.Bitmap(name = logo,
405  type = wx.BITMAP_TYPE_PNG))
406  infoSizer.Add(item = logoBitmap, proportion = 0,
407  flag = wx.ALL | wx.ALIGN_CENTER, border = 20)
408 
409  info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
410  label = 'GRASS GIS ' + vInfo['version'] + '\n\n')
411  info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
412  info.SetForegroundColour(wx.Colour(35, 142, 35))
413  infoSizer.Add(item = info, proportion = 0,
414  flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 1)
415 
416  row = 0
417  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
418  label = _('Official GRASS site:')),
419  pos = (row, 0),
420  flag = wx.ALIGN_RIGHT)
421 
422  infoGridSizer.Add(item = HyperLinkCtrl(parent = infoTxt, id = wx.ID_ANY,
423  label = 'http://grass.osgeo.org'),
424  pos = (row, 1),
425  flag = wx.ALIGN_LEFT)
426 
427  row += 2
428  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
429  label = '%s:' % _('SVN Revision')),
430  pos = (row, 0),
431  flag = wx.ALIGN_RIGHT)
432 
433  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
434  label = vInfo['revision']),
435  pos = (row, 1),
436  flag = wx.ALIGN_LEFT)
437 
438  row += 1
439  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
440  label = '%s:' % _('GIS Library Revision')),
441  pos = (row, 0),
442  flag = wx.ALIGN_RIGHT)
443 
444  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
445  label = vInfo['libgis_revision'] + ' (' +
446  vInfo['libgis_date'].split(' ')[0] + ')'),
447  pos = (row, 1),
448  flag = wx.ALIGN_LEFT)
449 
450  row += 2
451  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
452  label = 'Python:'),
453  pos = (row, 0),
454  flag = wx.ALIGN_RIGHT)
455 
456  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
457  label = platform.python_version()),
458  pos = (row, 1),
459  flag = wx.ALIGN_LEFT)
460 
461  row += 1
462  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
463  label = 'wxPython:'),
464  pos = (row, 0),
465  flag = wx.ALIGN_RIGHT)
466 
467  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
468  label = wx.__version__),
469  pos = (row, 1),
470  flag = wx.ALIGN_LEFT)
471  infoGridSizer.AddGrowableCol(0)
472  infoGridSizer.AddGrowableCol(1)
473 
474  infoSizer.Add(item = infoGridSizer,
475  proportion = 1,
476  flag = wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL)
477 
478  row += 2
479  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
480  label = "%s:" % _('Language')),
481  pos = (row, 0),
482  flag = wx.ALIGN_RIGHT)
483  lang = grass.gisenv().get('LANG', None)
484  if not lang:
485  import locale
486  loc = locale.getdefaultlocale()
487  if loc == (None, None):
488  lang = _('unknown')
489  else:
490  lang = u'%s.%s' % (loc[0], loc[1])
491  infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
492  label = lang),
493  pos = (row, 1),
494  flag = wx.ALIGN_LEFT)
495 
496  # create a flat notebook for displaying information about GRASS
497  aboutNotebook = GNotebook(panel, style = globalvar.FNPageStyle | FN.FNB_NO_X_BUTTON)
498  aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
499 
500  for title, win in ((_("Info"), infoTxt),
501  (_("Copyright"), self._pageCopyright()),
502  (_("License"), self._pageLicense()),
503  (_("Authors"), self._pageCredit()),
504  (_("Contributors"), self._pageContributors()),
505  (_("Extra contributors"), self._pageContributors(extra = True)),
506  (_("Translators"), self._pageTranslators())):
507  aboutNotebook.AddPage(page = win, text = title)
508  wx.CallAfter(aboutNotebook.SetSelection, 0)
509 
510  # buttons
511  btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
512  btnSizer = wx.BoxSizer(wx.HORIZONTAL)
513  btnSizer.Add(item = btnClose, proportion = 0,
514  flag = wx.ALL | wx.ALIGN_RIGHT,
515  border = 5)
516  # bindings
517  btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
518 
519  infoTxt.SetSizer(infoSizer)
520  infoSizer.Fit(infoTxt)
521 
522  sizer = wx.BoxSizer(wx.VERTICAL)
523  sizer.Add(item = aboutNotebook, proportion = 1,
524  flag = wx.EXPAND | wx.ALL, border = 1)
525  sizer.Add(item = btnSizer, proportion = 0,
526  flag = wx.ALL | wx.ALIGN_RIGHT, border = 1)
527  panel.SetSizer(sizer)
528 
529  self.Layout()
530  self.SetMinSize((400, 400))
531 
532  def _pageCopyright(self):
533  """Copyright information"""
534  copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
535  if os.path.exists(copyfile):
536  copyrightFile = open(copyfile, 'r')
537  copytext = copyrightFile.read()
538  copyrightFile.close()
539  else:
540  copytext = _('%s file missing') % 'COPYING'
541 
542  # put text into a scrolling panel
543  copyrightwin = ScrolledPanel(self)
544 
545  copyrighttxt = wx.StaticText(copyrightwin, id = wx.ID_ANY, label = copytext)
546  copyrightwin.SetAutoLayout(True)
547  copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
548  copyrightwin.sizer.Add(item = copyrighttxt, proportion = 1,
549  flag = wx.EXPAND | wx.ALL, border = 3)
550  copyrightwin.SetSizer(copyrightwin.sizer)
551  copyrightwin.Layout()
552  copyrightwin.SetupScrolling()
553 
554  return copyrightwin
555 
556  def _pageLicense(self):
557  """Licence about"""
558  licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
559  if os.path.exists(licfile):
560  licenceFile = open(licfile, 'r')
561  license = ''.join(licenceFile.readlines())
562  licenceFile.close()
563  else:
564  license = _('%s file missing') % 'GPL.TXT'
565  # put text into a scrolling panel
566  licensewin = ScrolledPanel(self)
567  licensetxt = wx.StaticText(licensewin, id = wx.ID_ANY, label = license)
568  licensewin.SetAutoLayout(True)
569  licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
570  licensewin.sizer.Add(item = licensetxt, proportion = 1,
571  flag = wx.EXPAND | wx.ALL, border = 3)
572  licensewin.SetSizer(licensewin.sizer)
573  licensewin.Layout()
574  licensewin.SetupScrolling()
575 
576  return licensewin
577 
578  def _pageCredit(self):
579  """Credit about"""
580  # credits
581  authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
582  if os.path.exists(authfile):
583  authorsFile = open(authfile, 'r')
584  authors = unicode(''.join(authorsFile.readlines()), "utf-8")
585  authorsFile.close()
586  else:
587  authors = _('%s file missing') % 'AUTHORS'
588  authorwin = ScrolledPanel(self)
589  authortxt = wx.StaticText(authorwin, id = wx.ID_ANY, label = authors)
590  authorwin.SetAutoLayout(True)
591  authorwin.SetupScrolling()
592  authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
593  authorwin.sizer.Add(item = authortxt, proportion = 1,
594  flag = wx.EXPAND | wx.ALL, border = 3)
595  authorwin.SetSizer(authorwin.sizer)
596  authorwin.Layout()
597 
598  return authorwin
599 
600  def _pageContributors(self, extra = False):
601  """Contributors info"""
602  if extra:
603  contribfile = os.path.join(os.getenv("GISBASE"), "contributors_extra.csv")
604  else:
605  contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
606  if os.path.exists(contribfile):
607  contribFile = codecs.open(contribfile, encoding = 'utf-8', mode = 'r')
608  contribs = list()
609  errLines = list()
610  for line in contribFile.readlines()[1:]:
611  line = line.rstrip('\n')
612  try:
613  if extra:
614  name, email, country, rfc2_agreed = line.split(',')
615  else:
616  cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
617  except ValueError:
618  errLines.append(line)
619  continue
620  if extra:
621  contribs.append((name, email))
622  else:
623  contribs.append((name, email, country, osgeo_id))
624 
625  contribFile.close()
626 
627  if errLines:
628  GError(parent = self,
629  message = _("Error when reading file '%s'.") % contribfile + \
630  "\n\n" + _("Lines:") + " %s" % \
631  os.linesep.join(map(DecodeString, errLines)))
632  else:
633  contribs = None
634 
635  contribwin = ScrolledPanel(self)
636  contribwin.SetAutoLayout(True)
637  contribwin.SetupScrolling()
638  contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
639 
640  if not contribs:
641  contribtxt = wx.StaticText(contribwin, id = wx.ID_ANY,
642  label = _('%s file missing') % contribfile)
643  contribwin.sizer.Add(item = contribtxt, proportion = 1,
644  flag = wx.EXPAND | wx.ALL, border = 3)
645  else:
646  if extra:
647  items = (_('Name'), _('E-mail'))
648  else:
649  items = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
650  contribBox = wx.FlexGridSizer(cols = len(items), vgap = 5, hgap = 5)
651  for item in items:
652  contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
653  label = item))
654  for vals in sorted(contribs, key = lambda x: x[0]):
655  for item in vals:
656  contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
657  label = item))
658  contribwin.sizer.Add(item = contribBox, proportion = 1,
659  flag = wx.EXPAND | wx.ALL, border = 3)
660 
661  contribwin.SetSizer(contribwin.sizer)
662  contribwin.Layout()
663 
664  return contribwin
665 
666  def _pageTranslators(self):
667  """Translators info"""
668  translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
669  if os.path.exists(translatorsfile):
670  translatorsFile = open(translatorsfile, 'r')
671  translators = dict()
672  errLines = list()
673  for line in translatorsFile.readlines()[1:]:
674  line = line.rstrip('\n')
675  try:
676  name, email, languages = line.split(',')
677  except ValueError:
678  errLines.append(line)
679  continue
680  for language in languages.split(' '):
681  if language not in translators:
682  translators[language] = list()
683  translators[language].append((name, email))
684  translatorsFile.close()
685 
686  if errLines:
687  GError(parent = self,
688  message = _("Error when reading file '%s'.") % translatorsfile + \
689  "\n\n" + _("Lines:") + " %s" % \
690  os.linesep.join(map(DecodeString, errLines)))
691  else:
692  translators = None
693 
694  translatorswin = ScrolledPanel(self)
695  translatorswin.SetAutoLayout(True)
696  translatorswin.SetupScrolling()
697  translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
698 
699  if not translators:
700  translatorstxt = wx.StaticText(translatorswin, id = wx.ID_ANY,
701  label = _('%s file missing') % 'translators.csv')
702  translatorswin.sizer.Add(item = translatorstxt, proportion = 1,
703  flag = wx.EXPAND | wx.ALL, border = 3)
704  else:
705  translatorsBox = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5)
706  languages = translators.keys()
707  languages.sort()
708  translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
709  label = _('Name')))
710  translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
711  label = _('E-mail')))
712  translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
713  label = _('Language')))
714  for lang in languages:
715  for translator in translators[lang]:
716  name, email = translator
717  translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
718  label = unicode(name, "utf-8")))
719  translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
720  label = email))
721  translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
722  label = lang))
723 
724  translatorswin.sizer.Add(item = translatorsBox, proportion = 1,
725  flag = wx.EXPAND | wx.ALL, border = 3)
726 
727  translatorswin.SetSizer(translatorswin.sizer)
728  translatorswin.Layout()
729 
730  return translatorswin
731 
732  def OnCloseWindow(self, event):
733  """!Close window"""
734  self.Close()
735 
736 class HelpFrame(wx.Dialog):
737  """!GRASS Quickstart help window
738 
739  As a base class wx.Dialog is used, because of not working
740  close button with wx.Frame when dialog is called from wizard.
741  If parent is None, application TopLevelWindow is used (wxPython standard behaviour).
742  """
743  def __init__(self, parent, id, title, size, file):
744  wx.Dialog.__init__(self, parent = parent, id = id, title = title,
745  size = size, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.MINIMIZE_BOX)
746 
747  sizer = wx.BoxSizer(wx.VERTICAL)
748 
749  # text
750  content = HelpPanel(parent = self)
751  content.LoadPage(file)
752 
753  sizer.Add(item = content, proportion = 1, flag = wx.EXPAND)
754 
755  self.SetAutoLayout(True)
756  self.SetSizer(sizer)
757  self.Layout()
758 
759 
760 class HelpWindow(wx.html.HtmlWindow):
761  """!This panel holds the text from GRASS docs.
762 
763  GISBASE must be set in the environment to find the html docs dir.
764  The SYNOPSIS section is skipped, since this Panel is supposed to
765  be integrated into the cmdPanel and options are obvious there.
766  """
767  def __init__(self, parent, grass_command, text, skip_description,
768  **kwargs):
769  """!If grass_command is given, the corresponding HTML help
770  file will be presented, with all links pointing to absolute
771  paths of local files.
772 
773  If 'skip_description' is True, the HTML corresponding to
774  SYNOPSIS will be skipped, thus only presenting the help file
775  from the DESCRIPTION section onwards.
776 
777  If 'text' is given, it must be the HTML text to be presented
778  in the Panel.
779  """
780  self.parent = parent
781  if not globalvar.CheckWxVersion([2, 9]):
782  wx.InitAllImageHandlers()
783  wx.html.HtmlWindow.__init__(self, parent = parent, **kwargs)
784 
785  gisbase = os.getenv("GISBASE")
786  self.loaded = False
787  self.history = list()
788  self.historyIdx = 0
789  self.fspath = os.path.join(gisbase, "docs", "html")
790 
791  self.SetStandardFonts (size = 10)
792  self.SetBorders(10)
793 
794  if text is None:
795  if skip_description:
796  url = os.path.join(self.fspath, grass_command + ".html")
797  self.fillContentsFromFile(url,
798  skip_description = skip_description)
799  self.history.append(url)
800  self.loaded = True
801  else:
802  ### FIXME: calling LoadPage() is strangely time-consuming (only first call)
803  # self.LoadPage(self.fspath + grass_command + ".html")
804  self.loaded = False
805  else:
806  self.SetPage(text)
807  self.loaded = True
808 
809  def OnLinkClicked(self, linkinfo):
810  url = linkinfo.GetHref()
811  if url[:4] != 'http':
812  url = os.path.join(self.fspath, url)
813  self.history.append(url)
814  self.historyIdx += 1
815  self.parent.OnHistory()
816 
817  super(HelpWindow, self).OnLinkClicked(linkinfo)
818 
819  def fillContentsFromFile(self, htmlFile, skip_description = True):
820  """!Load content from file"""
821  aLink = re.compile(r'(<a href="?)(.+\.html?["\s]*>)', re.IGNORECASE)
822  imgLink = re.compile(r'(<img src="?)(.+\.[png|gif])', re.IGNORECASE)
823  try:
824  contents = []
825  skip = False
826  for l in file(htmlFile, "rb").readlines():
827  if "DESCRIPTION" in l:
828  skip = False
829  if not skip:
830  # do skip the options description if requested
831  if "SYNOPSIS" in l:
832  skip = skip_description
833  else:
834  # FIXME: find only first item
835  findALink = aLink.search(l)
836  if findALink is not None:
837  contents.append(aLink.sub(findALink.group(1)+
838  self.fspath+findALink.group(2),l))
839  findImgLink = imgLink.search(l)
840  if findImgLink is not None:
841  contents.append(imgLink.sub(findImgLink.group(1)+
842  self.fspath+findImgLink.group(2),l))
843 
844  if findALink is None and findImgLink is None:
845  contents.append(l)
846  self.SetPage("".join(contents))
847  self.loaded = True
848  except: # The Manual file was not found
849  self.loaded = False
850 
851 class HelpPanel(wx.Panel):
852  def __init__(self, parent, grass_command = "index", text = None,
853  skip_description = False, **kwargs):
854  self.grass_command = grass_command
855  wx.Panel.__init__(self, parent = parent, id = wx.ID_ANY)
856 
857  self.content = HelpWindow(self, grass_command, text,
858  skip_description)
859 
860  self.btnNext = wx.Button(parent = self, id = wx.ID_ANY,
861  label = _("&Next"))
862  self.btnNext.Enable(False)
863  self.btnPrev = wx.Button(parent = self, id = wx.ID_ANY,
864  label = _("&Previous"))
865  self.btnPrev.Enable(False)
866 
867  self.btnNext.Bind(wx.EVT_BUTTON, self.OnNext)
868  self.btnPrev.Bind(wx.EVT_BUTTON, self.OnPrev)
869 
870  self._layout()
871 
872  def _layout(self):
873  """!Do layout"""
874  sizer = wx.BoxSizer(wx.VERTICAL)
875  btnSizer = wx.BoxSizer(wx.HORIZONTAL)
876 
877  btnSizer.Add(item = self.btnPrev, proportion = 0,
878  flag = wx.ALL, border = 5)
879  btnSizer.Add(item = wx.Size(1, 1), proportion = 1)
880  btnSizer.Add(item = self.btnNext, proportion = 0,
881  flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
882 
883  sizer.Add(item = self.content, proportion = 1,
884  flag = wx.EXPAND)
885  sizer.Add(item = btnSizer, proportion = 0,
886  flag = wx.EXPAND)
887 
888  self.SetSizer(sizer)
889  sizer.Fit(self)
890 
891  def LoadPage(self, path = None):
892  """!Load page"""
893  if not path:
894  path = self.GetFile()
895  self.content.history.append(path)
896  self.content.LoadPage(path)
897 
898  def GetFile(self):
899  """!Get HTML file"""
900  fMan = os.path.join(self.content.fspath, self.grass_command + ".html")
901  if os.path.isfile(fMan):
902  return fMan
903 
904  # check also addons
905  aPath = os.getenv('GRASS_ADDON_PATH')
906  if aPath:
907  for path in aPath.split(os.pathsep):
908  faMan = os.path.join(path, "docs", "html",
909  self.grass_command + ".html")
910  if os.path.isfile(faMan):
911  return faMan
912 
913  return None
914 
915  def IsLoaded(self):
916  return self.content.loaded
917 
918  def OnHistory(self):
919  """!Update buttons"""
920  nH = len(self.content.history)
921  iH = self.content.historyIdx
922  if iH == nH - 1:
923  self.btnNext.Enable(False)
924  elif iH > -1:
925  self.btnNext.Enable(True)
926  if iH < 1:
927  self.btnPrev.Enable(False)
928  else:
929  self.btnPrev.Enable(True)
930 
931  def OnNext(self, event):
932  """Load next page"""
933  self.content.historyIdx += 1
934  idx = self.content.historyIdx
935  path = self.content.history[idx]
936  self.content.LoadPage(path)
937  self.OnHistory()
938 
939  event.Skip()
940 
941  def OnPrev(self, event):
942  """Load previous page"""
943  self.content.historyIdx -= 1
944  idx = self.content.historyIdx
945  path = self.content.history[idx]
946  self.content.LoadPage(path)
947  self.OnHistory()
948 
949  event.Skip()
def OnCloseWindow
Close window.
Definition: ghelp.py:258
Search module window (used in MenuTreeWindow)
Definition: ghelp.py:47
wxGUI command interface
def Reset
Reset widget.
Definition: ghelp.py:188
def CheckWxVersion
Check wx version.
Definition: globalvar.py:33
def _pageTranslators
Definition: ghelp.py:666
def GetFile
Get HTML file.
Definition: ghelp.py:898
def LoadPage
Load page.
Definition: ghelp.py:891
Core GUI widgets.
def __AppendItems
Append items into tree (used by Load()
Definition: ghelp.py:358
def _pageCopyright
Definition: ghelp.py:532
def __init__
Definition: ghelp.py:337
Show menu tree.
Definition: ghelp.py:195
def IsLoaded
Definition: ghelp.py:915
def OnHistory
Update buttons.
Definition: ghelp.py:918
def _pageLicense
Definition: ghelp.py:556
Menu tree class.
Definition: ghelp.py:335
def split
Platform spefic shlex.split.
Definition: core/utils.py:37
def OnRun
Run selected command.
Definition: ghelp.py:262
def OnItemActivated
Item activated (double-click)
Definition: ghelp.py:290
Create custom About Window.
Definition: ghelp.py:382
def _layout
Do layout.
Definition: ghelp.py:87
def _layout
Do dialog layout.
Definition: ghelp.py:227
def _layout
Do layout.
Definition: ghelp.py:872
def _pageContributors
Definition: ghelp.py:600
def __init__
Definition: ghelp.py:743
loaded
FIXME: calling LoadPage() is strangely time-consuming (only first call) self.LoadPage(self.fspath + grass_command + ".html")
Definition: ghelp.py:786
This panel holds the text from GRASS docs.
Definition: ghelp.py:760
def __init__
Definition: ghelp.py:853
def Load
Load menu data tree.
Definition: ghelp.py:343
def OnShowItem
Show selected item.
Definition: ghelp.py:282
def SetSelection
Set selection element.
Definition: ghelp.py:119
def OnCloseWindow
Close window.
Definition: ghelp.py:732
def OnItemSelected
Item selected.
Definition: ghelp.py:304
def OnUpdateStatusBar
Update statusbar text.
Definition: ghelp.py:321
def OnSearchModule
Search module by keywords or description.
Definition: ghelp.py:123
GRASS Quickstart help window.
Definition: ghelp.py:736
def fillContentsFromFile
Load content from file.
Definition: ghelp.py:819
def OnSelectModule
Module selected from choice, update command prompt.
Definition: ghelp.py:172
def __init__
If grass_command is given, the corresponding HTML help file will be presented, with all links pointin...
Definition: ghelp.py:768
def OnLinkClicked
Definition: ghelp.py:809
def GetSelection
Get selected element.
Definition: ghelp.py:113