help.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  1. """!
  2. @package help.py
  3. @brief Help window
  4. Classes:
  5. - HelpWindow
  6. - SearchModuleWindow
  7. - ItemTree
  8. - MenuTreeWindow
  9. - MenuTree
  10. - AboutWindow
  11. - InstallExtensionWindow
  12. - ExtensionTree
  13. (C) 2008-2010 by the GRASS Development Team
  14. This program is free software under the GNU General Public License
  15. (>=v2). Read the file COPYING that comes with GRASS for details.
  16. @author Martin Landa <landa.martin gmail.com>
  17. """
  18. import os
  19. import wx
  20. try:
  21. import wx.lib.agw.customtreectrl as CT
  22. # import wx.lib.agw.hyperlink as hl
  23. except ImportError:
  24. import wx.lib.customtreectrl as CT
  25. # import wx.lib.hyperlink as hl
  26. import wx.lib.flatnotebook as FN
  27. import wx.lib.scrolledpanel as scrolled
  28. import menudata
  29. import gcmd
  30. import globalvar
  31. class HelpWindow(wx.Frame):
  32. """!GRASS Quickstart help window"""
  33. def __init__(self, parent, id, title, size, file):
  34. wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
  35. sizer = wx.BoxSizer(wx.VERTICAL)
  36. # text
  37. helpFrame = wx.html.HtmlWindow(parent=self, id=wx.ID_ANY)
  38. helpFrame.SetStandardFonts (size = 10)
  39. helpFrame.SetBorders(10)
  40. wx.InitAllImageHandlers()
  41. helpFrame.LoadFile(file)
  42. self.Ok = True
  43. sizer.Add(item=helpFrame, proportion=1, flag=wx.EXPAND)
  44. self.SetAutoLayout(True)
  45. self.SetSizer(sizer)
  46. # sizer.Fit(self)
  47. # sizer.SetSizeHints(self)
  48. self.Layout()
  49. class SearchModuleWindow(wx.Panel):
  50. """!Search module window (used in MenuTreeWindow)"""
  51. def __init__(self, parent, id = wx.ID_ANY, showLabel = True, **kwargs):
  52. self.showLabel = showLabel
  53. wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
  54. self._searchDict = { _('description') : 'description',
  55. _('command') : 'command',
  56. _('keywords') : 'keywords' }
  57. self.searchBy = wx.Choice(parent = self, id = wx.ID_ANY,
  58. choices = [_('description'),
  59. _('keywords'),
  60. _('command')])
  61. self.searchBy.SetSelection(0)
  62. self.search = wx.TextCtrl(parent = self, id = wx.ID_ANY,
  63. value = "", size = (-1, 25),
  64. style = wx.TE_PROCESS_ENTER)
  65. self._layout()
  66. def _layout(self):
  67. """!Do layout"""
  68. # search
  69. sizer = wx.BoxSizer(wx.HORIZONTAL)
  70. if self.showLabel:
  71. sizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
  72. label = _("Find module by:")),
  73. proportion = 0,
  74. flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
  75. border = 3)
  76. sizer.Add(item = self.searchBy, proportion = 0,
  77. flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT,
  78. border = 5)
  79. sizer.Add(item = self.search, proportion = 1,
  80. flag = wx.EXPAND | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL,
  81. border = 5)
  82. self.SetSizer(sizer)
  83. sizer.Fit(self)
  84. def GetSelection(self):
  85. """!Get selected element"""
  86. selection = self.searchBy.GetStringSelection()
  87. return self._searchDict[selection]
  88. def SetSelection(self, i):
  89. """!Set selection element"""
  90. self.searchBy.SetSelection(i)
  91. class MenuTreeWindow(wx.Panel):
  92. """!Show menu tree"""
  93. def __init__(self, parent, id = wx.ID_ANY, **kwargs):
  94. self.parent = parent # LayerManager
  95. wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
  96. self.dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
  97. label=" %s " % _("Menu tree (double-click to run command)"))
  98. # tree
  99. self.tree = MenuTree(parent = self, data = menudata.ManagerData())
  100. self.tree.Load()
  101. # search widget
  102. self.search = SearchModuleWindow(parent = self)
  103. # buttons
  104. self.btnRun = wx.Button(self, id = wx.ID_OK, label = _("Run"))
  105. self.btnRun.SetToolTipString(_("Run selected command"))
  106. self.btnRun.Enable(False)
  107. # bindings
  108. self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun)
  109. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  110. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  111. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  112. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  113. self._layout()
  114. self.search.SetFocus()
  115. def _layout(self):
  116. """!Do dialog layout"""
  117. sizer = wx.BoxSizer(wx.VERTICAL)
  118. # body
  119. dataSizer = wx.StaticBoxSizer(self.dataBox, wx.HORIZONTAL)
  120. dataSizer.Add(item = self.tree, proportion =1,
  121. flag = wx.EXPAND)
  122. # buttons
  123. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  124. btnSizer.Add(item = self.btnRun, proportion = 0,
  125. flag = wx.LEFT | wx.RIGHT, border = 5)
  126. sizer.Add(item = dataSizer, proportion = 1,
  127. flag = wx.EXPAND | wx.ALL, border = 5)
  128. sizer.Add(item = self.search, proportion=0,
  129. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  130. sizer.Add(item = btnSizer, proportion=0,
  131. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  132. sizer.Fit(self)
  133. sizer.SetSizeHints(self)
  134. self.SetSizer(sizer)
  135. self.Fit()
  136. self.SetAutoLayout(True)
  137. self.Layout()
  138. def OnCloseWindow(self, event):
  139. """!Close window"""
  140. self.Destroy()
  141. def OnRun(self, event):
  142. """!Run selected command"""
  143. if not self.tree.GetSelected():
  144. return # should not happen
  145. data = self.tree.GetPyData(self.tree.GetSelected())
  146. if not data:
  147. return
  148. handler = 'self.parent.' + data['handler'].lstrip('self.')
  149. if data['handler'] == 'self.OnXTerm':
  150. wx.MessageBox(parent = self,
  151. message = _('You must run this command from the menu or command line',
  152. 'This command require an XTerm'),
  153. caption = _('Message'), style=wx.OK | wx.ICON_ERROR | wx.CENTRE)
  154. elif data['command']:
  155. eval(handler)(event = None, cmd = data['command'].split())
  156. else:
  157. eval(handler)(None)
  158. def OnShowItem(self, event):
  159. """!Show selected item"""
  160. self.tree.OnShowItem(event)
  161. if self.tree.GetSelected():
  162. self.btnRun.Enable()
  163. else:
  164. self.btnRun.Enable(False)
  165. def OnItemActivated(self, event):
  166. """!Item activated (double-click)"""
  167. item = event.GetItem()
  168. if not item or not item.IsOk():
  169. return
  170. data = self.tree.GetPyData(item)
  171. if not data or not data.has_key('command'):
  172. return
  173. self.tree.itemSelected = item
  174. self.OnRun(None)
  175. def OnItemSelected(self, event):
  176. """!Item selected"""
  177. item = event.GetItem()
  178. if not item or not item.IsOk():
  179. return
  180. data = self.tree.GetPyData(item)
  181. if not data or not data.has_key('command'):
  182. return
  183. if data['command']:
  184. label = data['command'] + ' -- ' + data['description']
  185. else:
  186. label = data['description']
  187. self.parent.SetStatusText(label, 0)
  188. def OnUpdateStatusBar(self, event):
  189. """!Update statusbar text"""
  190. element = self.search.GetSelection()
  191. self.tree.SearchItems(element = element,
  192. value = event.GetString())
  193. nItems = len(self.tree.itemsMarked)
  194. if event.GetString():
  195. self.parent.SetStatusText(_("%d items match") % nItems, 0)
  196. else:
  197. self.parent.SetStatusText("", 0)
  198. event.Skip()
  199. class ItemTree(CT.CustomTreeCtrl):
  200. def __init__(self, parent, id = wx.ID_ANY,
  201. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
  202. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE, **kwargs):
  203. super(ItemTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  204. self.root = self.AddRoot(_("Menu tree"))
  205. self.itemsMarked = [] # list of marked items
  206. self.itemSelected = None
  207. def SearchItems(self, element, value):
  208. """!Search item
  209. @param element element index (see self.searchBy)
  210. @param value
  211. @return list of found tree items
  212. """
  213. items = list()
  214. if not value:
  215. return items
  216. item = self.GetFirstChild(self.root)[0]
  217. self._processItem(item, element, value, items)
  218. self.itemsMarked = items
  219. self.itemSelected = None
  220. return items
  221. def _processItem(self, item, element, value, listOfItems):
  222. """!Search items (used by SearchItems)
  223. @param item reference item
  224. @param listOfItems list of found items
  225. """
  226. while item and item.IsOk():
  227. subItem = self.GetFirstChild(item)[0]
  228. if subItem:
  229. self._processItem(subItem, element, value, listOfItems)
  230. data = self.GetPyData(item)
  231. if data and data.has_key(element) and \
  232. value.lower() in data[element].lower():
  233. listOfItems.append(item)
  234. item = self.GetNextSibling(item)
  235. def GetSelected(self):
  236. """!Get selected item"""
  237. return self.itemSelected
  238. def OnShowItem(self, event):
  239. """!Highlight first found item in menu tree"""
  240. if len(self.itemsMarked) > 0:
  241. if self.GetSelected():
  242. self.ToggleItemSelection(self.GetSelected())
  243. idx = self.itemsMarked.index(self.GetSelected()) + 1
  244. else:
  245. idx = 0
  246. try:
  247. self.ToggleItemSelection(self.itemsMarked[idx])
  248. self.itemSelected = self.itemsMarked[idx]
  249. self.EnsureVisible(self.itemsMarked[idx])
  250. except IndexError:
  251. self.ToggleItemSelection(self.itemsMarked[0]) # reselect first item
  252. self.EnsureVisible(self.itemsMarked[0])
  253. self.itemSelected = self.itemsMarked[0]
  254. else:
  255. for item in self.root.GetChildren():
  256. self.Collapse(item)
  257. itemSelected = self.GetSelection()
  258. if itemSelected:
  259. self.ToggleItemSelection(itemSelected)
  260. self.itemSelected = None
  261. class MenuTree(ItemTree):
  262. """!Menu tree class"""
  263. def __init__(self, parent, data, **kwargs):
  264. self.parent = parent
  265. self.menudata = data
  266. super(MenuTree, self).__init__(parent, **kwargs)
  267. def Load(self, data = None):
  268. """!Load menu data tree
  269. @param data menu data (None to use self.menudata)
  270. """
  271. if not data:
  272. data = self.menudata
  273. self.itemsMarked = [] # list of marked items
  274. for eachMenuData in data.GetMenu():
  275. for label, items in eachMenuData:
  276. item = self.AppendItem(parentId = self.root,
  277. text = label.replace('&', ''))
  278. self.__AppendItems(item, items)
  279. def __AppendItems(self, item, data):
  280. """!Append items into tree (used by Load()
  281. @param item tree item (parent)
  282. @parent data menu data"""
  283. for eachItem in data:
  284. if len(eachItem) == 2:
  285. if eachItem[0]:
  286. itemSub = self.AppendItem(parentId = item,
  287. text = eachItem[0])
  288. self.__AppendItems(itemSub, eachItem[1])
  289. else:
  290. if eachItem[0]:
  291. itemNew = self.AppendItem(parentId = item,
  292. text = eachItem[0])
  293. data = { 'item' : eachItem[0],
  294. 'description' : eachItem[1],
  295. 'handler' : eachItem[2],
  296. 'command' : eachItem[3],
  297. 'keywords' : eachItem[4] }
  298. self.SetPyData(itemNew, data)
  299. class AboutWindow(wx.Frame):
  300. def __init__(self, parent):
  301. """!Create custom About Window
  302. @todo improve styling
  303. """
  304. wx.Frame.__init__(self, parent=parent, id=wx.ID_ANY, size=(550,400),
  305. title=_('About GRASS GIS'))
  306. panel = wx.Panel(parent = self, id = wx.ID_ANY)
  307. # icon
  308. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  309. # get version and web site
  310. version, svn_gis_h_rev, svn_gis_h_date = gcmd.RunCommand('g.version',
  311. flags = 'r',
  312. read = True).splitlines()
  313. infoTxt = wx.Panel(parent = panel, id = wx.ID_ANY)
  314. infoSizer = wx.BoxSizer(wx.VERTICAL)
  315. infoGridSizer = wx.GridBagSizer(vgap=5, hgap=5)
  316. infoGridSizer.AddGrowableCol(0)
  317. infoGridSizer.AddGrowableCol(1)
  318. logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
  319. logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
  320. bitmap = wx.Bitmap(name = logo,
  321. type = wx.BITMAP_TYPE_ICO))
  322. infoSizer.Add(item = logoBitmap, proportion = 0,
  323. flag = wx.ALL | wx.ALIGN_CENTER, border = 25)
  324. info = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  325. label = version.replace('GRASS', 'GRASS GIS').strip() + '\n\n')
  326. info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  327. infoSizer.Add(item = info, proportion = 0,
  328. flag = wx.BOTTOM | wx.ALIGN_CENTER, border = 15)
  329. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  330. label = _('Official GRASS site:')),
  331. pos = (0, 0),
  332. flag = wx.ALIGN_RIGHT)
  333. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  334. label = 'http://grass.osgeo.org'),
  335. pos = (0, 1),
  336. flag = wx.ALIGN_LEFT)
  337. # infoGridSizer.Add(item = hl.HyperLinkCtrl(parent = self, id = wx.ID_ANY,
  338. # label = 'http://grass.osgeo.org',
  339. # URL = 'http://grass.osgeo.org'),
  340. # pos = (0, 1),
  341. # flag = wx.LEFT)
  342. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  343. label = _('GIS Library Revision:')),
  344. pos = (2, 0),
  345. flag = wx.ALIGN_RIGHT)
  346. infoGridSizer.Add(item = wx.StaticText(parent = infoTxt, id = wx.ID_ANY,
  347. label = svn_gis_h_rev.split(' ')[1] + ' (' +
  348. svn_gis_h_date.split(' ')[1] + ')'),
  349. pos = (2, 1),
  350. flag = wx.ALIGN_LEFT)
  351. infoSizer.Add(item = infoGridSizer,
  352. proportion = 1,
  353. flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL,
  354. border = 25)
  355. #
  356. # create pages
  357. #
  358. copyrightwin = self.PageCopyright()
  359. licensewin = self.PageLicense()
  360. authorwin = self.PageCredit()
  361. contribwin = self.PageContributors()
  362. transwin = self.PageTranslators()
  363. # create a flat notebook for displaying information about GRASS
  364. nbstyle = FN.FNB_VC8 | \
  365. FN.FNB_BACKGROUND_GRADIENT | \
  366. FN.FNB_TABS_BORDER_SIMPLE | \
  367. FN.FNB_NO_X_BUTTON
  368. aboutNotebook = FN.FlatNotebook(panel, id=wx.ID_ANY, style=nbstyle)
  369. aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
  370. # make pages for About GRASS notebook
  371. pg1 = aboutNotebook.AddPage(infoTxt, text=_("Info"))
  372. pg2 = aboutNotebook.AddPage(copyrightwin, text=_("Copyright"))
  373. pg3 = aboutNotebook.AddPage(licensewin, text=_("License"))
  374. pg4 = aboutNotebook.AddPage(authorwin, text=_("Authors"))
  375. pg5 = aboutNotebook.AddPage(contribwin, text=_("Contributors"))
  376. pg5 = aboutNotebook.AddPage(transwin, text=_("Translators"))
  377. wx.CallAfter(aboutNotebook.SetSelection, 0)
  378. # buttons
  379. btnClose = wx.Button(parent = panel, id = wx.ID_CLOSE)
  380. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  381. btnSizer.Add(item = btnClose, proportion = 0,
  382. flag = wx.ALL | wx.ALIGN_RIGHT,
  383. border = 5)
  384. # bindings
  385. # self.aboutNotebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnAGPageChanged)
  386. btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  387. infoTxt.SetSizer(infoSizer)
  388. infoSizer.Fit(infoTxt)
  389. sizer = wx.BoxSizer(wx.VERTICAL)
  390. sizer.Add(item=aboutNotebook, proportion=1,
  391. flag=wx.EXPAND | wx.ALL, border=1)
  392. sizer.Add(item=btnSizer, proportion=0,
  393. flag=wx.ALL | wx.ALIGN_RIGHT, border=1)
  394. panel.SetSizer(sizer)
  395. self.Layout()
  396. def PageCopyright(self):
  397. """Copyright information"""
  398. copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
  399. if os.path.exists(copyfile):
  400. copyrightFile = open(copyfile, 'r')
  401. copytext = copyrightFile.read()
  402. copyrightFile.close()
  403. else:
  404. copytext = _('%s file missing') % 'COPYING'
  405. # put text into a scrolling panel
  406. copyrightwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  407. size=wx.DefaultSize,
  408. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  409. copyrighttxt = wx.StaticText(copyrightwin, id=wx.ID_ANY, label=copytext)
  410. copyrightwin.SetAutoLayout(True)
  411. copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
  412. copyrightwin.sizer.Add(item=copyrighttxt, proportion=1,
  413. flag=wx.EXPAND | wx.ALL, border=3)
  414. copyrightwin.SetSizer(copyrightwin.sizer)
  415. copyrightwin.Layout()
  416. copyrightwin.SetupScrolling()
  417. return copyrightwin
  418. def PageLicense(self):
  419. """Licence about"""
  420. licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
  421. if os.path.exists(licfile):
  422. licenceFile = open(licfile, 'r')
  423. license = ''.join(licenceFile.readlines())
  424. licenceFile.close()
  425. else:
  426. license = _('%s file missing') % 'GPL.TXT'
  427. # put text into a scrolling panel
  428. licensewin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  429. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  430. licensetxt = wx.StaticText(licensewin, id=wx.ID_ANY, label=license)
  431. licensewin.SetAutoLayout(True)
  432. licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
  433. licensewin.sizer.Add(item=licensetxt, proportion=1,
  434. flag=wx.EXPAND | wx.ALL, border=3)
  435. licensewin.SetSizer(licensewin.sizer)
  436. licensewin.Layout()
  437. licensewin.SetupScrolling()
  438. return licensewin
  439. def PageCredit(self):
  440. """Credit about"""
  441. # credits
  442. authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
  443. if os.path.exists(authfile):
  444. authorsFile = open(authfile, 'r')
  445. authors = unicode(''.join(authorsFile.readlines()), "utf-8")
  446. authorsFile.close()
  447. else:
  448. authors = _('%s file missing') % 'AUTHORS'
  449. authorwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  450. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  451. authortxt = wx.StaticText(authorwin, id=wx.ID_ANY, label=authors)
  452. authorwin.SetAutoLayout(1)
  453. authorwin.SetupScrolling()
  454. authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
  455. authorwin.sizer.Add(item=authortxt, proportion=1,
  456. flag=wx.EXPAND | wx.ALL, border=3)
  457. authorwin.SetSizer(authorwin.sizer)
  458. authorwin.Layout()
  459. return authorwin
  460. def PageContributors(self):
  461. """Contributors info"""
  462. contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
  463. if os.path.exists(contribfile):
  464. contribFile = open(contribfile, 'r')
  465. contribs = list()
  466. for line in contribFile.readlines():
  467. cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
  468. contribs.append((name, email, country, osgeo_id))
  469. contribs[0] = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
  470. contribFile.close()
  471. else:
  472. contribs = None
  473. contribwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  474. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  475. contribwin.SetAutoLayout(1)
  476. contribwin.SetupScrolling()
  477. contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
  478. if not contribs:
  479. contribtxt = wx.StaticText(contribwin, id=wx.ID_ANY,
  480. label=_('%s file missing') % 'contibutors.csv')
  481. contribwin.sizer.Add(item=contribtxt, proportion=1,
  482. flag=wx.EXPAND | wx.ALL, border=3)
  483. else:
  484. contribBox = wx.FlexGridSizer(cols=4, vgap=5, hgap=5)
  485. for developer in contribs:
  486. for item in developer:
  487. contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
  488. label = item))
  489. contribwin.sizer.Add(item=contribBox, proportion=1,
  490. flag=wx.EXPAND | wx.ALL, border=3)
  491. contribwin.SetSizer(contribwin.sizer)
  492. contribwin.Layout()
  493. return contribwin
  494. def PageTranslators(self):
  495. """Translators info"""
  496. translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
  497. if os.path.exists(translatorsfile):
  498. translatorsFile = open(translatorsfile, 'r')
  499. translators = dict()
  500. for line in translatorsFile.readlines()[1:]:
  501. name, email, languages = line.rstrip('\n').split(',')
  502. for language in languages.split(' '):
  503. if not translators.has_key(language):
  504. translators[language] = list()
  505. translators[language].append((name, email))
  506. translatorsFile.close()
  507. else:
  508. translators = None
  509. translatorswin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  510. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  511. translatorswin.SetAutoLayout(1)
  512. translatorswin.SetupScrolling()
  513. translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
  514. if not translators:
  515. translatorstxt = wx.StaticText(translatorswin, id=wx.ID_ANY,
  516. label=_('%s file missing') % 'translators.csv')
  517. translatorswin.sizer.Add(item=translatorstxt, proportion=1,
  518. flag=wx.EXPAND | wx.ALL, border=3)
  519. else:
  520. translatorsBox = wx.FlexGridSizer(cols=3, vgap=5, hgap=5)
  521. languages = translators.keys()
  522. languages.sort()
  523. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  524. label = _('Name')))
  525. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  526. label = _('E-mail')))
  527. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  528. label = _('Language')))
  529. for lang in languages:
  530. for translator in translators[lang]:
  531. name, email = translator
  532. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  533. label = unicode(name, "utf-8")))
  534. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  535. label = email))
  536. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  537. label = lang))
  538. translatorswin.sizer.Add(item=translatorsBox, proportion=1,
  539. flag=wx.EXPAND | wx.ALL, border=3)
  540. translatorswin.SetSizer(translatorswin.sizer)
  541. translatorswin.Layout()
  542. return translatorswin
  543. def OnCloseWindow(self, event):
  544. """!Close window"""
  545. self.Close()
  546. class InstallExtensionWindow(wx.Frame):
  547. def __init__(self, parent, id = wx.ID_ANY,
  548. title = _("Fetch & install new extension from GRASS Addons"), **kwargs):
  549. self.parent = parent
  550. wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
  551. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  552. self.repoBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  553. label=" %s " % _("Repository"))
  554. self.findBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  555. label=" %s " % _("Find extension by"))
  556. self.treeBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  557. label=" %s " % _("List of extensions"))
  558. self.repo = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
  559. value = 'https://svn.osgeo.org/grass/grass-addons')
  560. self.fullDesc = wx.CheckBox(parent = self.panel, id=wx.ID_ANY,
  561. label = _("Fetch full info including description and keywords (takes time)"))
  562. self.fullDesc.SetValue(False)
  563. self.search = SearchModuleWindow(parent = self.panel, showLabel = False)
  564. self.search.SetSelection(2)
  565. self.tree = ExtensionTree(parent = self.panel, log = parent.GetLogWindow())
  566. self.statusbar = self.CreateStatusBar(0)
  567. self.btnFetch = wx.Button(parent = self.panel, id = wx.ID_ANY,
  568. label = _("&Fetch"))
  569. self.btnFetch.SetToolTipString(_("Fetch list of available modules from GRASS Addons SVN repository"))
  570. self.btnClose = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
  571. self.btnInstall = wx.Button(parent = self.panel, id = wx.ID_ANY,
  572. label = _("&Install"))
  573. self.btnInstall.SetToolTipString(_("Install selected add-ons GRASS module"))
  574. self.btnInstall.Enable(False)
  575. self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  576. self.btnFetch.Bind(wx.EVT_BUTTON, self.OnFetch)
  577. self.btnInstall.Bind(wx.EVT_BUTTON, self.OnInstall)
  578. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  579. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  580. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  581. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  582. self._layout()
  583. def _layout(self):
  584. """!Do layout"""
  585. sizer = wx.BoxSizer(wx.VERTICAL)
  586. repoSizer = wx.StaticBoxSizer(self.repoBox, wx.VERTICAL)
  587. repo1Sizer = wx.BoxSizer(wx.HORIZONTAL)
  588. repo1Sizer.Add(item = self.repo, proportion = 1,
  589. flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
  590. repo1Sizer.Add(item = self.btnFetch, proportion = 0,
  591. flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL, border = 1)
  592. repoSizer.Add(item = repo1Sizer,
  593. flag = wx.EXPAND)
  594. repoSizer.Add(item = self.fullDesc)
  595. findSizer = wx.StaticBoxSizer(self.findBox, wx.HORIZONTAL)
  596. findSizer.Add(item = self.search, proportion = 1)
  597. treeSizer = wx.StaticBoxSizer(self.treeBox, wx.HORIZONTAL)
  598. treeSizer.Add(item = self.tree, proportion = 1,
  599. flag = wx.ALL | wx.EXPAND, border = 1)
  600. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  601. btnSizer.Add(item = self.btnClose, proportion = 0,
  602. flag = wx.LEFT | wx.RIGHT, border = 5)
  603. btnSizer.Add(item = self.btnInstall, proportion = 0,
  604. flag = wx.LEFT | wx.RIGHT, border = 5)
  605. sizer.Add(item = repoSizer, proportion = 0,
  606. flag = wx.ALL | wx.EXPAND, border = 3)
  607. sizer.Add(item = findSizer, proportion = 0,
  608. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  609. sizer.Add(item = treeSizer, proportion = 1,
  610. flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3)
  611. sizer.Add(item = btnSizer, proportion=0,
  612. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  613. self.panel.SetSizer(sizer)
  614. sizer.Fit(self.panel)
  615. self.Layout()
  616. def _install(self, name):
  617. if not name:
  618. return
  619. log = self.parent.GetLogWindow()
  620. log.RunCmd(['g.extension', 'extension=' + name,
  621. 'svnurl=' + self.repo.GetValue().strip()])
  622. self.OnCloseWindow(None)
  623. def OnUpdateStatusBar(self, event):
  624. """!Update statusbar text"""
  625. element = self.search.GetSelection()
  626. if not self.tree.IsLoaded():
  627. self.SetStatusText(_("Fetch list of available extensions by clicking on 'Fetch' button"), 0)
  628. return
  629. self.tree.SearchItems(element = element,
  630. value = event.GetString())
  631. nItems = len(self.tree.itemsMarked)
  632. if event.GetString():
  633. self.SetStatusText(_("%d items match") % nItems, 0)
  634. else:
  635. self.SetStatusText("", 0)
  636. event.Skip()
  637. def OnCloseWindow(self, event):
  638. """!Close window"""
  639. self.Destroy()
  640. def OnFetch(self, event):
  641. """!Fetch list of available extensions"""
  642. self.SetStatusText(_("Fetching list of modules from GRASS-Addons SVN (be patient)..."), 0)
  643. self.tree.Load(url = self.repo.GetValue().strip(), full = self.fullDesc.IsChecked())
  644. self.SetStatusText("", 0)
  645. def OnItemActivated(self, event):
  646. item = event.GetItem()
  647. data = self.tree.GetPyData(item)
  648. if data and data.has_key('command'):
  649. self._install(data['command'])
  650. def OnInstall(self, event):
  651. """!Install selected extension"""
  652. item = self.tree.GetSelected()
  653. if not item.IsOk():
  654. return
  655. self._install(self.tree.GetItemText(item))
  656. def OnItemSelected(self, event):
  657. """!Item selected"""
  658. item = event.GetItem()
  659. self.tree.itemSelected = item
  660. data = self.tree.GetPyData(item)
  661. if not data:
  662. self.SetStatusText('', 0)
  663. self.btnInstall.Enable(False)
  664. else:
  665. self.SetStatusText(data.get('description', ''), 0)
  666. self.btnInstall.Enable(True)
  667. def OnShowItem(self, event):
  668. """!Show selected item"""
  669. self.tree.OnShowItem(event)
  670. if self.tree.GetSelected():
  671. self.btnInstall.Enable()
  672. else:
  673. self.btnInstall.Enable(False)
  674. class ExtensionTree(ItemTree):
  675. """!List of available extensions"""
  676. def __init__(self, parent, log, id = wx.ID_ANY,
  677. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS |
  678. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
  679. **kwargs):
  680. self.parent = parent # GMFrame
  681. self.log = log
  682. super(ExtensionTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  683. self._initTree()
  684. def _initTree(self):
  685. for prefix in ('display', 'database',
  686. 'general', 'imagery',
  687. 'misc', 'postscript', 'paint',
  688. 'raster', 'raster3D', 'sites', 'vector'):
  689. self.AppendItem(parentId = self.root,
  690. text = prefix)
  691. self._loaded = False
  692. def _expandPrefix(self, c):
  693. name = { 'd' : 'display',
  694. 'db' : 'database',
  695. 'g' : 'general',
  696. 'i' : 'imagery',
  697. 'm' : 'misc',
  698. 'ps' : 'postscript',
  699. 'p' : 'paint',
  700. 'r' : 'raster',
  701. 'r3' : 'raster3D',
  702. 's' : 'sites',
  703. 'v' : 'vector' }
  704. if name.has_key(c):
  705. return name[c]
  706. return c
  707. def _findItem(self, text):
  708. """!Find item"""
  709. item = self.GetFirstChild(self.root)[0]
  710. while item and item.IsOk():
  711. if text == self.GetItemText(item):
  712. return item
  713. item = self.GetNextSibling(item)
  714. return None
  715. def Load(self, url, full = False):
  716. """!Load list of extensions"""
  717. self.DeleteAllItems()
  718. self.root = self.AddRoot(_("Menu tree"))
  719. self._initTree()
  720. if full:
  721. flags = 'g'
  722. else:
  723. flags = 'l'
  724. ret = gcmd.RunCommand('g.extension', read = True,
  725. svnurl = url,
  726. flags = flags, quiet = True)
  727. if not ret:
  728. return
  729. mdict = dict()
  730. for line in ret.splitlines():
  731. if full:
  732. key, value = line.split('=', 1)
  733. if key == 'name':
  734. prefix, name = value.split('.', 1)
  735. if not mdict.has_key(prefix):
  736. mdict[prefix] = dict()
  737. mdict[prefix][name] = dict()
  738. else:
  739. mdict[prefix][name][key] = value
  740. else:
  741. prefix, name = line.strip().split('.', 1)
  742. if not mdict.has_key(prefix):
  743. mdict[prefix] = dict()
  744. mdict[prefix][name] = { 'command' : prefix + '.' + name }
  745. for prefix in mdict.keys():
  746. prefixName = self._expandPrefix(prefix)
  747. item = self._findItem(prefixName)
  748. names = mdict[prefix].keys()
  749. names.sort()
  750. for name in names:
  751. new = self.AppendItem(parentId = item,
  752. text = prefix + '.' + name)
  753. data = dict()
  754. for key in mdict[prefix][name].keys():
  755. data[key] = mdict[prefix][name][key]
  756. self.SetPyData(new, data)
  757. self._loaded = True
  758. def IsLoaded(self):
  759. """Check if items are loaded"""
  760. return self._loaded