help.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. """!
  2. @package help.py
  3. @brief Help window
  4. @todo Needs improvements...
  5. Classes:
  6. - HelpWindow
  7. - MenuTreeWindow
  8. - AboutWindow
  9. (C) 2008-2009 by the GRASS Development Team
  10. This program is free software under the GNU General Public
  11. License (>=v2). Read the file COPYING that comes with GRASS
  12. for details.
  13. @author Martin Landa <landa.martin gmail.com>
  14. """
  15. import os
  16. import wx
  17. import wx.lib.customtreectrl as CT
  18. import wx.lib.flatnotebook as FN
  19. import wx.lib.scrolledpanel as scrolled
  20. from wx.lib.wordwrap import wordwrap
  21. import menudata
  22. import gcmd
  23. import globalvar
  24. class HelpWindow(wx.Frame):
  25. """!GRASS Quickstart help window"""
  26. def __init__(self, parent, id, title, size, file):
  27. wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
  28. sizer = wx.BoxSizer(wx.VERTICAL)
  29. # text
  30. helpFrame = wx.html.HtmlWindow(parent=self, id=wx.ID_ANY)
  31. helpFrame.SetStandardFonts (size = 10)
  32. helpFrame.SetBorders(10)
  33. wx.InitAllImageHandlers()
  34. helpFrame.LoadFile(file)
  35. self.Ok = True
  36. sizer.Add(item=helpFrame, proportion=1, flag=wx.EXPAND)
  37. self.SetAutoLayout(True)
  38. self.SetSizer(sizer)
  39. # sizer.Fit(self)
  40. # sizer.SetSizeHints(self)
  41. self.Layout()
  42. class MenuTreeWindow(wx.Frame):
  43. """!Show menu tree"""
  44. def __init__(self, parent, id = wx.ID_ANY, title = _("Menu tree window"), **kwargs):
  45. self.parent = parent # LayerManager
  46. wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
  47. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  48. # tree
  49. self.tree = MenuTree(parent = self.panel, data = menudata.Data())
  50. self.tree.Load()
  51. self.searchDict = { _('label') : 'label', # i18n workaround
  52. _('help') : 'help',
  53. _('command') : 'command',
  54. _('keywords') : 'keywords' }
  55. # search
  56. self.searchBy = wx.Choice(parent = self.panel, id = wx.ID_ANY,
  57. choices = [_('label'),
  58. _('help'),
  59. _('command'),
  60. _('keywords')])
  61. self.search = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
  62. value = "", size = (-1, 25),
  63. style = wx.TE_PROCESS_ENTER)
  64. # statusbar
  65. self.statusbar = self.CreateStatusBar(number=1)
  66. # close on run
  67. self.closeOnRun = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
  68. label = _("Close dialog on run"))
  69. self.closeOnRun.SetValue(True)
  70. # buttons
  71. self.btnRun = wx.Button(self.panel, id = wx.ID_OK, label = _("Run"))
  72. self.btnRun.SetToolTipString(_("Run selected command"))
  73. self.btnRun.Enable(False)
  74. self.btnClose = wx.Button(self.panel, id = wx.ID_CLOSE)
  75. self.btnClose.SetToolTipString(_("Close dialog"))
  76. # bindings
  77. self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  78. self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun)
  79. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  80. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  81. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  82. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  83. self.__Layout()
  84. self.search.SetFocus()
  85. def __Layout(self):
  86. """!Do dialog layout"""
  87. sizer = wx.BoxSizer(wx.VERTICAL)
  88. # body
  89. dataBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  90. label=" %s " % _("Menu tree (double-click to run command)"))
  91. dataSizer = wx.StaticBoxSizer(dataBox, wx.HORIZONTAL)
  92. dataSizer.Add(item = self.tree, proportion =1,
  93. flag = wx.EXPAND)
  94. # search
  95. searchSizer = wx.BoxSizer(wx.HORIZONTAL)
  96. searchSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
  97. label = _("Search:")),
  98. proportion = 0,
  99. flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
  100. border = 3)
  101. searchSizer.Add(item = self.searchBy, proportion = 0,
  102. flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT,
  103. border = 5)
  104. searchSizer.Add(item = self.search, proportion = 1,
  105. flag = wx.EXPAND | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL,
  106. border = 5)
  107. # buttons
  108. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  109. btnSizer.Add(item = self.btnRun, proportion = 0,
  110. flag = wx.LEFT | wx.RIGHT, border = 5)
  111. btnSizer.Add(item = self.btnClose, proportion = 0,
  112. flag = wx.LEFT | wx.RIGHT, border = 5)
  113. sizer.Add(item = dataSizer, proportion = 1,
  114. flag = wx.EXPAND | wx.ALL, border = 5)
  115. sizer.Add(item = searchSizer, proportion=0,
  116. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  117. sizer.Add(item = btnSizer, proportion=0,
  118. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  119. sizer.Add(item = self.closeOnRun, proportion=0,
  120. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  121. self.panel.SetAutoLayout(True)
  122. self.panel.SetSizer(sizer)
  123. sizer.Fit(self.panel)
  124. self.Layout()
  125. self.SetSize((530, 370))
  126. def OnCloseWindow(self, event):
  127. """!Close window"""
  128. self.Destroy()
  129. def OnRun(self, event):
  130. """!Run selected command"""
  131. if not self.tree.GetSelected():
  132. return # should not happen
  133. data = self.tree.GetPyData(self.tree.GetSelected())
  134. if not data:
  135. return
  136. handler = 'self.parent.' + data['handler'].lstrip('self.')
  137. if data['command']:
  138. eval(handler)(event = None, cmd = data['command'].split())
  139. else:
  140. eval(handler)(None)
  141. if self.closeOnRun.IsChecked():
  142. self.OnCloseWindow(None)
  143. def OnItemActivated(self, event):
  144. """!Item activated (double-click)"""
  145. item = event.GetItem()
  146. if not item or not item.IsOk():
  147. return
  148. data = self.tree.GetPyData(item)
  149. if not data or not data.has_key('command'):
  150. return
  151. self.tree.itemSelected = item
  152. self.OnRun(None)
  153. def OnItemSelected(self, event):
  154. """!Item selected"""
  155. item = event.GetItem()
  156. if not item or not item.IsOk():
  157. return
  158. data = self.tree.GetPyData(item)
  159. if not data or not data.has_key('command'):
  160. return
  161. if data['command']:
  162. label = data['command'] + ' -- ' + data['help']
  163. else:
  164. label = data['help']
  165. self.statusbar.SetStatusText(label, 0)
  166. def OnShowItem(self, event):
  167. """!Highlight first found item in menu tree"""
  168. if len(self.tree.itemsMarked) > 0:
  169. if self.tree.GetSelected():
  170. self.tree.ToggleItemSelection(self.tree.GetSelected())
  171. idx = self.tree.itemsMarked.index(self.tree.GetSelected()) + 1
  172. else:
  173. idx = 0
  174. try:
  175. self.tree.ToggleItemSelection(self.tree.itemsMarked[idx])
  176. self.tree.itemSelected = self.tree.itemsMarked[idx]
  177. self.tree.EnsureVisible(self.tree.itemsMarked[idx])
  178. except IndexError:
  179. self.tree.ToggleItemSelection(self.tree.itemsMarked[0]) # reselect first item
  180. self.tree.EnsureVisible(self.tree.itemsMarked[0])
  181. self.tree.itemSelected = self.tree.itemsMarked[0]
  182. else:
  183. for item in self.tree.root.GetChildren():
  184. self.tree.Collapse(item)
  185. itemSelected = self.tree.GetSelection()
  186. if itemSelected:
  187. self.tree.ToggleItemSelection(itemSelected)
  188. self.tree.itemSelected = None
  189. if self.tree.itemSelected:
  190. self.btnRun.Enable()
  191. else:
  192. self.btnRun.Enable(False)
  193. def OnUpdateStatusBar(self, event):
  194. """!Update statusbar text"""
  195. element = self.searchDict[self.searchBy.GetStringSelection()]
  196. self.tree.itemsMarked = self.SearchItems(element = element,
  197. value = event.GetString())
  198. self.tree.itemSelected = None
  199. nItems = len(self.tree.itemsMarked)
  200. if event.GetString():
  201. self.statusbar.SetStatusText(_("%d items match") % nItems, 0)
  202. else:
  203. self.statusbar.SetStatusText("", 0)
  204. event.Skip()
  205. def SearchItems(self, element, value):
  206. """!Search item
  207. @param element element index (see self.searchBy)
  208. @param value
  209. @return list of found tree items
  210. """
  211. items = list()
  212. if not value:
  213. return items
  214. item = self.tree.GetFirstChild(self.tree.root)[0]
  215. self.__ProcessItem(item, element, value, items)
  216. return items
  217. def __ProcessItem(self, item, element, value, listOfItems):
  218. """!Search items (used by SearchItems)
  219. @param item reference item
  220. @param listOfItems list of found items
  221. """
  222. while item and item.IsOk():
  223. subItem = self.tree.GetFirstChild(item)[0]
  224. if subItem:
  225. self.__ProcessItem(subItem, element, value, listOfItems)
  226. data = self.tree.GetPyData(item)
  227. if data and data.has_key(element) and \
  228. value.lower() in data[element].lower():
  229. listOfItems.append(item)
  230. item = self.tree.GetNextSibling(item)
  231. class MenuTree(CT.CustomTreeCtrl):
  232. """!Menu tree class"""
  233. def __init__(self, parent, data, id = wx.ID_ANY,
  234. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS | \
  235. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
  236. **kwargs):
  237. self.parent = parent
  238. self.menudata = data
  239. super(MenuTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  240. self.root = self.AddRoot(_("Menu tree"))
  241. self.itemsMarked = [] # list of marked items
  242. self.itemSelected = None
  243. def Load(self, data = None):
  244. """!Load menu data tree
  245. @param data menu data (None to use self.menudata)
  246. """
  247. if not data:
  248. data = self.menudata
  249. self.itemsMarked = [] # list of marked items
  250. for eachMenuData in data.GetMenu():
  251. for label, items in eachMenuData:
  252. item = self.AppendItem(parentId = self.root,
  253. text = label)
  254. self.__AppendItems(item, items)
  255. def __AppendItems(self, item, data):
  256. """!Append items into tree (used by Load()
  257. @param item tree item (parent)
  258. @parent data menu data"""
  259. for eachItem in data:
  260. if len(eachItem) == 2:
  261. if eachItem[0]:
  262. itemSub = self.AppendItem(parentId = item,
  263. text = eachItem[0])
  264. self.__AppendItems(itemSub, eachItem[1])
  265. else:
  266. if eachItem[0]:
  267. itemNew = self.AppendItem(parentId = item,
  268. text = eachItem[0])
  269. data = { 'label' : eachItem[0],
  270. 'help' : eachItem[1],
  271. 'handler' : eachItem[2],
  272. 'command' : eachItem[3],
  273. 'keywords' : eachItem[4] }
  274. self.SetPyData(itemNew, data)
  275. def GetSelected(self):
  276. """!Get selected item"""
  277. return self.itemSelected
  278. class AboutWindow(wx.Frame):
  279. def __init__(self, parent):
  280. """!Create custom About Window
  281. @todo improve styling
  282. """
  283. wx.Frame.__init__(self, parent=parent, id=wx.ID_ANY, size=(550,400),
  284. title=_('About GRASS GIS'))
  285. # icon
  286. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  287. # get version and web site
  288. version, svn_gis_h_rev, svn_gis_h_date = gcmd.RunCommand('g.version',
  289. flags = 'r',
  290. read = True).splitlines()
  291. infoTxt = wx.Panel(parent = self, id = wx.ID_ANY)
  292. infoSizer = wx.BoxSizer(wx.VERTICAL)
  293. logo = os.path.join(globalvar.ETCDIR, "gui", "icons", "grass.ico")
  294. logoBitmap = wx.StaticBitmap(parent = infoTxt, id = wx.ID_ANY,
  295. bitmap = wx.Bitmap(name = logo,
  296. type = wx.BITMAP_TYPE_ICO))
  297. infoSizer.Add(item = logoBitmap, proportion = 0,
  298. flag = wx.ALL | wx.ALIGN_CENTER, border = 10)
  299. i = 0
  300. for label in [version.replace('GRASS', 'GRASS GIS').strip() + '\n\n',
  301. _('Official GRASS site: http://grass.osgeo.org') + '\n\n',
  302. _('GIS Library') + ' ' + svn_gis_h_rev + '(' + svn_gis_h_date.split(' ')[1] + ')']:
  303. info = wx.StaticText(parent = infoTxt,
  304. id = wx.ID_ANY,
  305. label = label)
  306. if i == 0:
  307. info.SetFont(wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, ""))
  308. infoSizer.Add(item = info, proportion = 0,
  309. flag = wx.TOP | wx.ALIGN_CENTER, border = 5)
  310. i += 1
  311. #
  312. # create pages
  313. #
  314. copyrightwin = self.PageCopyright()
  315. licensewin = self.PageLicense()
  316. authorwin = self.PageCredit()
  317. contribwin = self.PageContributors()
  318. transwin = self.PageTranslators()
  319. # create a flat notebook for displaying information about GRASS
  320. nbstyle = FN.FNB_VC8 | \
  321. FN.FNB_BACKGROUND_GRADIENT | \
  322. FN.FNB_TABS_BORDER_SIMPLE | \
  323. FN.FNB_NO_X_BUTTON | \
  324. FN.FNB_NO_NAV_BUTTONS
  325. aboutNotebook = FN.FlatNotebook(self, id=wx.ID_ANY, style=nbstyle)
  326. aboutNotebook.SetTabAreaColour(globalvar.FNPageColor)
  327. # make pages for About GRASS notebook
  328. pg1 = aboutNotebook.AddPage(infoTxt, text=_("Info"))
  329. pg2 = aboutNotebook.AddPage(copyrightwin, text=_("Copyright"))
  330. pg3 = aboutNotebook.AddPage(licensewin, text=_("License"))
  331. pg4 = aboutNotebook.AddPage(authorwin, text=_("Authors"))
  332. pg5 = aboutNotebook.AddPage(contribwin, text=_("Contributors"))
  333. pg5 = aboutNotebook.AddPage(transwin, text=_("Translators"))
  334. # buttons
  335. btnClose = wx.Button(parent = self, id = wx.ID_CLOSE)
  336. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  337. btnSizer.Add(item = btnClose, proportion = 1,
  338. flag = wx.ALL | wx.EXPAND | wx.ALIGN_RIGHT,
  339. border = 5)
  340. # bindings
  341. # self.aboutNotebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnAGPageChanged)
  342. btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  343. infoTxt.SetSizer(infoSizer)
  344. infoSizer.Fit(infoTxt)
  345. sizer = wx.BoxSizer(wx.VERTICAL)
  346. sizer.Add(item=aboutNotebook, proportion=1,
  347. flag=wx.EXPAND | wx.ALL, border=1)
  348. sizer.Add(item=btnSizer, proportion=0,
  349. flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=1)
  350. self.SetSizer(sizer)
  351. self.Layout()
  352. def PageCopyright(self):
  353. """Copyright information"""
  354. copyfile = os.path.join(os.getenv("GISBASE"), "COPYING")
  355. if os.path.exists(copyfile):
  356. copyrightFile = open(copyfile, 'r')
  357. copyrightOut = []
  358. copyright = copyrightFile.readlines()
  359. copytext = wordwrap(''.join(copyright[:11] + copyright[26:-3]),
  360. 575, wx.ClientDC(self))
  361. copyrightFile.close()
  362. else:
  363. copytext = _('%s file missing') % 'COPYING'
  364. # put text into a scrolling panel
  365. copyrightwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  366. size=wx.DefaultSize,
  367. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  368. copyrighttxt = wx.StaticText(copyrightwin, id=wx.ID_ANY, label=copytext)
  369. copyrightwin.SetAutoLayout(True)
  370. copyrightwin.sizer = wx.BoxSizer(wx.VERTICAL)
  371. copyrightwin.sizer.Add(item=copyrighttxt, proportion=1,
  372. flag=wx.EXPAND | wx.ALL, border=3)
  373. copyrightwin.SetSizer(copyrightwin.sizer)
  374. copyrightwin.Layout()
  375. copyrightwin.SetupScrolling()
  376. return copyrightwin
  377. def PageLicense(self):
  378. """Licence about"""
  379. licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT")
  380. if os.path.exists(licfile):
  381. licenceFile = open(licfile, 'r')
  382. license = ''.join(licenceFile.readlines())
  383. licenceFile.close()
  384. else:
  385. license = _('%s file missing') % 'GPL.TXT'
  386. # put text into a scrolling panel
  387. licensewin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  388. style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER)
  389. licensetxt = wx.StaticText(licensewin, id=wx.ID_ANY, label=license)
  390. licensewin.SetAutoLayout(True)
  391. licensewin.sizer = wx.BoxSizer(wx.VERTICAL)
  392. licensewin.sizer.Add(item=licensetxt, proportion=1,
  393. flag=wx.EXPAND | wx.ALL, border=3)
  394. licensewin.SetSizer(licensewin.sizer)
  395. licensewin.Layout()
  396. licensewin.SetupScrolling()
  397. return licensewin
  398. def PageCredit(self):
  399. """Credit about"""
  400. # credits
  401. authfile = os.path.join(os.getenv("GISBASE"), "AUTHORS")
  402. if os.path.exists(authfile):
  403. authorsFile = open(authfile, 'r')
  404. authors = unicode(''.join(authorsFile.readlines()), "utf-8")
  405. authorsFile.close()
  406. else:
  407. authors = _('%s file missing') % 'AUTHORS'
  408. authorwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  409. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  410. authortxt = wx.StaticText(authorwin, id=wx.ID_ANY, label=str(authors))
  411. authorwin.SetAutoLayout(1)
  412. authorwin.SetupScrolling()
  413. authorwin.sizer = wx.BoxSizer(wx.VERTICAL)
  414. authorwin.sizer.Add(item=authortxt, proportion=1,
  415. flag=wx.EXPAND | wx.ALL, border=3)
  416. authorwin.SetSizer(authorwin.sizer)
  417. authorwin.Layout()
  418. return authorwin
  419. def PageContributors(self):
  420. """Contributors info"""
  421. contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv")
  422. if os.path.exists(contribfile):
  423. contribFile = open(contribfile, 'r')
  424. contribs = list()
  425. for line in contribFile.readlines():
  426. cvs_id, name, email, country, osgeo_id, rfc2_agreed = line.split(',')
  427. contribs.append((name, email, country, osgeo_id))
  428. contribs[0] = (_('Name'), _('E-mail'), _('Country'), _('OSGeo_ID'))
  429. contribFile.close()
  430. else:
  431. contribs = None
  432. contribwin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  433. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  434. contribwin.SetAutoLayout(1)
  435. contribwin.SetupScrolling()
  436. contribwin.sizer = wx.BoxSizer(wx.VERTICAL)
  437. if not contribs:
  438. contribtxt = wx.StaticText(contribwin, id=wx.ID_ANY,
  439. label=_('%s file missing') % 'contibutors.csv')
  440. contribwin.sizer.Add(item=contribtxt, proportion=1,
  441. flag=wx.EXPAND | wx.ALL, border=3)
  442. else:
  443. contribBox = wx.FlexGridSizer(cols=4, vgap=5, hgap=5)
  444. for developer in contribs:
  445. for item in developer:
  446. contribBox.Add(item = wx.StaticText(parent = contribwin, id = wx.ID_ANY,
  447. label = item))
  448. contribwin.sizer.Add(item=contribBox, proportion=1,
  449. flag=wx.EXPAND | wx.ALL, border=3)
  450. contribwin.SetSizer(contribwin.sizer)
  451. contribwin.Layout()
  452. return contribwin
  453. def PageTranslators(self):
  454. """Translators info"""
  455. translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv")
  456. if os.path.exists(translatorsfile):
  457. translatorsFile = open(translatorsfile, 'r')
  458. translators = dict()
  459. for line in translatorsFile.readlines()[1:]:
  460. name, email, languages = line.rstrip('\n').split(',')
  461. for language in languages.split(' '):
  462. if not translators.has_key(language):
  463. translators[language] = list()
  464. translators[language].append((name, email))
  465. translatorsFile.close()
  466. else:
  467. translators = None
  468. translatorswin = scrolled.ScrolledPanel(self, id=wx.ID_ANY,
  469. style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
  470. translatorswin.SetAutoLayout(1)
  471. translatorswin.SetupScrolling()
  472. translatorswin.sizer = wx.BoxSizer(wx.VERTICAL)
  473. if not translators:
  474. translatorstxt = wx.StaticText(translatorswin, id=wx.ID_ANY,
  475. label=_('%s file missing') % 'translators.csv')
  476. translatorswin.sizer.Add(item=translatorstxt, proportion=1,
  477. flag=wx.EXPAND | wx.ALL, border=3)
  478. else:
  479. translatorsBox = wx.FlexGridSizer(cols=3, vgap=5, hgap=5)
  480. languages = translators.keys()
  481. languages.sort()
  482. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  483. label = _('Name')))
  484. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  485. label = _('E-mail')))
  486. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  487. label = _('Language')))
  488. for lang in languages:
  489. for translator in translators[lang]:
  490. name, email = translator
  491. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  492. label = name))
  493. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  494. label = email))
  495. translatorsBox.Add(item = wx.StaticText(parent = translatorswin, id = wx.ID_ANY,
  496. label = lang))
  497. translatorswin.sizer.Add(item=translatorsBox, proportion=1,
  498. flag=wx.EXPAND | wx.ALL, border=3)
  499. translatorswin.SetSizer(translatorswin.sizer)
  500. translatorswin.Layout()
  501. return translatorswin
  502. def OnCloseWindow(self, event):
  503. """!Close window"""
  504. self.Close()