help.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. """!
  2. @package help.py
  3. @brief Help window
  4. @todo Needs improvements...
  5. Classes:
  6. - HelpWindow
  7. - MenuTreeWindow
  8. (C) 2008-2009 by the GRASS Development Team
  9. This program is free software under the GNU General Public
  10. License (>=v2). Read the file COPYING that comes with GRASS
  11. for details.
  12. @author Martin Landa <landa.martin gmail.com>
  13. """
  14. import wx
  15. import wx.lib.customtreectrl as CT
  16. import menudata
  17. import gcmd
  18. class HelpWindow(wx.Frame):
  19. """!GRASS Quickstart help window"""
  20. def __init__(self, parent, id, title, size, file):
  21. wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
  22. sizer = wx.BoxSizer(wx.VERTICAL)
  23. # text
  24. helpFrame = wx.html.HtmlWindow(parent=self, id=wx.ID_ANY)
  25. helpFrame.SetStandardFonts (size = 10)
  26. helpFrame.SetBorders(10)
  27. wx.InitAllImageHandlers()
  28. helpFrame.LoadFile(file)
  29. self.Ok = True
  30. sizer.Add(item=helpFrame, proportion=1, flag=wx.EXPAND)
  31. self.SetAutoLayout(True)
  32. self.SetSizer(sizer)
  33. # sizer.Fit(self)
  34. # sizer.SetSizeHints(self)
  35. self.Layout()
  36. class MenuTreeWindow(wx.Frame):
  37. """!Show menu tree"""
  38. def __init__(self, parent, id = wx.ID_ANY, title = _("Menu tree window"), **kwargs):
  39. self.parent = parent # LayerManager
  40. wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
  41. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  42. # tree
  43. self.tree = MenuTree(parent = self.panel, data = menudata.Data())
  44. self.tree.Load()
  45. self.searchDict = { _('label') : 'label', # i18n workaround
  46. _('help') : 'help',
  47. _('command') : 'command',
  48. _('keywords') : 'keywords' }
  49. # search
  50. self.searchBy = wx.Choice(parent = self.panel, id = wx.ID_ANY,
  51. choices = [_('label'),
  52. _('help'),
  53. _('command'),
  54. _('keywords')])
  55. self.search = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
  56. value = "", size = (-1, 25),
  57. style = wx.TE_PROCESS_ENTER)
  58. # statusbar
  59. self.statusbar = self.CreateStatusBar(number=1)
  60. # close on run
  61. self.closeOnRun = wx.CheckBox(parent = self.panel, id = wx.ID_ANY,
  62. label = _("Close dialog on run"))
  63. self.closeOnRun.SetValue(True)
  64. # buttons
  65. self.btnRun = wx.Button(self.panel, id = wx.ID_OK, label = _("Run"))
  66. self.btnRun.SetToolTipString(_("Run selected command"))
  67. self.btnRun.Enable(False)
  68. self.btnClose = wx.Button(self.panel, id = wx.ID_CLOSE)
  69. self.btnClose.SetToolTipString(_("Close dialog"))
  70. # bindings
  71. self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseWindow)
  72. self.btnRun.Bind(wx.EVT_BUTTON, self.OnRun)
  73. self.search.Bind(wx.EVT_TEXT_ENTER, self.OnShowItem)
  74. self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  75. self.tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnItemActivated)
  76. self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnItemSelected)
  77. self.__Layout()
  78. self.search.SetFocus()
  79. def __Layout(self):
  80. """!Do dialog layout"""
  81. sizer = wx.BoxSizer(wx.VERTICAL)
  82. # body
  83. dataBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY,
  84. label=" %s " % _("Menu tree (double-click to run command)"))
  85. dataSizer = wx.StaticBoxSizer(dataBox, wx.HORIZONTAL)
  86. dataSizer.Add(item = self.tree, proportion =1,
  87. flag = wx.EXPAND)
  88. # search
  89. searchSizer = wx.BoxSizer(wx.HORIZONTAL)
  90. searchSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY,
  91. label = _("Search:")),
  92. proportion = 0,
  93. flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.ALL,
  94. border = 3)
  95. searchSizer.Add(item = self.searchBy, proportion = 0,
  96. flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT,
  97. border = 5)
  98. searchSizer.Add(item = self.search, proportion = 1,
  99. flag = wx.EXPAND | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL,
  100. border = 5)
  101. # buttons
  102. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  103. btnSizer.Add(item = self.btnRun, proportion = 0,
  104. flag = wx.LEFT | wx.RIGHT, border = 5)
  105. btnSizer.Add(item = self.btnClose, proportion = 0,
  106. flag = wx.LEFT | wx.RIGHT, border = 5)
  107. sizer.Add(item = dataSizer, proportion = 1,
  108. flag = wx.EXPAND | wx.ALL, border = 5)
  109. sizer.Add(item = searchSizer, proportion=0,
  110. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  111. sizer.Add(item = btnSizer, proportion=0,
  112. flag = wx.ALIGN_RIGHT | wx.ALL, border = 5)
  113. sizer.Add(item = self.closeOnRun, proportion=0,
  114. flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
  115. self.panel.SetAutoLayout(True)
  116. self.panel.SetSizer(sizer)
  117. sizer.Fit(self.panel)
  118. self.Layout()
  119. self.SetSize((530, 370))
  120. def OnCloseWindow(self, event):
  121. """!Close window"""
  122. self.Destroy()
  123. def OnRun(self, event):
  124. """!Run selected command"""
  125. if not self.tree.GetSelected():
  126. return # should not happen
  127. data = self.tree.GetPyData(self.tree.GetSelected())
  128. if not data:
  129. return
  130. handler = 'self.parent.' + data['handler'].lstrip('self.')
  131. if data['command']:
  132. eval(handler)(event = None, cmd = data['command'].split())
  133. else:
  134. eval(handler)(None)
  135. if self.closeOnRun.IsChecked():
  136. self.OnCloseWindow(None)
  137. def OnItemActivated(self, event):
  138. """!Item activated (double-click)"""
  139. item = event.GetItem()
  140. if not item or not item.IsOk():
  141. return
  142. data = self.tree.GetPyData(item)
  143. if not data or not data.has_key('command'):
  144. return
  145. self.tree.itemSelected = item
  146. self.OnRun(None)
  147. def OnItemSelected(self, event):
  148. """!Item selected"""
  149. item = event.GetItem()
  150. if not item or not item.IsOk():
  151. return
  152. data = self.tree.GetPyData(item)
  153. if not data or not data.has_key('command'):
  154. return
  155. if data['command']:
  156. label = data['command'] + ' -- ' + data['help']
  157. else:
  158. label = data['help']
  159. self.statusbar.SetStatusText(label, 0)
  160. def OnShowItem(self, event):
  161. """!Highlight first found item in menu tree"""
  162. if len(self.tree.itemsMarked) > 0:
  163. if self.tree.GetSelected():
  164. self.tree.ToggleItemSelection(self.tree.GetSelected())
  165. idx = self.tree.itemsMarked.index(self.tree.GetSelected()) + 1
  166. else:
  167. idx = 0
  168. try:
  169. self.tree.ToggleItemSelection(self.tree.itemsMarked[idx])
  170. self.tree.itemSelected = self.tree.itemsMarked[idx]
  171. self.tree.EnsureVisible(self.tree.itemsMarked[idx])
  172. except IndexError:
  173. self.tree.ToggleItemSelection(self.tree.itemsMarked[0]) # reselect first item
  174. self.tree.EnsureVisible(self.tree.itemsMarked[0])
  175. self.tree.itemSelected = self.tree.itemsMarked[0]
  176. else:
  177. for item in self.tree.root.GetChildren():
  178. self.tree.Collapse(item)
  179. itemSelected = self.tree.GetSelection()
  180. if itemSelected:
  181. self.tree.ToggleItemSelection(itemSelected)
  182. self.tree.itemSelected = None
  183. def OnUpdateStatusBar(self, event):
  184. """!Update statusbar text"""
  185. element = self.searchDict[self.searchBy.GetStringSelection()]
  186. self.tree.itemsMarked = self.SearchItems(element = element,
  187. value = event.GetString())
  188. self.tree.itemSelected = None
  189. nItems = len(self.tree.itemsMarked)
  190. if event.GetString():
  191. self.statusbar.SetStatusText(_("%d items match") % nItems, 0)
  192. else:
  193. self.statusbar.SetStatusText("", 0)
  194. if nItems > 0:
  195. self.tree.itemSelected = self.tree.itemsMarked[0]
  196. self.btnRun.Enable()
  197. else:
  198. self.btnRun.Enable(False)
  199. event.Skip()
  200. def SearchItems(self, element, value):
  201. """!Search item
  202. @param element element index (see self.searchBy)
  203. @param value
  204. @return list of found tree items
  205. """
  206. items = list()
  207. if not value:
  208. return items
  209. item = self.tree.GetFirstChild(self.tree.root)[0]
  210. self.__ProcessItem(item, element, value, items)
  211. return items
  212. def __ProcessItem(self, item, element, value, listOfItems):
  213. """!Search items (used by SearchItems)
  214. @param item reference item
  215. @param listOfItems list of found items
  216. """
  217. while item and item.IsOk():
  218. subItem = self.tree.GetFirstChild(item)[0]
  219. if subItem:
  220. self.__ProcessItem(subItem, element, value, listOfItems)
  221. data = self.tree.GetPyData(item)
  222. if data and data.has_key(element) and \
  223. value.lower() in data[element].lower():
  224. listOfItems.append(item)
  225. item = self.tree.GetNextSibling(item)
  226. class MenuTree(CT.CustomTreeCtrl):
  227. """!Menu tree class"""
  228. def __init__(self, parent, data, id = wx.ID_ANY,
  229. ctstyle = CT.TR_HIDE_ROOT | CT.TR_FULL_ROW_HIGHLIGHT | CT.TR_HAS_BUTTONS | \
  230. CT.TR_LINES_AT_ROOT | CT.TR_SINGLE,
  231. **kwargs):
  232. self.parent = parent
  233. self.menudata = data
  234. super(MenuTree, self).__init__(parent, id, ctstyle = ctstyle, **kwargs)
  235. self.root = self.AddRoot(_("Menu tree"))
  236. self.itemsMarked = [] # list of marked items
  237. self.itemSelected = None
  238. def Load(self, data = None):
  239. """!Load menu data tree
  240. @param data menu data (None to use self.menudata)
  241. """
  242. if not data:
  243. data = self.menudata
  244. self.itemsMarked = [] # list of marked items
  245. for eachMenuData in data.GetMenu():
  246. for label, items in eachMenuData:
  247. item = self.AppendItem(parentId = self.root,
  248. text = label)
  249. self.__AppendItems(item, items)
  250. def __AppendItems(self, item, data):
  251. """!Append items into tree (used by Load()
  252. @param item tree item (parent)
  253. @parent data menu data"""
  254. for eachItem in data:
  255. if len(eachItem) == 2:
  256. if eachItem[0]:
  257. itemSub = self.AppendItem(parentId = item,
  258. text = eachItem[0])
  259. self.__AppendItems(itemSub, eachItem[1])
  260. else:
  261. if eachItem[0]:
  262. itemNew = self.AppendItem(parentId = item,
  263. text = eachItem[0])
  264. data = { 'label' : eachItem[0],
  265. 'help' : eachItem[1],
  266. 'handler' : eachItem[2],
  267. 'command' : eachItem[3],
  268. 'keywords' : eachItem[4] }
  269. self.SetPyData(itemNew, data)
  270. def GetSelected(self):
  271. """!Get selected item"""
  272. return self.itemSelected