menu.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. """
  2. @package gui_core.menu
  3. @brief Menu classes for wxGUI
  4. Classes:
  5. - menu::Menu
  6. - menu::SearchModuleWindow
  7. (C) 2010-2013 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Martin Landa <landa.martin gmail.com>
  11. @author Pawel Netzel (menu customization)
  12. @author Milena Nowotarska (menu customization)
  13. @author Robert Szczepanek (menu customization)
  14. @author Vaclav Petras <wenzeslaus gmail.com> (menu customization)
  15. """
  16. import re
  17. import wx
  18. from core import globalvar
  19. from core import utils
  20. from core.gcmd import EncodeString
  21. from core.utils import _
  22. from gui_core.widgets import SearchModuleWidget
  23. from gui_core.treeview import CTreeView
  24. from icons.icon import MetaIcon
  25. from grass.pydispatch.signal import Signal
  26. class Menu(wx.MenuBar):
  27. def __init__(self, parent, model):
  28. """Creates menubar"""
  29. wx.MenuBar.__init__(self)
  30. self.parent = parent
  31. self.model = model
  32. self.menucmd = dict()
  33. self.bmpsize = (16, 16)
  34. for child in self.model.root.children:
  35. self.Append(self._createMenu(child), child.label)
  36. def _createMenu(self, node):
  37. """Creates menu"""
  38. menu = wx.Menu()
  39. for child in node.children:
  40. if child.children:
  41. label = child.label
  42. subMenu = self._createMenu(child)
  43. menu.AppendMenu(wx.ID_ANY, label, subMenu)
  44. else:
  45. data = child.data.copy()
  46. data.pop('label')
  47. self._createMenuItem(menu, label=child.label, **data)
  48. self.parent.Bind(wx.EVT_MENU_HIGHLIGHT_ALL, self.OnMenuHighlight)
  49. return menu
  50. def _createMenuItem(
  51. self, menu, label, description, handler, command, keywords,
  52. shortcut='', icon='', wxId=wx.ID_ANY, kind=wx.ITEM_NORMAL):
  53. """Creates menu items
  54. There are three menu styles (menu item text styles).
  55. 1 -- label only, 2 -- label and cmd name, 3 -- cmd name only
  56. """
  57. if not label:
  58. menu.AppendSeparator()
  59. return
  60. if command:
  61. helpString = command + ' -- ' + description
  62. else:
  63. helpString = description
  64. if shortcut:
  65. label += '\t' + shortcut
  66. menuItem = wx.MenuItem(menu, wxId, label, helpString, kind)
  67. if icon:
  68. menuItem.SetBitmap(MetaIcon(img=icon).GetBitmap(self.bmpsize))
  69. menu.AppendItem(menuItem)
  70. self.menucmd[menuItem.GetId()] = command
  71. if command:
  72. try:
  73. cmd = utils.split(str(command))
  74. except UnicodeError:
  75. cmd = utils.split(EncodeString((command)))
  76. # disable only grass commands which are not present (e.g.
  77. # r.in.lidar)
  78. if cmd and cmd[0] not in globalvar.grassCmd and \
  79. re.match('[rvdipmgt][3bs]?\.([a-z0-9\.])+', cmd[0]):
  80. menuItem.Enable(False)
  81. rhandler = eval('self.parent.' + handler)
  82. self.parent.Bind(wx.EVT_MENU, rhandler, menuItem)
  83. def GetData(self):
  84. """Get menu data"""
  85. return self.model
  86. def GetCmd(self):
  87. """Get dictionary of commands (key is id)
  88. :return: dictionary of commands
  89. """
  90. return self.menucmd
  91. def OnMenuHighlight(self, event):
  92. """
  93. Default menu help handler
  94. """
  95. # Show how to get menu item info from this event handler
  96. id = event.GetMenuId()
  97. item = self.FindItemById(id)
  98. if item:
  99. text = item.GetText()
  100. help = item.GetHelp()
  101. # but in this case just call Skip so the default is done
  102. event.Skip()
  103. class SearchModuleWindow(wx.Panel):
  104. """Menu tree and search widget for searching modules.
  105. Signal:
  106. showNotification - attribute 'message'
  107. """
  108. def __init__(self, parent, handlerObj, model, id=wx.ID_ANY, **kwargs):
  109. self.parent = parent
  110. self.handlerObj = handlerObj
  111. self.showNotification = Signal('SearchModuleWindow.showNotification')
  112. wx.Panel.__init__(self, parent=parent, id=id, **kwargs)
  113. # tree
  114. self._tree = CTreeView(model=model, parent=self)
  115. self._tree.SetToolTipString(
  116. _("Double-click or Ctrl-Enter to run selected module"))
  117. # self._dataBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
  118. # label = " %s " % _("Module tree"))
  119. # search widget
  120. self._search = SearchModuleWidget(parent=self,
  121. model=model,
  122. showChoice=False)
  123. self._search.showSearchResult.connect(
  124. lambda result: self._tree.Select(result))
  125. self._search.showNotification.connect(self.showNotification)
  126. self._helpText = wx.StaticText(
  127. parent=self, id=wx.ID_ANY,
  128. label="Press Enter for next match, Ctrl+Enter to run command")
  129. self._helpText.SetForegroundColour(
  130. wx.SystemSettings_GetColour(
  131. wx.SYS_COLOUR_GRAYTEXT))
  132. # buttons
  133. self._btnRun = wx.Button(self, id=wx.ID_OK, label=_("&Run"))
  134. self._btnRun.SetToolTipString(_("Run selected module from the tree"))
  135. # bindings
  136. self._btnRun.Bind(wx.EVT_BUTTON, lambda evt: self.Run())
  137. self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
  138. self._tree.selectionChanged.connect(self.OnItemSelected)
  139. self._tree.itemActivated.connect(lambda node: self.Run(node))
  140. self._layout()
  141. self._search.SetFocus()
  142. def _layout(self):
  143. """Do dialog layout"""
  144. sizer = wx.BoxSizer(wx.VERTICAL)
  145. # body
  146. dataSizer = wx.BoxSizer(wx.HORIZONTAL)
  147. dataSizer.Add(item=self._tree, proportion=1,
  148. flag=wx.EXPAND)
  149. # buttons
  150. btnSizer = wx.BoxSizer(wx.HORIZONTAL)
  151. btnSizer.Add(item=self._btnRun, proportion=0)
  152. sizer.Add(item=dataSizer, proportion=1,
  153. flag=wx.EXPAND | wx.ALL, border=5)
  154. sizer.Add(item=self._search, proportion=0,
  155. flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  156. sizer.Add(item=btnSizer, proportion=0,
  157. flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.RIGHT, border=5)
  158. sizer.Add(item=self._helpText,
  159. proportion=0, flag=wx.EXPAND | wx.LEFT, border=5)
  160. sizer.Fit(self)
  161. sizer.SetSizeHints(self)
  162. self.SetSizer(sizer)
  163. self.Fit()
  164. self.SetAutoLayout(True)
  165. self.Layout()
  166. def Run(self, module=None):
  167. """Run selected command.
  168. :param module: module (represented by tree node)
  169. """
  170. if module is None:
  171. if not self._tree.GetSelected():
  172. return
  173. module = self._tree.GetSelected()[0]
  174. data = module.data
  175. if not data:
  176. return
  177. handler = 'self.handlerObj.' + data['handler'].lstrip('self.')
  178. if data['command']:
  179. eval(handler)(event=None, cmd=data['command'].split())
  180. else:
  181. eval(handler)(event=None)
  182. def OnKeyUp(self, event):
  183. """Key or key combination pressed"""
  184. if event.ControlDown() and \
  185. event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
  186. self.Run()
  187. def OnItemSelected(self, node):
  188. """Item selected"""
  189. data = node.data
  190. if not data or 'command' not in data:
  191. return
  192. if data['command']:
  193. label = data['command']
  194. if data['description']:
  195. label += ' -- ' + data['description']
  196. else:
  197. label = data['description']
  198. self.showNotification.emit(message=label)